sync
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux",
704                 "Roo.bootstrap",
705                 "Roo.bootstrap.dash");
706 /*
707  * Based on:
708  * Ext JS Library 1.1.1
709  * Copyright(c) 2006-2007, Ext JS, LLC.
710  *
711  * Originally Released Under LGPL - original licence link has changed is not relivant.
712  *
713  * Fork - LGPL
714  * <script type="text/javascript">
715  */
716
717 (function() {    
718     // wrappedn so fnCleanup is not in global scope...
719     if(Roo.isIE) {
720         function fnCleanUp() {
721             var p = Function.prototype;
722             delete p.createSequence;
723             delete p.defer;
724             delete p.createDelegate;
725             delete p.createCallback;
726             delete p.createInterceptor;
727
728             window.detachEvent("onunload", fnCleanUp);
729         }
730         window.attachEvent("onunload", fnCleanUp);
731     }
732 })();
733
734
735 /**
736  * @class Function
737  * These functions are available on every Function object (any JavaScript function).
738  */
739 Roo.apply(Function.prototype, {
740      /**
741      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
742      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
743      * Will create a function that is bound to those 2 args.
744      * @return {Function} The new function
745     */
746     createCallback : function(/*args...*/){
747         // make args available, in function below
748         var args = arguments;
749         var method = this;
750         return function() {
751             return method.apply(window, args);
752         };
753     },
754
755     /**
756      * Creates a delegate (callback) that sets the scope to obj.
757      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
758      * Will create a function that is automatically scoped to this.
759      * @param {Object} obj (optional) The object for which the scope is set
760      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
761      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
762      *                                             if a number the args are inserted at the specified position
763      * @return {Function} The new function
764      */
765     createDelegate : function(obj, args, appendArgs){
766         var method = this;
767         return function() {
768             var callArgs = args || arguments;
769             if(appendArgs === true){
770                 callArgs = Array.prototype.slice.call(arguments, 0);
771                 callArgs = callArgs.concat(args);
772             }else if(typeof appendArgs == "number"){
773                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
774                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
775                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
776             }
777             return method.apply(obj || window, callArgs);
778         };
779     },
780
781     /**
782      * Calls this function after the number of millseconds specified.
783      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
784      * @param {Object} obj (optional) The object for which the scope is set
785      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
786      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
787      *                                             if a number the args are inserted at the specified position
788      * @return {Number} The timeout id that can be used with clearTimeout
789      */
790     defer : function(millis, obj, args, appendArgs){
791         var fn = this.createDelegate(obj, args, appendArgs);
792         if(millis){
793             return setTimeout(fn, millis);
794         }
795         fn();
796         return 0;
797     },
798     /**
799      * Create a combined function call sequence of the original function + the passed function.
800      * The resulting function returns the results of the original function.
801      * The passed fcn is called with the parameters of the original function
802      * @param {Function} fcn The function to sequence
803      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
804      * @return {Function} The new function
805      */
806     createSequence : function(fcn, scope){
807         if(typeof fcn != "function"){
808             return this;
809         }
810         var method = this;
811         return function() {
812             var retval = method.apply(this || window, arguments);
813             fcn.apply(scope || this || window, arguments);
814             return retval;
815         };
816     },
817
818     /**
819      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
820      * The resulting function returns the results of the original function.
821      * The passed fcn is called with the parameters of the original function.
822      * @addon
823      * @param {Function} fcn The function to call before the original
824      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
825      * @return {Function} The new function
826      */
827     createInterceptor : function(fcn, scope){
828         if(typeof fcn != "function"){
829             return this;
830         }
831         var method = this;
832         return function() {
833             fcn.target = this;
834             fcn.method = method;
835             if(fcn.apply(scope || this || window, arguments) === false){
836                 return;
837             }
838             return method.apply(this || window, arguments);
839         };
840     }
841 });
842 /*
843  * Based on:
844  * Ext JS Library 1.1.1
845  * Copyright(c) 2006-2007, Ext JS, LLC.
846  *
847  * Originally Released Under LGPL - original licence link has changed is not relivant.
848  *
849  * Fork - LGPL
850  * <script type="text/javascript">
851  */
852
853 Roo.applyIf(String, {
854     
855     /** @scope String */
856     
857     /**
858      * Escapes the passed string for ' and \
859      * @param {String} string The string to escape
860      * @return {String} The escaped string
861      * @static
862      */
863     escape : function(string) {
864         return string.replace(/('|\\)/g, "\\$1");
865     },
866
867     /**
868      * Pads the left side of a string with a specified character.  This is especially useful
869      * for normalizing number and date strings.  Example usage:
870      * <pre><code>
871 var s = String.leftPad('123', 5, '0');
872 // s now contains the string: '00123'
873 </code></pre>
874      * @param {String} string The original string
875      * @param {Number} size The total length of the output string
876      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
877      * @return {String} The padded string
878      * @static
879      */
880     leftPad : function (val, size, ch) {
881         var result = new String(val);
882         if(ch === null || ch === undefined || ch === '') {
883             ch = " ";
884         }
885         while (result.length < size) {
886             result = ch + result;
887         }
888         return result;
889     },
890
891     /**
892      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
893      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
894      * <pre><code>
895 var cls = 'my-class', text = 'Some text';
896 var s = String.format('<div class="{0}">{1}</div>', cls, text);
897 // s now contains the string: '<div class="my-class">Some text</div>'
898 </code></pre>
899      * @param {String} string The tokenized string to be formatted
900      * @param {String} value1 The value to replace token {0}
901      * @param {String} value2 Etc...
902      * @return {String} The formatted string
903      * @static
904      */
905     format : function(format){
906         var args = Array.prototype.slice.call(arguments, 1);
907         return format.replace(/\{(\d+)\}/g, function(m, i){
908             return Roo.util.Format.htmlEncode(args[i]);
909         });
910     }
911   
912     
913 });
914
915 /**
916  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
917  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
918  * they are already different, the first value passed in is returned.  Note that this method returns the new value
919  * but does not change the current string.
920  * <pre><code>
921 // alternate sort directions
922 sort = sort.toggle('ASC', 'DESC');
923
924 // instead of conditional logic:
925 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
926 </code></pre>
927  * @param {String} value The value to compare to the current string
928  * @param {String} other The new value to use if the string already equals the first value passed in
929  * @return {String} The new value
930  */
931  
932 String.prototype.toggle = function(value, other){
933     return this == value ? other : value;
934 };
935
936
937 /**
938   * Remove invalid unicode characters from a string 
939   *
940   * @return {String} The clean string
941   */
942 String.prototype.unicodeClean = function () {
943     return this.replace(/[\s\S]/g,
944         function(character) {
945             if (character.charCodeAt()< 256) {
946               return character;
947            }
948            try {
949                 encodeURIComponent(character);
950            } catch(e) { 
951               return '';
952            }
953            return character;
954         }
955     );
956 };
957   
958 /*
959  * Based on:
960  * Ext JS Library 1.1.1
961  * Copyright(c) 2006-2007, Ext JS, LLC.
962  *
963  * Originally Released Under LGPL - original licence link has changed is not relivant.
964  *
965  * Fork - LGPL
966  * <script type="text/javascript">
967  */
968
969  /**
970  * @class Number
971  */
972 Roo.applyIf(Number.prototype, {
973     /**
974      * Checks whether or not the current number is within a desired range.  If the number is already within the
975      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
976      * exceeded.  Note that this method returns the constrained value but does not change the current number.
977      * @param {Number} min The minimum number in the range
978      * @param {Number} max The maximum number in the range
979      * @return {Number} The constrained value if outside the range, otherwise the current value
980      */
981     constrain : function(min, max){
982         return Math.min(Math.max(this, min), max);
983     }
984 });/*
985  * Based on:
986  * Ext JS Library 1.1.1
987  * Copyright(c) 2006-2007, Ext JS, LLC.
988  *
989  * Originally Released Under LGPL - original licence link has changed is not relivant.
990  *
991  * Fork - LGPL
992  * <script type="text/javascript">
993  */
994  /**
995  * @class Array
996  */
997 Roo.applyIf(Array.prototype, {
998     /**
999      * 
1000      * Checks whether or not the specified object exists in the array.
1001      * @param {Object} o The object to check for
1002      * @return {Number} The index of o in the array (or -1 if it is not found)
1003      */
1004     indexOf : function(o){
1005        for (var i = 0, len = this.length; i < len; i++){
1006               if(this[i] == o) { return i; }
1007        }
1008            return -1;
1009     },
1010
1011     /**
1012      * Removes the specified object from the array.  If the object is not found nothing happens.
1013      * @param {Object} o The object to remove
1014      */
1015     remove : function(o){
1016        var index = this.indexOf(o);
1017        if(index != -1){
1018            this.splice(index, 1);
1019        }
1020     },
1021     /**
1022      * Map (JS 1.6 compatibility)
1023      * @param {Function} function  to call
1024      */
1025     map : function(fun )
1026     {
1027         var len = this.length >>> 0;
1028         if (typeof fun != "function") {
1029             throw new TypeError();
1030         }
1031         var res = new Array(len);
1032         var thisp = arguments[1];
1033         for (var i = 0; i < len; i++)
1034         {
1035             if (i in this) {
1036                 res[i] = fun.call(thisp, this[i], i, this);
1037             }
1038         }
1039
1040         return res;
1041     },
1042     /**
1043      * equals
1044      * @param {Array} o The array to compare to
1045      * @returns {Boolean} true if the same
1046      */
1047     equals : function(b)
1048     {
1049             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1050         if (this === b) {
1051             return true;
1052         }
1053         if (b == null) {
1054             return false;
1055         }
1056         if (this.length !== b.length) {
1057             return false;
1058         }
1059           
1060         // sort?? a.sort().equals(b.sort());
1061           
1062         for (var i = 0; i < this.length; ++i) {
1063             if (this[i] !== b[i]) {
1064             return false;
1065             }
1066         }
1067         return true;
1068     } 
1069     
1070     
1071     
1072     
1073 });
1074
1075 Roo.applyIf(Array, {
1076  /**
1077      * from
1078      * @static
1079      * @param {Array} o Or Array like object (eg. nodelist)
1080      * @returns {Array} 
1081      */
1082     from : function(o)
1083     {
1084         var ret= [];
1085     
1086         for (var i =0; i < o.length; i++) { 
1087             ret[i] = o[i];
1088         }
1089         return ret;
1090       
1091     }
1092 });
1093 /*
1094  * Based on:
1095  * Ext JS Library 1.1.1
1096  * Copyright(c) 2006-2007, Ext JS, LLC.
1097  *
1098  * Originally Released Under LGPL - original licence link has changed is not relivant.
1099  *
1100  * Fork - LGPL
1101  * <script type="text/javascript">
1102  */
1103
1104 /**
1105  * @class Date
1106  *
1107  * The date parsing and format syntax is a subset of
1108  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1109  * supported will provide results equivalent to their PHP versions.
1110  *
1111  * Following is the list of all currently supported formats:
1112  *<pre>
1113 Sample date:
1114 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1115
1116 Format  Output      Description
1117 ------  ----------  --------------------------------------------------------------
1118   d      10         Day of the month, 2 digits with leading zeros
1119   D      Wed        A textual representation of a day, three letters
1120   j      10         Day of the month without leading zeros
1121   l      Wednesday  A full textual representation of the day of the week
1122   S      th         English ordinal day of month suffix, 2 chars (use with j)
1123   w      3          Numeric representation of the day of the week
1124   z      9          The julian date, or day of the year (0-365)
1125   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1126   F      January    A full textual representation of the month
1127   m      01         Numeric representation of a month, with leading zeros
1128   M      Jan        Month name abbreviation, three letters
1129   n      1          Numeric representation of a month, without leading zeros
1130   t      31         Number of days in the given month
1131   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1132   Y      2007       A full numeric representation of a year, 4 digits
1133   y      07         A two digit representation of a year
1134   a      pm         Lowercase Ante meridiem and Post meridiem
1135   A      PM         Uppercase Ante meridiem and Post meridiem
1136   g      3          12-hour format of an hour without leading zeros
1137   G      15         24-hour format of an hour without leading zeros
1138   h      03         12-hour format of an hour with leading zeros
1139   H      15         24-hour format of an hour with leading zeros
1140   i      05         Minutes with leading zeros
1141   s      01         Seconds, with leading zeros
1142   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1143   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1144   T      CST        Timezone setting of the machine running the code
1145   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1146 </pre>
1147  *
1148  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1149  * <pre><code>
1150 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1151 document.write(dt.format('Y-m-d'));                         //2007-01-10
1152 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1153 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1154  </code></pre>
1155  *
1156  * Here are some standard date/time patterns that you might find helpful.  They
1157  * are not part of the source of Date.js, but to use them you can simply copy this
1158  * block of code into any script that is included after Date.js and they will also become
1159  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1160  * <pre><code>
1161 Date.patterns = {
1162     ISO8601Long:"Y-m-d H:i:s",
1163     ISO8601Short:"Y-m-d",
1164     ShortDate: "n/j/Y",
1165     LongDate: "l, F d, Y",
1166     FullDateTime: "l, F d, Y g:i:s A",
1167     MonthDay: "F d",
1168     ShortTime: "g:i A",
1169     LongTime: "g:i:s A",
1170     SortableDateTime: "Y-m-d\\TH:i:s",
1171     UniversalSortableDateTime: "Y-m-d H:i:sO",
1172     YearMonth: "F, Y"
1173 };
1174 </code></pre>
1175  *
1176  * Example usage:
1177  * <pre><code>
1178 var dt = new Date();
1179 document.write(dt.format(Date.patterns.ShortDate));
1180  </code></pre>
1181  */
1182
1183 /*
1184  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1185  * They generate precompiled functions from date formats instead of parsing and
1186  * processing the pattern every time you format a date.  These functions are available
1187  * on every Date object (any javascript function).
1188  *
1189  * The original article and download are here:
1190  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1191  *
1192  */
1193  
1194  
1195  // was in core
1196 /**
1197  Returns the number of milliseconds between this date and date
1198  @param {Date} date (optional) Defaults to now
1199  @return {Number} The diff in milliseconds
1200  @member Date getElapsed
1201  */
1202 Date.prototype.getElapsed = function(date) {
1203         return Math.abs((date || new Date()).getTime()-this.getTime());
1204 };
1205 // was in date file..
1206
1207
1208 // private
1209 Date.parseFunctions = {count:0};
1210 // private
1211 Date.parseRegexes = [];
1212 // private
1213 Date.formatFunctions = {count:0};
1214
1215 // private
1216 Date.prototype.dateFormat = function(format) {
1217     if (Date.formatFunctions[format] == null) {
1218         Date.createNewFormat(format);
1219     }
1220     var func = Date.formatFunctions[format];
1221     return this[func]();
1222 };
1223
1224
1225 /**
1226  * Formats a date given the supplied format string
1227  * @param {String} format The format string
1228  * @return {String} The formatted date
1229  * @method
1230  */
1231 Date.prototype.format = Date.prototype.dateFormat;
1232
1233 // private
1234 Date.createNewFormat = function(format) {
1235     var funcName = "format" + Date.formatFunctions.count++;
1236     Date.formatFunctions[format] = funcName;
1237     var code = "Date.prototype." + funcName + " = function(){return ";
1238     var special = false;
1239     var ch = '';
1240     for (var i = 0; i < format.length; ++i) {
1241         ch = format.charAt(i);
1242         if (!special && ch == "\\") {
1243             special = true;
1244         }
1245         else if (special) {
1246             special = false;
1247             code += "'" + String.escape(ch) + "' + ";
1248         }
1249         else {
1250             code += Date.getFormatCode(ch);
1251         }
1252     }
1253     /** eval:var:zzzzzzzzzzzzz */
1254     eval(code.substring(0, code.length - 3) + ";}");
1255 };
1256
1257 // private
1258 Date.getFormatCode = function(character) {
1259     switch (character) {
1260     case "d":
1261         return "String.leftPad(this.getDate(), 2, '0') + ";
1262     case "D":
1263         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1264     case "j":
1265         return "this.getDate() + ";
1266     case "l":
1267         return "Date.dayNames[this.getDay()] + ";
1268     case "S":
1269         return "this.getSuffix() + ";
1270     case "w":
1271         return "this.getDay() + ";
1272     case "z":
1273         return "this.getDayOfYear() + ";
1274     case "W":
1275         return "this.getWeekOfYear() + ";
1276     case "F":
1277         return "Date.monthNames[this.getMonth()] + ";
1278     case "m":
1279         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1280     case "M":
1281         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1282     case "n":
1283         return "(this.getMonth() + 1) + ";
1284     case "t":
1285         return "this.getDaysInMonth() + ";
1286     case "L":
1287         return "(this.isLeapYear() ? 1 : 0) + ";
1288     case "Y":
1289         return "this.getFullYear() + ";
1290     case "y":
1291         return "('' + this.getFullYear()).substring(2, 4) + ";
1292     case "a":
1293         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1294     case "A":
1295         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1296     case "g":
1297         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1298     case "G":
1299         return "this.getHours() + ";
1300     case "h":
1301         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1302     case "H":
1303         return "String.leftPad(this.getHours(), 2, '0') + ";
1304     case "i":
1305         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1306     case "s":
1307         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1308     case "O":
1309         return "this.getGMTOffset() + ";
1310     case "P":
1311         return "this.getGMTColonOffset() + ";
1312     case "T":
1313         return "this.getTimezone() + ";
1314     case "Z":
1315         return "(this.getTimezoneOffset() * -60) + ";
1316     default:
1317         return "'" + String.escape(character) + "' + ";
1318     }
1319 };
1320
1321 /**
1322  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1323  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1324  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1325  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1326  * string or the parse operation will fail.
1327  * Example Usage:
1328 <pre><code>
1329 //dt = Fri May 25 2007 (current date)
1330 var dt = new Date();
1331
1332 //dt = Thu May 25 2006 (today's month/day in 2006)
1333 dt = Date.parseDate("2006", "Y");
1334
1335 //dt = Sun Jan 15 2006 (all date parts specified)
1336 dt = Date.parseDate("2006-1-15", "Y-m-d");
1337
1338 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1339 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1340 </code></pre>
1341  * @param {String} input The unparsed date as a string
1342  * @param {String} format The format the date is in
1343  * @return {Date} The parsed date
1344  * @static
1345  */
1346 Date.parseDate = function(input, format) {
1347     if (Date.parseFunctions[format] == null) {
1348         Date.createParser(format);
1349     }
1350     var func = Date.parseFunctions[format];
1351     return Date[func](input);
1352 };
1353 /**
1354  * @private
1355  */
1356
1357 Date.createParser = function(format) {
1358     var funcName = "parse" + Date.parseFunctions.count++;
1359     var regexNum = Date.parseRegexes.length;
1360     var currentGroup = 1;
1361     Date.parseFunctions[format] = funcName;
1362
1363     var code = "Date." + funcName + " = function(input){\n"
1364         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1365         + "var d = new Date();\n"
1366         + "y = d.getFullYear();\n"
1367         + "m = d.getMonth();\n"
1368         + "d = d.getDate();\n"
1369         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1370         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1371         + "if (results && results.length > 0) {";
1372     var regex = "";
1373
1374     var special = false;
1375     var ch = '';
1376     for (var i = 0; i < format.length; ++i) {
1377         ch = format.charAt(i);
1378         if (!special && ch == "\\") {
1379             special = true;
1380         }
1381         else if (special) {
1382             special = false;
1383             regex += String.escape(ch);
1384         }
1385         else {
1386             var obj = Date.formatCodeToRegex(ch, currentGroup);
1387             currentGroup += obj.g;
1388             regex += obj.s;
1389             if (obj.g && obj.c) {
1390                 code += obj.c;
1391             }
1392         }
1393     }
1394
1395     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1396         + "{v = new Date(y, m, d, h, i, s);}\n"
1397         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1398         + "{v = new Date(y, m, d, h, i);}\n"
1399         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1400         + "{v = new Date(y, m, d, h);}\n"
1401         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1402         + "{v = new Date(y, m, d);}\n"
1403         + "else if (y >= 0 && m >= 0)\n"
1404         + "{v = new Date(y, m);}\n"
1405         + "else if (y >= 0)\n"
1406         + "{v = new Date(y);}\n"
1407         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1408         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1409         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1410         + ";}";
1411
1412     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1413     /** eval:var:zzzzzzzzzzzzz */
1414     eval(code);
1415 };
1416
1417 // private
1418 Date.formatCodeToRegex = function(character, currentGroup) {
1419     switch (character) {
1420     case "D":
1421         return {g:0,
1422         c:null,
1423         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1424     case "j":
1425         return {g:1,
1426             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1427             s:"(\\d{1,2})"}; // day of month without leading zeroes
1428     case "d":
1429         return {g:1,
1430             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1431             s:"(\\d{2})"}; // day of month with leading zeroes
1432     case "l":
1433         return {g:0,
1434             c:null,
1435             s:"(?:" + Date.dayNames.join("|") + ")"};
1436     case "S":
1437         return {g:0,
1438             c:null,
1439             s:"(?:st|nd|rd|th)"};
1440     case "w":
1441         return {g:0,
1442             c:null,
1443             s:"\\d"};
1444     case "z":
1445         return {g:0,
1446             c:null,
1447             s:"(?:\\d{1,3})"};
1448     case "W":
1449         return {g:0,
1450             c:null,
1451             s:"(?:\\d{2})"};
1452     case "F":
1453         return {g:1,
1454             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1455             s:"(" + Date.monthNames.join("|") + ")"};
1456     case "M":
1457         return {g:1,
1458             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1459             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1460     case "n":
1461         return {g:1,
1462             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1463             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1464     case "m":
1465         return {g:1,
1466             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1467             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1468     case "t":
1469         return {g:0,
1470             c:null,
1471             s:"\\d{1,2}"};
1472     case "L":
1473         return {g:0,
1474             c:null,
1475             s:"(?:1|0)"};
1476     case "Y":
1477         return {g:1,
1478             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1479             s:"(\\d{4})"};
1480     case "y":
1481         return {g:1,
1482             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1483                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1484             s:"(\\d{1,2})"};
1485     case "a":
1486         return {g:1,
1487             c:"if (results[" + currentGroup + "] == 'am') {\n"
1488                 + "if (h == 12) { h = 0; }\n"
1489                 + "} else { if (h < 12) { h += 12; }}",
1490             s:"(am|pm)"};
1491     case "A":
1492         return {g:1,
1493             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1494                 + "if (h == 12) { h = 0; }\n"
1495                 + "} else { if (h < 12) { h += 12; }}",
1496             s:"(AM|PM)"};
1497     case "g":
1498     case "G":
1499         return {g:1,
1500             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1501             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1502     case "h":
1503     case "H":
1504         return {g:1,
1505             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1506             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1507     case "i":
1508         return {g:1,
1509             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1510             s:"(\\d{2})"};
1511     case "s":
1512         return {g:1,
1513             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1514             s:"(\\d{2})"};
1515     case "O":
1516         return {g:1,
1517             c:[
1518                 "o = results[", currentGroup, "];\n",
1519                 "var sn = o.substring(0,1);\n", // get + / - sign
1520                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1521                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1522                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1523                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1524             ].join(""),
1525             s:"([+\-]\\d{2,4})"};
1526     
1527     
1528     case "P":
1529         return {g:1,
1530                 c:[
1531                    "o = results[", currentGroup, "];\n",
1532                    "var sn = o.substring(0,1);\n",
1533                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1534                    "var mn = o.substring(4,6) % 60;\n",
1535                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1536                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1537             ].join(""),
1538             s:"([+\-]\\d{4})"};
1539     case "T":
1540         return {g:0,
1541             c:null,
1542             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1543     case "Z":
1544         return {g:1,
1545             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1546                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1547             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1548     default:
1549         return {g:0,
1550             c:null,
1551             s:String.escape(character)};
1552     }
1553 };
1554
1555 /**
1556  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1557  * @return {String} The abbreviated timezone name (e.g. 'CST')
1558  */
1559 Date.prototype.getTimezone = function() {
1560     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1561 };
1562
1563 /**
1564  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1565  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1566  */
1567 Date.prototype.getGMTOffset = function() {
1568     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1569         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1570         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1571 };
1572
1573 /**
1574  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1575  * @return {String} 2-characters representing hours and 2-characters representing minutes
1576  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1577  */
1578 Date.prototype.getGMTColonOffset = function() {
1579         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1580                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1581                 + ":"
1582                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1583 }
1584
1585 /**
1586  * Get the numeric day number of the year, adjusted for leap year.
1587  * @return {Number} 0 through 364 (365 in leap years)
1588  */
1589 Date.prototype.getDayOfYear = function() {
1590     var num = 0;
1591     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1592     for (var i = 0; i < this.getMonth(); ++i) {
1593         num += Date.daysInMonth[i];
1594     }
1595     return num + this.getDate() - 1;
1596 };
1597
1598 /**
1599  * Get the string representation of the numeric week number of the year
1600  * (equivalent to the format specifier 'W').
1601  * @return {String} '00' through '52'
1602  */
1603 Date.prototype.getWeekOfYear = function() {
1604     // Skip to Thursday of this week
1605     var now = this.getDayOfYear() + (4 - this.getDay());
1606     // Find the first Thursday of the year
1607     var jan1 = new Date(this.getFullYear(), 0, 1);
1608     var then = (7 - jan1.getDay() + 4);
1609     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1610 };
1611
1612 /**
1613  * Whether or not the current date is in a leap year.
1614  * @return {Boolean} True if the current date is in a leap year, else false
1615  */
1616 Date.prototype.isLeapYear = function() {
1617     var year = this.getFullYear();
1618     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1619 };
1620
1621 /**
1622  * Get the first day of the current month, adjusted for leap year.  The returned value
1623  * is the numeric day index within the week (0-6) which can be used in conjunction with
1624  * the {@link #monthNames} array to retrieve the textual day name.
1625  * Example:
1626  *<pre><code>
1627 var dt = new Date('1/10/2007');
1628 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1629 </code></pre>
1630  * @return {Number} The day number (0-6)
1631  */
1632 Date.prototype.getFirstDayOfMonth = function() {
1633     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1634     return (day < 0) ? (day + 7) : day;
1635 };
1636
1637 /**
1638  * Get the last day of the current month, adjusted for leap year.  The returned value
1639  * is the numeric day index within the week (0-6) which can be used in conjunction with
1640  * the {@link #monthNames} array to retrieve the textual day name.
1641  * Example:
1642  *<pre><code>
1643 var dt = new Date('1/10/2007');
1644 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1645 </code></pre>
1646  * @return {Number} The day number (0-6)
1647  */
1648 Date.prototype.getLastDayOfMonth = function() {
1649     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1650     return (day < 0) ? (day + 7) : day;
1651 };
1652
1653
1654 /**
1655  * Get the first date of this date's month
1656  * @return {Date}
1657  */
1658 Date.prototype.getFirstDateOfMonth = function() {
1659     return new Date(this.getFullYear(), this.getMonth(), 1);
1660 };
1661
1662 /**
1663  * Get the last date of this date's month
1664  * @return {Date}
1665  */
1666 Date.prototype.getLastDateOfMonth = function() {
1667     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1668 };
1669 /**
1670  * Get the number of days in the current month, adjusted for leap year.
1671  * @return {Number} The number of days in the month
1672  */
1673 Date.prototype.getDaysInMonth = function() {
1674     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1675     return Date.daysInMonth[this.getMonth()];
1676 };
1677
1678 /**
1679  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1680  * @return {String} 'st, 'nd', 'rd' or 'th'
1681  */
1682 Date.prototype.getSuffix = function() {
1683     switch (this.getDate()) {
1684         case 1:
1685         case 21:
1686         case 31:
1687             return "st";
1688         case 2:
1689         case 22:
1690             return "nd";
1691         case 3:
1692         case 23:
1693             return "rd";
1694         default:
1695             return "th";
1696     }
1697 };
1698
1699 // private
1700 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1701
1702 /**
1703  * An array of textual month names.
1704  * Override these values for international dates, for example...
1705  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1706  * @type Array
1707  * @static
1708  */
1709 Date.monthNames =
1710    ["January",
1711     "February",
1712     "March",
1713     "April",
1714     "May",
1715     "June",
1716     "July",
1717     "August",
1718     "September",
1719     "October",
1720     "November",
1721     "December"];
1722
1723 /**
1724  * An array of textual day names.
1725  * Override these values for international dates, for example...
1726  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1727  * @type Array
1728  * @static
1729  */
1730 Date.dayNames =
1731    ["Sunday",
1732     "Monday",
1733     "Tuesday",
1734     "Wednesday",
1735     "Thursday",
1736     "Friday",
1737     "Saturday"];
1738
1739 // private
1740 Date.y2kYear = 50;
1741 // private
1742 Date.monthNumbers = {
1743     Jan:0,
1744     Feb:1,
1745     Mar:2,
1746     Apr:3,
1747     May:4,
1748     Jun:5,
1749     Jul:6,
1750     Aug:7,
1751     Sep:8,
1752     Oct:9,
1753     Nov:10,
1754     Dec:11};
1755
1756 /**
1757  * Creates and returns a new Date instance with the exact same date value as the called instance.
1758  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1759  * variable will also be changed.  When the intention is to create a new variable that will not
1760  * modify the original instance, you should create a clone.
1761  *
1762  * Example of correctly cloning a date:
1763  * <pre><code>
1764 //wrong way:
1765 var orig = new Date('10/1/2006');
1766 var copy = orig;
1767 copy.setDate(5);
1768 document.write(orig);  //returns 'Thu Oct 05 2006'!
1769
1770 //correct way:
1771 var orig = new Date('10/1/2006');
1772 var copy = orig.clone();
1773 copy.setDate(5);
1774 document.write(orig);  //returns 'Thu Oct 01 2006'
1775 </code></pre>
1776  * @return {Date} The new Date instance
1777  */
1778 Date.prototype.clone = function() {
1779         return new Date(this.getTime());
1780 };
1781
1782 /**
1783  * Clears any time information from this date
1784  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1785  @return {Date} this or the clone
1786  */
1787 Date.prototype.clearTime = function(clone){
1788     if(clone){
1789         return this.clone().clearTime();
1790     }
1791     this.setHours(0);
1792     this.setMinutes(0);
1793     this.setSeconds(0);
1794     this.setMilliseconds(0);
1795     return this;
1796 };
1797
1798 // private
1799 // safari setMonth is broken -- check that this is only donw once...
1800 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1801     Date.brokenSetMonth = Date.prototype.setMonth;
1802         Date.prototype.setMonth = function(num){
1803                 if(num <= -1){
1804                         var n = Math.ceil(-num);
1805                         var back_year = Math.ceil(n/12);
1806                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1807                         this.setFullYear(this.getFullYear() - back_year);
1808                         return Date.brokenSetMonth.call(this, month);
1809                 } else {
1810                         return Date.brokenSetMonth.apply(this, arguments);
1811                 }
1812         };
1813 }
1814
1815 /** Date interval constant 
1816 * @static 
1817 * @type String */
1818 Date.MILLI = "ms";
1819 /** Date interval constant 
1820 * @static 
1821 * @type String */
1822 Date.SECOND = "s";
1823 /** Date interval constant 
1824 * @static 
1825 * @type String */
1826 Date.MINUTE = "mi";
1827 /** Date interval constant 
1828 * @static 
1829 * @type String */
1830 Date.HOUR = "h";
1831 /** Date interval constant 
1832 * @static 
1833 * @type String */
1834 Date.DAY = "d";
1835 /** Date interval constant 
1836 * @static 
1837 * @type String */
1838 Date.MONTH = "mo";
1839 /** Date interval constant 
1840 * @static 
1841 * @type String */
1842 Date.YEAR = "y";
1843
1844 /**
1845  * Provides a convenient method of performing basic date arithmetic.  This method
1846  * does not modify the Date instance being called - it creates and returns
1847  * a new Date instance containing the resulting date value.
1848  *
1849  * Examples:
1850  * <pre><code>
1851 //Basic usage:
1852 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1853 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1854
1855 //Negative values will subtract correctly:
1856 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1857 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1858
1859 //You can even chain several calls together in one line!
1860 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1861 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1862  </code></pre>
1863  *
1864  * @param {String} interval   A valid date interval enum value
1865  * @param {Number} value      The amount to add to the current date
1866  * @return {Date} The new Date instance
1867  */
1868 Date.prototype.add = function(interval, value){
1869   var d = this.clone();
1870   if (!interval || value === 0) { return d; }
1871   switch(interval.toLowerCase()){
1872     case Date.MILLI:
1873       d.setMilliseconds(this.getMilliseconds() + value);
1874       break;
1875     case Date.SECOND:
1876       d.setSeconds(this.getSeconds() + value);
1877       break;
1878     case Date.MINUTE:
1879       d.setMinutes(this.getMinutes() + value);
1880       break;
1881     case Date.HOUR:
1882       d.setHours(this.getHours() + value);
1883       break;
1884     case Date.DAY:
1885       d.setDate(this.getDate() + value);
1886       break;
1887     case Date.MONTH:
1888       var day = this.getDate();
1889       if(day > 28){
1890           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1891       }
1892       d.setDate(day);
1893       d.setMonth(this.getMonth() + value);
1894       break;
1895     case Date.YEAR:
1896       d.setFullYear(this.getFullYear() + value);
1897       break;
1898   }
1899   return d;
1900 };
1901 /**
1902  * @class Roo.lib.Dom
1903  * @licence LGPL
1904  * @static
1905  * 
1906  * Dom utils (from YIU afaik)
1907  *
1908  * 
1909  **/
1910 Roo.lib.Dom = {
1911     /**
1912      * Get the view width
1913      * @param {Boolean} full True will get the full document, otherwise it's the view width
1914      * @return {Number} The width
1915      */
1916      
1917     getViewWidth : function(full) {
1918         return full ? this.getDocumentWidth() : this.getViewportWidth();
1919     },
1920     /**
1921      * Get the view height
1922      * @param {Boolean} full True will get the full document, otherwise it's the view height
1923      * @return {Number} The height
1924      */
1925     getViewHeight : function(full) {
1926         return full ? this.getDocumentHeight() : this.getViewportHeight();
1927     },
1928     /**
1929      * Get the Full Document height 
1930      * @return {Number} The height
1931      */
1932     getDocumentHeight: function() {
1933         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1934         return Math.max(scrollHeight, this.getViewportHeight());
1935     },
1936     /**
1937      * Get the Full Document width
1938      * @return {Number} The width
1939      */
1940     getDocumentWidth: function() {
1941         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1942         return Math.max(scrollWidth, this.getViewportWidth());
1943     },
1944     /**
1945      * Get the Window Viewport height
1946      * @return {Number} The height
1947      */
1948     getViewportHeight: function() {
1949         var height = self.innerHeight;
1950         var mode = document.compatMode;
1951
1952         if ((mode || Roo.isIE) && !Roo.isOpera) {
1953             height = (mode == "CSS1Compat") ?
1954                      document.documentElement.clientHeight :
1955                      document.body.clientHeight;
1956         }
1957
1958         return height;
1959     },
1960     /**
1961      * Get the Window Viewport width
1962      * @return {Number} The width
1963      */
1964     getViewportWidth: function() {
1965         var width = self.innerWidth;
1966         var mode = document.compatMode;
1967
1968         if (mode || Roo.isIE) {
1969             width = (mode == "CSS1Compat") ?
1970                     document.documentElement.clientWidth :
1971                     document.body.clientWidth;
1972         }
1973         return width;
1974     },
1975
1976     isAncestor : function(p, c) {
1977         p = Roo.getDom(p);
1978         c = Roo.getDom(c);
1979         if (!p || !c) {
1980             return false;
1981         }
1982
1983         if (p.contains && !Roo.isSafari) {
1984             return p.contains(c);
1985         } else if (p.compareDocumentPosition) {
1986             return !!(p.compareDocumentPosition(c) & 16);
1987         } else {
1988             var parent = c.parentNode;
1989             while (parent) {
1990                 if (parent == p) {
1991                     return true;
1992                 }
1993                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1994                     return false;
1995                 }
1996                 parent = parent.parentNode;
1997             }
1998             return false;
1999         }
2000     },
2001
2002     getRegion : function(el) {
2003         return Roo.lib.Region.getRegion(el);
2004     },
2005
2006     getY : function(el) {
2007         return this.getXY(el)[1];
2008     },
2009
2010     getX : function(el) {
2011         return this.getXY(el)[0];
2012     },
2013
2014     getXY : function(el) {
2015         var p, pe, b, scroll, bd = document.body;
2016         el = Roo.getDom(el);
2017         var fly = Roo.lib.AnimBase.fly;
2018         if (el.getBoundingClientRect) {
2019             b = el.getBoundingClientRect();
2020             scroll = fly(document).getScroll();
2021             return [b.left + scroll.left, b.top + scroll.top];
2022         }
2023         var x = 0, y = 0;
2024
2025         p = el;
2026
2027         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2028
2029         while (p) {
2030
2031             x += p.offsetLeft;
2032             y += p.offsetTop;
2033
2034             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2035                 hasAbsolute = true;
2036             }
2037
2038             if (Roo.isGecko) {
2039                 pe = fly(p);
2040
2041                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2042                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2043
2044
2045                 x += bl;
2046                 y += bt;
2047
2048
2049                 if (p != el && pe.getStyle('overflow') != 'visible') {
2050                     x += bl;
2051                     y += bt;
2052                 }
2053             }
2054             p = p.offsetParent;
2055         }
2056
2057         if (Roo.isSafari && hasAbsolute) {
2058             x -= bd.offsetLeft;
2059             y -= bd.offsetTop;
2060         }
2061
2062         if (Roo.isGecko && !hasAbsolute) {
2063             var dbd = fly(bd);
2064             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2065             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2066         }
2067
2068         p = el.parentNode;
2069         while (p && p != bd) {
2070             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2071                 x -= p.scrollLeft;
2072                 y -= p.scrollTop;
2073             }
2074             p = p.parentNode;
2075         }
2076         return [x, y];
2077     },
2078  
2079   
2080
2081
2082     setXY : function(el, xy) {
2083         el = Roo.fly(el, '_setXY');
2084         el.position();
2085         var pts = el.translatePoints(xy);
2086         if (xy[0] !== false) {
2087             el.dom.style.left = pts.left + "px";
2088         }
2089         if (xy[1] !== false) {
2090             el.dom.style.top = pts.top + "px";
2091         }
2092     },
2093
2094     setX : function(el, x) {
2095         this.setXY(el, [x, false]);
2096     },
2097
2098     setY : function(el, y) {
2099         this.setXY(el, [false, y]);
2100     }
2101 };
2102 /*
2103  * Portions of this file are based on pieces of Yahoo User Interface Library
2104  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2105  * YUI licensed under the BSD License:
2106  * http://developer.yahoo.net/yui/license.txt
2107  * <script type="text/javascript">
2108  *
2109  */
2110
2111 Roo.lib.Event = function() {
2112     var loadComplete = false;
2113     var listeners = [];
2114     var unloadListeners = [];
2115     var retryCount = 0;
2116     var onAvailStack = [];
2117     var counter = 0;
2118     var lastError = null;
2119
2120     return {
2121         POLL_RETRYS: 200,
2122         POLL_INTERVAL: 20,
2123         EL: 0,
2124         TYPE: 1,
2125         FN: 2,
2126         WFN: 3,
2127         OBJ: 3,
2128         ADJ_SCOPE: 4,
2129         _interval: null,
2130
2131         startInterval: function() {
2132             if (!this._interval) {
2133                 var self = this;
2134                 var callback = function() {
2135                     self._tryPreloadAttach();
2136                 };
2137                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2138
2139             }
2140         },
2141
2142         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2143             onAvailStack.push({ id:         p_id,
2144                 fn:         p_fn,
2145                 obj:        p_obj,
2146                 override:   p_override,
2147                 checkReady: false    });
2148
2149             retryCount = this.POLL_RETRYS;
2150             this.startInterval();
2151         },
2152
2153
2154         addListener: function(el, eventName, fn) {
2155             el = Roo.getDom(el);
2156             if (!el || !fn) {
2157                 return false;
2158             }
2159
2160             if ("unload" == eventName) {
2161                 unloadListeners[unloadListeners.length] =
2162                 [el, eventName, fn];
2163                 return true;
2164             }
2165
2166             var wrappedFn = function(e) {
2167                 return fn(Roo.lib.Event.getEvent(e));
2168             };
2169
2170             var li = [el, eventName, fn, wrappedFn];
2171
2172             var index = listeners.length;
2173             listeners[index] = li;
2174
2175             this.doAdd(el, eventName, wrappedFn, false);
2176             return true;
2177
2178         },
2179
2180
2181         removeListener: function(el, eventName, fn) {
2182             var i, len;
2183
2184             el = Roo.getDom(el);
2185
2186             if(!fn) {
2187                 return this.purgeElement(el, false, eventName);
2188             }
2189
2190
2191             if ("unload" == eventName) {
2192
2193                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2194                     var li = unloadListeners[i];
2195                     if (li &&
2196                         li[0] == el &&
2197                         li[1] == eventName &&
2198                         li[2] == fn) {
2199                         unloadListeners.splice(i, 1);
2200                         return true;
2201                     }
2202                 }
2203
2204                 return false;
2205             }
2206
2207             var cacheItem = null;
2208
2209
2210             var index = arguments[3];
2211
2212             if ("undefined" == typeof index) {
2213                 index = this._getCacheIndex(el, eventName, fn);
2214             }
2215
2216             if (index >= 0) {
2217                 cacheItem = listeners[index];
2218             }
2219
2220             if (!el || !cacheItem) {
2221                 return false;
2222             }
2223
2224             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2225
2226             delete listeners[index][this.WFN];
2227             delete listeners[index][this.FN];
2228             listeners.splice(index, 1);
2229
2230             return true;
2231
2232         },
2233
2234
2235         getTarget: function(ev, resolveTextNode) {
2236             ev = ev.browserEvent || ev;
2237             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2238             var t = ev.target || ev.srcElement;
2239             return this.resolveTextNode(t);
2240         },
2241
2242
2243         resolveTextNode: function(node) {
2244             if (Roo.isSafari && node && 3 == node.nodeType) {
2245                 return node.parentNode;
2246             } else {
2247                 return node;
2248             }
2249         },
2250
2251
2252         getPageX: function(ev) {
2253             ev = ev.browserEvent || ev;
2254             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2255             var x = ev.pageX;
2256             if (!x && 0 !== x) {
2257                 x = ev.clientX || 0;
2258
2259                 if (Roo.isIE) {
2260                     x += this.getScroll()[1];
2261                 }
2262             }
2263
2264             return x;
2265         },
2266
2267
2268         getPageY: function(ev) {
2269             ev = ev.browserEvent || ev;
2270             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2271             var y = ev.pageY;
2272             if (!y && 0 !== y) {
2273                 y = ev.clientY || 0;
2274
2275                 if (Roo.isIE) {
2276                     y += this.getScroll()[0];
2277                 }
2278             }
2279
2280
2281             return y;
2282         },
2283
2284
2285         getXY: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2288             return [this.getPageX(ev), this.getPageY(ev)];
2289         },
2290
2291
2292         getRelatedTarget: function(ev) {
2293             ev = ev.browserEvent || ev;
2294             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2295             var t = ev.relatedTarget;
2296             if (!t) {
2297                 if (ev.type == "mouseout") {
2298                     t = ev.toElement;
2299                 } else if (ev.type == "mouseover") {
2300                     t = ev.fromElement;
2301                 }
2302             }
2303
2304             return this.resolveTextNode(t);
2305         },
2306
2307
2308         getTime: function(ev) {
2309             ev = ev.browserEvent || ev;
2310             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2311             if (!ev.time) {
2312                 var t = new Date().getTime();
2313                 try {
2314                     ev.time = t;
2315                 } catch(ex) {
2316                     this.lastError = ex;
2317                     return t;
2318                 }
2319             }
2320
2321             return ev.time;
2322         },
2323
2324
2325         stopEvent: function(ev) {
2326             this.stopPropagation(ev);
2327             this.preventDefault(ev);
2328         },
2329
2330
2331         stopPropagation: function(ev) {
2332             ev = ev.browserEvent || ev;
2333             if (ev.stopPropagation) {
2334                 ev.stopPropagation();
2335             } else {
2336                 ev.cancelBubble = true;
2337             }
2338         },
2339
2340
2341         preventDefault: function(ev) {
2342             ev = ev.browserEvent || ev;
2343             if(ev.preventDefault) {
2344                 ev.preventDefault();
2345             } else {
2346                 ev.returnValue = false;
2347             }
2348         },
2349
2350
2351         getEvent: function(e) {
2352             var ev = e || window.event;
2353             if (!ev) {
2354                 var c = this.getEvent.caller;
2355                 while (c) {
2356                     ev = c.arguments[0];
2357                     if (ev && Event == ev.constructor) {
2358                         break;
2359                     }
2360                     c = c.caller;
2361                 }
2362             }
2363             return ev;
2364         },
2365
2366
2367         getCharCode: function(ev) {
2368             ev = ev.browserEvent || ev;
2369             return ev.charCode || ev.keyCode || 0;
2370         },
2371
2372
2373         _getCacheIndex: function(el, eventName, fn) {
2374             for (var i = 0,len = listeners.length; i < len; ++i) {
2375                 var li = listeners[i];
2376                 if (li &&
2377                     li[this.FN] == fn &&
2378                     li[this.EL] == el &&
2379                     li[this.TYPE] == eventName) {
2380                     return i;
2381                 }
2382             }
2383
2384             return -1;
2385         },
2386
2387
2388         elCache: {},
2389
2390
2391         getEl: function(id) {
2392             return document.getElementById(id);
2393         },
2394
2395
2396         clearCache: function() {
2397         },
2398
2399
2400         _load: function(e) {
2401             loadComplete = true;
2402             var EU = Roo.lib.Event;
2403
2404
2405             if (Roo.isIE) {
2406                 EU.doRemove(window, "load", EU._load);
2407             }
2408         },
2409
2410
2411         _tryPreloadAttach: function() {
2412
2413             if (this.locked) {
2414                 return false;
2415             }
2416
2417             this.locked = true;
2418
2419
2420             var tryAgain = !loadComplete;
2421             if (!tryAgain) {
2422                 tryAgain = (retryCount > 0);
2423             }
2424
2425
2426             var notAvail = [];
2427             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2428                 var item = onAvailStack[i];
2429                 if (item) {
2430                     var el = this.getEl(item.id);
2431
2432                     if (el) {
2433                         if (!item.checkReady ||
2434                             loadComplete ||
2435                             el.nextSibling ||
2436                             (document && document.body)) {
2437
2438                             var scope = el;
2439                             if (item.override) {
2440                                 if (item.override === true) {
2441                                     scope = item.obj;
2442                                 } else {
2443                                     scope = item.override;
2444                                 }
2445                             }
2446                             item.fn.call(scope, item.obj);
2447                             onAvailStack[i] = null;
2448                         }
2449                     } else {
2450                         notAvail.push(item);
2451                     }
2452                 }
2453             }
2454
2455             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2456
2457             if (tryAgain) {
2458
2459                 this.startInterval();
2460             } else {
2461                 clearInterval(this._interval);
2462                 this._interval = null;
2463             }
2464
2465             this.locked = false;
2466
2467             return true;
2468
2469         },
2470
2471
2472         purgeElement: function(el, recurse, eventName) {
2473             var elListeners = this.getListeners(el, eventName);
2474             if (elListeners) {
2475                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2476                     var l = elListeners[i];
2477                     this.removeListener(el, l.type, l.fn);
2478                 }
2479             }
2480
2481             if (recurse && el && el.childNodes) {
2482                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2483                     this.purgeElement(el.childNodes[i], recurse, eventName);
2484                 }
2485             }
2486         },
2487
2488
2489         getListeners: function(el, eventName) {
2490             var results = [], searchLists;
2491             if (!eventName) {
2492                 searchLists = [listeners, unloadListeners];
2493             } else if (eventName == "unload") {
2494                 searchLists = [unloadListeners];
2495             } else {
2496                 searchLists = [listeners];
2497             }
2498
2499             for (var j = 0; j < searchLists.length; ++j) {
2500                 var searchList = searchLists[j];
2501                 if (searchList && searchList.length > 0) {
2502                     for (var i = 0,len = searchList.length; i < len; ++i) {
2503                         var l = searchList[i];
2504                         if (l && l[this.EL] === el &&
2505                             (!eventName || eventName === l[this.TYPE])) {
2506                             results.push({
2507                                 type:   l[this.TYPE],
2508                                 fn:     l[this.FN],
2509                                 obj:    l[this.OBJ],
2510                                 adjust: l[this.ADJ_SCOPE],
2511                                 index:  i
2512                             });
2513                         }
2514                     }
2515                 }
2516             }
2517
2518             return (results.length) ? results : null;
2519         },
2520
2521
2522         _unload: function(e) {
2523
2524             var EU = Roo.lib.Event, i, j, l, len, index;
2525
2526             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2527                 l = unloadListeners[i];
2528                 if (l) {
2529                     var scope = window;
2530                     if (l[EU.ADJ_SCOPE]) {
2531                         if (l[EU.ADJ_SCOPE] === true) {
2532                             scope = l[EU.OBJ];
2533                         } else {
2534                             scope = l[EU.ADJ_SCOPE];
2535                         }
2536                     }
2537                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2538                     unloadListeners[i] = null;
2539                     l = null;
2540                     scope = null;
2541                 }
2542             }
2543
2544             unloadListeners = null;
2545
2546             if (listeners && listeners.length > 0) {
2547                 j = listeners.length;
2548                 while (j) {
2549                     index = j - 1;
2550                     l = listeners[index];
2551                     if (l) {
2552                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2553                                 l[EU.FN], index);
2554                     }
2555                     j = j - 1;
2556                 }
2557                 l = null;
2558
2559                 EU.clearCache();
2560             }
2561
2562             EU.doRemove(window, "unload", EU._unload);
2563
2564         },
2565
2566
2567         getScroll: function() {
2568             var dd = document.documentElement, db = document.body;
2569             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2570                 return [dd.scrollTop, dd.scrollLeft];
2571             } else if (db) {
2572                 return [db.scrollTop, db.scrollLeft];
2573             } else {
2574                 return [0, 0];
2575             }
2576         },
2577
2578
2579         doAdd: function () {
2580             if (window.addEventListener) {
2581                 return function(el, eventName, fn, capture) {
2582                     el.addEventListener(eventName, fn, (capture));
2583                 };
2584             } else if (window.attachEvent) {
2585                 return function(el, eventName, fn, capture) {
2586                     el.attachEvent("on" + eventName, fn);
2587                 };
2588             } else {
2589                 return function() {
2590                 };
2591             }
2592         }(),
2593
2594
2595         doRemove: function() {
2596             if (window.removeEventListener) {
2597                 return function (el, eventName, fn, capture) {
2598                     el.removeEventListener(eventName, fn, (capture));
2599                 };
2600             } else if (window.detachEvent) {
2601                 return function (el, eventName, fn) {
2602                     el.detachEvent("on" + eventName, fn);
2603                 };
2604             } else {
2605                 return function() {
2606                 };
2607             }
2608         }()
2609     };
2610     
2611 }();
2612 (function() {     
2613    
2614     var E = Roo.lib.Event;
2615     E.on = E.addListener;
2616     E.un = E.removeListener;
2617
2618     if (document && document.body) {
2619         E._load();
2620     } else {
2621         E.doAdd(window, "load", E._load);
2622     }
2623     E.doAdd(window, "unload", E._unload);
2624     E._tryPreloadAttach();
2625 })();
2626
2627  
2628
2629 (function() {
2630     /**
2631      * @class Roo.lib.Ajax
2632      *
2633      * provide a simple Ajax request utility functions
2634      * 
2635      * Portions of this file are based on pieces of Yahoo User Interface Library
2636     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2637     * YUI licensed under the BSD License:
2638     * http://developer.yahoo.net/yui/license.txt
2639     * <script type="text/javascript">
2640     *
2641      *
2642      */
2643     Roo.lib.Ajax = {
2644         /**
2645          * @static 
2646          */
2647         request : function(method, uri, cb, data, options) {
2648             if(options){
2649                 var hs = options.headers;
2650                 if(hs){
2651                     for(var h in hs){
2652                         if(hs.hasOwnProperty(h)){
2653                             this.initHeader(h, hs[h], false);
2654                         }
2655                     }
2656                 }
2657                 if(options.xmlData){
2658                     this.initHeader('Content-Type', 'text/xml', false);
2659                     method = 'POST';
2660                     data = options.xmlData;
2661                 }
2662             }
2663
2664             return this.asyncRequest(method, uri, cb, data);
2665         },
2666         /**
2667          * serialize a form
2668          *
2669          * @static
2670          * @param {DomForm} form element
2671          * @return {String} urlencode form output.
2672          */
2673         serializeForm : function(form) {
2674             if(typeof form == 'string') {
2675                 form = (document.getElementById(form) || document.forms[form]);
2676             }
2677
2678             var el, name, val, disabled, data = '', hasSubmit = false;
2679             for (var i = 0; i < form.elements.length; i++) {
2680                 el = form.elements[i];
2681                 disabled = form.elements[i].disabled;
2682                 name = form.elements[i].name;
2683                 val = form.elements[i].value;
2684
2685                 if (!disabled && name){
2686                     switch (el.type)
2687                             {
2688                         case 'select-one':
2689                         case 'select-multiple':
2690                             for (var j = 0; j < el.options.length; j++) {
2691                                 if (el.options[j].selected) {
2692                                     if (Roo.isIE) {
2693                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2694                                     }
2695                                     else {
2696                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2697                                     }
2698                                 }
2699                             }
2700                             break;
2701                         case 'radio':
2702                         case 'checkbox':
2703                             if (el.checked) {
2704                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2705                             }
2706                             break;
2707                         case 'file':
2708
2709                         case undefined:
2710
2711                         case 'reset':
2712
2713                         case 'button':
2714
2715                             break;
2716                         case 'submit':
2717                             if(hasSubmit == false) {
2718                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2719                                 hasSubmit = true;
2720                             }
2721                             break;
2722                         default:
2723                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2724                             break;
2725                     }
2726                 }
2727             }
2728             data = data.substr(0, data.length - 1);
2729             return data;
2730         },
2731
2732         headers:{},
2733
2734         hasHeaders:false,
2735
2736         useDefaultHeader:true,
2737
2738         defaultPostHeader:'application/x-www-form-urlencoded',
2739
2740         useDefaultXhrHeader:true,
2741
2742         defaultXhrHeader:'XMLHttpRequest',
2743
2744         hasDefaultHeaders:true,
2745
2746         defaultHeaders:{},
2747
2748         poll:{},
2749
2750         timeout:{},
2751
2752         pollInterval:50,
2753
2754         transactionId:0,
2755
2756         setProgId:function(id)
2757         {
2758             this.activeX.unshift(id);
2759         },
2760
2761         setDefaultPostHeader:function(b)
2762         {
2763             this.useDefaultHeader = b;
2764         },
2765
2766         setDefaultXhrHeader:function(b)
2767         {
2768             this.useDefaultXhrHeader = b;
2769         },
2770
2771         setPollingInterval:function(i)
2772         {
2773             if (typeof i == 'number' && isFinite(i)) {
2774                 this.pollInterval = i;
2775             }
2776         },
2777
2778         createXhrObject:function(transactionId)
2779         {
2780             var obj,http;
2781             try
2782             {
2783
2784                 http = new XMLHttpRequest();
2785
2786                 obj = { conn:http, tId:transactionId };
2787             }
2788             catch(e)
2789             {
2790                 for (var i = 0; i < this.activeX.length; ++i) {
2791                     try
2792                     {
2793
2794                         http = new ActiveXObject(this.activeX[i]);
2795
2796                         obj = { conn:http, tId:transactionId };
2797                         break;
2798                     }
2799                     catch(e) {
2800                     }
2801                 }
2802             }
2803             finally
2804             {
2805                 return obj;
2806             }
2807         },
2808
2809         getConnectionObject:function()
2810         {
2811             var o;
2812             var tId = this.transactionId;
2813
2814             try
2815             {
2816                 o = this.createXhrObject(tId);
2817                 if (o) {
2818                     this.transactionId++;
2819                 }
2820             }
2821             catch(e) {
2822             }
2823             finally
2824             {
2825                 return o;
2826             }
2827         },
2828
2829         asyncRequest:function(method, uri, callback, postData)
2830         {
2831             var o = this.getConnectionObject();
2832
2833             if (!o) {
2834                 return null;
2835             }
2836             else {
2837                 o.conn.open(method, uri, true);
2838
2839                 if (this.useDefaultXhrHeader) {
2840                     if (!this.defaultHeaders['X-Requested-With']) {
2841                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2842                     }
2843                 }
2844
2845                 if(postData && this.useDefaultHeader){
2846                     this.initHeader('Content-Type', this.defaultPostHeader);
2847                 }
2848
2849                  if (this.hasDefaultHeaders || this.hasHeaders) {
2850                     this.setHeader(o);
2851                 }
2852
2853                 this.handleReadyState(o, callback);
2854                 o.conn.send(postData || null);
2855
2856                 return o;
2857             }
2858         },
2859
2860         handleReadyState:function(o, callback)
2861         {
2862             var oConn = this;
2863
2864             if (callback && callback.timeout) {
2865                 
2866                 this.timeout[o.tId] = window.setTimeout(function() {
2867                     oConn.abort(o, callback, true);
2868                 }, callback.timeout);
2869             }
2870
2871             this.poll[o.tId] = window.setInterval(
2872                     function() {
2873                         if (o.conn && o.conn.readyState == 4) {
2874                             window.clearInterval(oConn.poll[o.tId]);
2875                             delete oConn.poll[o.tId];
2876
2877                             if(callback && callback.timeout) {
2878                                 window.clearTimeout(oConn.timeout[o.tId]);
2879                                 delete oConn.timeout[o.tId];
2880                             }
2881
2882                             oConn.handleTransactionResponse(o, callback);
2883                         }
2884                     }
2885                     , this.pollInterval);
2886         },
2887
2888         handleTransactionResponse:function(o, callback, isAbort)
2889         {
2890
2891             if (!callback) {
2892                 this.releaseObject(o);
2893                 return;
2894             }
2895
2896             var httpStatus, responseObject;
2897
2898             try
2899             {
2900                 if (o.conn.status !== undefined && o.conn.status != 0) {
2901                     httpStatus = o.conn.status;
2902                 }
2903                 else {
2904                     httpStatus = 13030;
2905                 }
2906             }
2907             catch(e) {
2908
2909
2910                 httpStatus = 13030;
2911             }
2912
2913             if (httpStatus >= 200 && httpStatus < 300) {
2914                 responseObject = this.createResponseObject(o, callback.argument);
2915                 if (callback.success) {
2916                     if (!callback.scope) {
2917                         callback.success(responseObject);
2918                     }
2919                     else {
2920
2921
2922                         callback.success.apply(callback.scope, [responseObject]);
2923                     }
2924                 }
2925             }
2926             else {
2927                 switch (httpStatus) {
2928
2929                     case 12002:
2930                     case 12029:
2931                     case 12030:
2932                     case 12031:
2933                     case 12152:
2934                     case 13030:
2935                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2936                         if (callback.failure) {
2937                             if (!callback.scope) {
2938                                 callback.failure(responseObject);
2939                             }
2940                             else {
2941                                 callback.failure.apply(callback.scope, [responseObject]);
2942                             }
2943                         }
2944                         break;
2945                     default:
2946                         responseObject = this.createResponseObject(o, callback.argument);
2947                         if (callback.failure) {
2948                             if (!callback.scope) {
2949                                 callback.failure(responseObject);
2950                             }
2951                             else {
2952                                 callback.failure.apply(callback.scope, [responseObject]);
2953                             }
2954                         }
2955                 }
2956             }
2957
2958             this.releaseObject(o);
2959             responseObject = null;
2960         },
2961
2962         createResponseObject:function(o, callbackArg)
2963         {
2964             var obj = {};
2965             var headerObj = {};
2966
2967             try
2968             {
2969                 var headerStr = o.conn.getAllResponseHeaders();
2970                 var header = headerStr.split('\n');
2971                 for (var i = 0; i < header.length; i++) {
2972                     var delimitPos = header[i].indexOf(':');
2973                     if (delimitPos != -1) {
2974                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2975                     }
2976                 }
2977             }
2978             catch(e) {
2979             }
2980
2981             obj.tId = o.tId;
2982             obj.status = o.conn.status;
2983             obj.statusText = o.conn.statusText;
2984             obj.getResponseHeader = headerObj;
2985             obj.getAllResponseHeaders = headerStr;
2986             obj.responseText = o.conn.responseText;
2987             obj.responseXML = o.conn.responseXML;
2988
2989             if (typeof callbackArg !== undefined) {
2990                 obj.argument = callbackArg;
2991             }
2992
2993             return obj;
2994         },
2995
2996         createExceptionObject:function(tId, callbackArg, isAbort)
2997         {
2998             var COMM_CODE = 0;
2999             var COMM_ERROR = 'communication failure';
3000             var ABORT_CODE = -1;
3001             var ABORT_ERROR = 'transaction aborted';
3002
3003             var obj = {};
3004
3005             obj.tId = tId;
3006             if (isAbort) {
3007                 obj.status = ABORT_CODE;
3008                 obj.statusText = ABORT_ERROR;
3009             }
3010             else {
3011                 obj.status = COMM_CODE;
3012                 obj.statusText = COMM_ERROR;
3013             }
3014
3015             if (callbackArg) {
3016                 obj.argument = callbackArg;
3017             }
3018
3019             return obj;
3020         },
3021
3022         initHeader:function(label, value, isDefault)
3023         {
3024             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3025
3026             if (headerObj[label] === undefined) {
3027                 headerObj[label] = value;
3028             }
3029             else {
3030
3031
3032                 headerObj[label] = value + "," + headerObj[label];
3033             }
3034
3035             if (isDefault) {
3036                 this.hasDefaultHeaders = true;
3037             }
3038             else {
3039                 this.hasHeaders = true;
3040             }
3041         },
3042
3043
3044         setHeader:function(o)
3045         {
3046             if (this.hasDefaultHeaders) {
3047                 for (var prop in this.defaultHeaders) {
3048                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3049                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3050                     }
3051                 }
3052             }
3053
3054             if (this.hasHeaders) {
3055                 for (var prop in this.headers) {
3056                     if (this.headers.hasOwnProperty(prop)) {
3057                         o.conn.setRequestHeader(prop, this.headers[prop]);
3058                     }
3059                 }
3060                 this.headers = {};
3061                 this.hasHeaders = false;
3062             }
3063         },
3064
3065         resetDefaultHeaders:function() {
3066             delete this.defaultHeaders;
3067             this.defaultHeaders = {};
3068             this.hasDefaultHeaders = false;
3069         },
3070
3071         abort:function(o, callback, isTimeout)
3072         {
3073             if(this.isCallInProgress(o)) {
3074                 o.conn.abort();
3075                 window.clearInterval(this.poll[o.tId]);
3076                 delete this.poll[o.tId];
3077                 if (isTimeout) {
3078                     delete this.timeout[o.tId];
3079                 }
3080
3081                 this.handleTransactionResponse(o, callback, true);
3082
3083                 return true;
3084             }
3085             else {
3086                 return false;
3087             }
3088         },
3089
3090
3091         isCallInProgress:function(o)
3092         {
3093             if (o && o.conn) {
3094                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3095             }
3096             else {
3097
3098                 return false;
3099             }
3100         },
3101
3102
3103         releaseObject:function(o)
3104         {
3105
3106             o.conn = null;
3107
3108             o = null;
3109         },
3110
3111         activeX:[
3112         'MSXML2.XMLHTTP.3.0',
3113         'MSXML2.XMLHTTP',
3114         'Microsoft.XMLHTTP'
3115         ]
3116
3117
3118     };
3119 })();/*
3120  * Portions of this file are based on pieces of Yahoo User Interface Library
3121  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3122  * YUI licensed under the BSD License:
3123  * http://developer.yahoo.net/yui/license.txt
3124  * <script type="text/javascript">
3125  *
3126  */
3127
3128 Roo.lib.Region = function(t, r, b, l) {
3129     this.top = t;
3130     this[1] = t;
3131     this.right = r;
3132     this.bottom = b;
3133     this.left = l;
3134     this[0] = l;
3135 };
3136
3137
3138 Roo.lib.Region.prototype = {
3139     contains : function(region) {
3140         return ( region.left >= this.left &&
3141                  region.right <= this.right &&
3142                  region.top >= this.top &&
3143                  region.bottom <= this.bottom    );
3144
3145     },
3146
3147     getArea : function() {
3148         return ( (this.bottom - this.top) * (this.right - this.left) );
3149     },
3150
3151     intersect : function(region) {
3152         var t = Math.max(this.top, region.top);
3153         var r = Math.min(this.right, region.right);
3154         var b = Math.min(this.bottom, region.bottom);
3155         var l = Math.max(this.left, region.left);
3156
3157         if (b >= t && r >= l) {
3158             return new Roo.lib.Region(t, r, b, l);
3159         } else {
3160             return null;
3161         }
3162     },
3163     union : function(region) {
3164         var t = Math.min(this.top, region.top);
3165         var r = Math.max(this.right, region.right);
3166         var b = Math.max(this.bottom, region.bottom);
3167         var l = Math.min(this.left, region.left);
3168
3169         return new Roo.lib.Region(t, r, b, l);
3170     },
3171
3172     adjust : function(t, l, b, r) {
3173         this.top += t;
3174         this.left += l;
3175         this.right += r;
3176         this.bottom += b;
3177         return this;
3178     }
3179 };
3180
3181 Roo.lib.Region.getRegion = function(el) {
3182     var p = Roo.lib.Dom.getXY(el);
3183
3184     var t = p[1];
3185     var r = p[0] + el.offsetWidth;
3186     var b = p[1] + el.offsetHeight;
3187     var l = p[0];
3188
3189     return new Roo.lib.Region(t, r, b, l);
3190 };
3191 /*
3192  * Portions of this file are based on pieces of Yahoo User Interface Library
3193  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3194  * YUI licensed under the BSD License:
3195  * http://developer.yahoo.net/yui/license.txt
3196  * <script type="text/javascript">
3197  *
3198  */
3199 //@@dep Roo.lib.Region
3200
3201
3202 Roo.lib.Point = function(x, y) {
3203     if (x instanceof Array) {
3204         y = x[1];
3205         x = x[0];
3206     }
3207     this.x = this.right = this.left = this[0] = x;
3208     this.y = this.top = this.bottom = this[1] = y;
3209 };
3210
3211 Roo.lib.Point.prototype = new Roo.lib.Region();
3212 /*
3213  * Portions of this file are based on pieces of Yahoo User Interface Library
3214  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3215  * YUI licensed under the BSD License:
3216  * http://developer.yahoo.net/yui/license.txt
3217  * <script type="text/javascript">
3218  *
3219  */
3220  
3221 (function() {   
3222
3223     Roo.lib.Anim = {
3224         scroll : function(el, args, duration, easing, cb, scope) {
3225             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3226         },
3227
3228         motion : function(el, args, duration, easing, cb, scope) {
3229             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3230         },
3231
3232         color : function(el, args, duration, easing, cb, scope) {
3233             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3234         },
3235
3236         run : function(el, args, duration, easing, cb, scope, type) {
3237             type = type || Roo.lib.AnimBase;
3238             if (typeof easing == "string") {
3239                 easing = Roo.lib.Easing[easing];
3240             }
3241             var anim = new type(el, args, duration, easing);
3242             anim.animateX(function() {
3243                 Roo.callback(cb, scope);
3244             });
3245             return anim;
3246         }
3247     };
3248 })();/*
3249  * Portions of this file are based on pieces of Yahoo User Interface Library
3250  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3251  * YUI licensed under the BSD License:
3252  * http://developer.yahoo.net/yui/license.txt
3253  * <script type="text/javascript">
3254  *
3255  */
3256
3257 (function() {    
3258     var libFlyweight;
3259     
3260     function fly(el) {
3261         if (!libFlyweight) {
3262             libFlyweight = new Roo.Element.Flyweight();
3263         }
3264         libFlyweight.dom = el;
3265         return libFlyweight;
3266     }
3267
3268     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3269     
3270    
3271     
3272     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3273         if (el) {
3274             this.init(el, attributes, duration, method);
3275         }
3276     };
3277
3278     Roo.lib.AnimBase.fly = fly;
3279     
3280     
3281     
3282     Roo.lib.AnimBase.prototype = {
3283
3284         toString: function() {
3285             var el = this.getEl();
3286             var id = el.id || el.tagName;
3287             return ("Anim " + id);
3288         },
3289
3290         patterns: {
3291             noNegatives:        /width|height|opacity|padding/i,
3292             offsetAttribute:  /^((width|height)|(top|left))$/,
3293             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3294             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3295         },
3296
3297
3298         doMethod: function(attr, start, end) {
3299             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3300         },
3301
3302
3303         setAttribute: function(attr, val, unit) {
3304             if (this.patterns.noNegatives.test(attr)) {
3305                 val = (val > 0) ? val : 0;
3306             }
3307
3308             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3309         },
3310
3311
3312         getAttribute: function(attr) {
3313             var el = this.getEl();
3314             var val = fly(el).getStyle(attr);
3315
3316             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3317                 return parseFloat(val);
3318             }
3319
3320             var a = this.patterns.offsetAttribute.exec(attr) || [];
3321             var pos = !!( a[3] );
3322             var box = !!( a[2] );
3323
3324
3325             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3326                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3327             } else {
3328                 val = 0;
3329             }
3330
3331             return val;
3332         },
3333
3334
3335         getDefaultUnit: function(attr) {
3336             if (this.patterns.defaultUnit.test(attr)) {
3337                 return 'px';
3338             }
3339
3340             return '';
3341         },
3342
3343         animateX : function(callback, scope) {
3344             var f = function() {
3345                 this.onComplete.removeListener(f);
3346                 if (typeof callback == "function") {
3347                     callback.call(scope || this, this);
3348                 }
3349             };
3350             this.onComplete.addListener(f, this);
3351             this.animate();
3352         },
3353
3354
3355         setRuntimeAttribute: function(attr) {
3356             var start;
3357             var end;
3358             var attributes = this.attributes;
3359
3360             this.runtimeAttributes[attr] = {};
3361
3362             var isset = function(prop) {
3363                 return (typeof prop !== 'undefined');
3364             };
3365
3366             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3367                 return false;
3368             }
3369
3370             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3371
3372
3373             if (isset(attributes[attr]['to'])) {
3374                 end = attributes[attr]['to'];
3375             } else if (isset(attributes[attr]['by'])) {
3376                 if (start.constructor == Array) {
3377                     end = [];
3378                     for (var i = 0, len = start.length; i < len; ++i) {
3379                         end[i] = start[i] + attributes[attr]['by'][i];
3380                     }
3381                 } else {
3382                     end = start + attributes[attr]['by'];
3383                 }
3384             }
3385
3386             this.runtimeAttributes[attr].start = start;
3387             this.runtimeAttributes[attr].end = end;
3388
3389
3390             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3391         },
3392
3393
3394         init: function(el, attributes, duration, method) {
3395
3396             var isAnimated = false;
3397
3398
3399             var startTime = null;
3400
3401
3402             var actualFrames = 0;
3403
3404
3405             el = Roo.getDom(el);
3406
3407
3408             this.attributes = attributes || {};
3409
3410
3411             this.duration = duration || 1;
3412
3413
3414             this.method = method || Roo.lib.Easing.easeNone;
3415
3416
3417             this.useSeconds = true;
3418
3419
3420             this.currentFrame = 0;
3421
3422
3423             this.totalFrames = Roo.lib.AnimMgr.fps;
3424
3425
3426             this.getEl = function() {
3427                 return el;
3428             };
3429
3430
3431             this.isAnimated = function() {
3432                 return isAnimated;
3433             };
3434
3435
3436             this.getStartTime = function() {
3437                 return startTime;
3438             };
3439
3440             this.runtimeAttributes = {};
3441
3442
3443             this.animate = function() {
3444                 if (this.isAnimated()) {
3445                     return false;
3446                 }
3447
3448                 this.currentFrame = 0;
3449
3450                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3451
3452                 Roo.lib.AnimMgr.registerElement(this);
3453             };
3454
3455
3456             this.stop = function(finish) {
3457                 if (finish) {
3458                     this.currentFrame = this.totalFrames;
3459                     this._onTween.fire();
3460                 }
3461                 Roo.lib.AnimMgr.stop(this);
3462             };
3463
3464             var onStart = function() {
3465                 this.onStart.fire();
3466
3467                 this.runtimeAttributes = {};
3468                 for (var attr in this.attributes) {
3469                     this.setRuntimeAttribute(attr);
3470                 }
3471
3472                 isAnimated = true;
3473                 actualFrames = 0;
3474                 startTime = new Date();
3475             };
3476
3477
3478             var onTween = function() {
3479                 var data = {
3480                     duration: new Date() - this.getStartTime(),
3481                     currentFrame: this.currentFrame
3482                 };
3483
3484                 data.toString = function() {
3485                     return (
3486                             'duration: ' + data.duration +
3487                             ', currentFrame: ' + data.currentFrame
3488                             );
3489                 };
3490
3491                 this.onTween.fire(data);
3492
3493                 var runtimeAttributes = this.runtimeAttributes;
3494
3495                 for (var attr in runtimeAttributes) {
3496                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3497                 }
3498
3499                 actualFrames += 1;
3500             };
3501
3502             var onComplete = function() {
3503                 var actual_duration = (new Date() - startTime) / 1000 ;
3504
3505                 var data = {
3506                     duration: actual_duration,
3507                     frames: actualFrames,
3508                     fps: actualFrames / actual_duration
3509                 };
3510
3511                 data.toString = function() {
3512                     return (
3513                             'duration: ' + data.duration +
3514                             ', frames: ' + data.frames +
3515                             ', fps: ' + data.fps
3516                             );
3517                 };
3518
3519                 isAnimated = false;
3520                 actualFrames = 0;
3521                 this.onComplete.fire(data);
3522             };
3523
3524
3525             this._onStart = new Roo.util.Event(this);
3526             this.onStart = new Roo.util.Event(this);
3527             this.onTween = new Roo.util.Event(this);
3528             this._onTween = new Roo.util.Event(this);
3529             this.onComplete = new Roo.util.Event(this);
3530             this._onComplete = new Roo.util.Event(this);
3531             this._onStart.addListener(onStart);
3532             this._onTween.addListener(onTween);
3533             this._onComplete.addListener(onComplete);
3534         }
3535     };
3536 })();
3537 /*
3538  * Portions of this file are based on pieces of Yahoo User Interface Library
3539  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3540  * YUI licensed under the BSD License:
3541  * http://developer.yahoo.net/yui/license.txt
3542  * <script type="text/javascript">
3543  *
3544  */
3545
3546 Roo.lib.AnimMgr = new function() {
3547
3548     var thread = null;
3549
3550
3551     var queue = [];
3552
3553
3554     var tweenCount = 0;
3555
3556
3557     this.fps = 1000;
3558
3559
3560     this.delay = 1;
3561
3562
3563     this.registerElement = function(tween) {
3564         queue[queue.length] = tween;
3565         tweenCount += 1;
3566         tween._onStart.fire();
3567         this.start();
3568     };
3569
3570
3571     this.unRegister = function(tween, index) {
3572         tween._onComplete.fire();
3573         index = index || getIndex(tween);
3574         if (index != -1) {
3575             queue.splice(index, 1);
3576         }
3577
3578         tweenCount -= 1;
3579         if (tweenCount <= 0) {
3580             this.stop();
3581         }
3582     };
3583
3584
3585     this.start = function() {
3586         if (thread === null) {
3587             thread = setInterval(this.run, this.delay);
3588         }
3589     };
3590
3591
3592     this.stop = function(tween) {
3593         if (!tween) {
3594             clearInterval(thread);
3595
3596             for (var i = 0, len = queue.length; i < len; ++i) {
3597                 if (queue[0].isAnimated()) {
3598                     this.unRegister(queue[0], 0);
3599                 }
3600             }
3601
3602             queue = [];
3603             thread = null;
3604             tweenCount = 0;
3605         }
3606         else {
3607             this.unRegister(tween);
3608         }
3609     };
3610
3611
3612     this.run = function() {
3613         for (var i = 0, len = queue.length; i < len; ++i) {
3614             var tween = queue[i];
3615             if (!tween || !tween.isAnimated()) {
3616                 continue;
3617             }
3618
3619             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3620             {
3621                 tween.currentFrame += 1;
3622
3623                 if (tween.useSeconds) {
3624                     correctFrame(tween);
3625                 }
3626                 tween._onTween.fire();
3627             }
3628             else {
3629                 Roo.lib.AnimMgr.stop(tween, i);
3630             }
3631         }
3632     };
3633
3634     var getIndex = function(anim) {
3635         for (var i = 0, len = queue.length; i < len; ++i) {
3636             if (queue[i] == anim) {
3637                 return i;
3638             }
3639         }
3640         return -1;
3641     };
3642
3643
3644     var correctFrame = function(tween) {
3645         var frames = tween.totalFrames;
3646         var frame = tween.currentFrame;
3647         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3648         var elapsed = (new Date() - tween.getStartTime());
3649         var tweak = 0;
3650
3651         if (elapsed < tween.duration * 1000) {
3652             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3653         } else {
3654             tweak = frames - (frame + 1);
3655         }
3656         if (tweak > 0 && isFinite(tweak)) {
3657             if (tween.currentFrame + tweak >= frames) {
3658                 tweak = frames - (frame + 1);
3659             }
3660
3661             tween.currentFrame += tweak;
3662         }
3663     };
3664 };
3665
3666     /*
3667  * Portions of this file are based on pieces of Yahoo User Interface Library
3668  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3669  * YUI licensed under the BSD License:
3670  * http://developer.yahoo.net/yui/license.txt
3671  * <script type="text/javascript">
3672  *
3673  */
3674 Roo.lib.Bezier = new function() {
3675
3676         this.getPosition = function(points, t) {
3677             var n = points.length;
3678             var tmp = [];
3679
3680             for (var i = 0; i < n; ++i) {
3681                 tmp[i] = [points[i][0], points[i][1]];
3682             }
3683
3684             for (var j = 1; j < n; ++j) {
3685                 for (i = 0; i < n - j; ++i) {
3686                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3687                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3688                 }
3689             }
3690
3691             return [ tmp[0][0], tmp[0][1] ];
3692
3693         };
3694     }; 
3695
3696 /**
3697  * @class Roo.lib.Color
3698  * @constructor
3699  * An abstract Color implementation. Concrete Color implementations should use
3700  * an instance of this function as their prototype, and implement the getRGB and
3701  * getHSL functions. getRGB should return an object representing the RGB
3702  * components of this Color, with the red, green, and blue components in the
3703  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3704  * return an object representing the HSL components of this Color, with the hue
3705  * component in the range [0,360), the saturation and lightness components in
3706  * the range [0,100], and the alpha component in the range [0,1].
3707  *
3708  *
3709  * Color.js
3710  *
3711  * Functions for Color handling and processing.
3712  *
3713  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3714  *
3715  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3716  * rights to this program, with the intention of it becoming part of the public
3717  * domain. Because this program is released into the public domain, it comes with
3718  * no warranty either expressed or implied, to the extent permitted by law.
3719  * 
3720  * For more free and public domain JavaScript code by the same author, visit:
3721  * http://www.safalra.com/web-design/javascript/
3722  * 
3723  */
3724 Roo.lib.Color = function() { }
3725
3726
3727 Roo.apply(Roo.lib.Color.prototype, {
3728   
3729   rgb : null,
3730   hsv : null,
3731   hsl : null,
3732   
3733   /**
3734    * getIntegerRGB
3735    * @return {Object} an object representing the RGBA components of this Color. The red,
3736    * green, and blue components are converted to integers in the range [0,255].
3737    * The alpha is a value in the range [0,1].
3738    */
3739   getIntegerRGB : function(){
3740
3741     // get the RGB components of this Color
3742     var rgb = this.getRGB();
3743
3744     // return the integer components
3745     return {
3746       'r' : Math.round(rgb.r),
3747       'g' : Math.round(rgb.g),
3748       'b' : Math.round(rgb.b),
3749       'a' : rgb.a
3750     };
3751
3752   },
3753
3754   /**
3755    * getPercentageRGB
3756    * @return {Object} an object representing the RGBA components of this Color. The red,
3757    * green, and blue components are converted to numbers in the range [0,100].
3758    * The alpha is a value in the range [0,1].
3759    */
3760   getPercentageRGB : function(){
3761
3762     // get the RGB components of this Color
3763     var rgb = this.getRGB();
3764
3765     // return the percentage components
3766     return {
3767       'r' : 100 * rgb.r / 255,
3768       'g' : 100 * rgb.g / 255,
3769       'b' : 100 * rgb.b / 255,
3770       'a' : rgb.a
3771     };
3772
3773   },
3774
3775   /**
3776    * getCSSHexadecimalRGB
3777    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3778    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3779    * are two-digit hexadecimal numbers.
3780    */
3781   getCSSHexadecimalRGB : function()
3782   {
3783
3784     // get the integer RGB components
3785     var rgb = this.getIntegerRGB();
3786
3787     // determine the hexadecimal equivalents
3788     var r16 = rgb.r.toString(16);
3789     var g16 = rgb.g.toString(16);
3790     var b16 = rgb.b.toString(16);
3791
3792     // return the CSS RGB Color value
3793     return '#'
3794         + (r16.length == 2 ? r16 : '0' + r16)
3795         + (g16.length == 2 ? g16 : '0' + g16)
3796         + (b16.length == 2 ? b16 : '0' + b16);
3797
3798   },
3799
3800   /**
3801    * getCSSIntegerRGB
3802    * @return {String} a string representing this Color as a CSS integer RGB Color
3803    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3804    * are integers in the range [0,255].
3805    */
3806   getCSSIntegerRGB : function(){
3807
3808     // get the integer RGB components
3809     var rgb = this.getIntegerRGB();
3810
3811     // return the CSS RGB Color value
3812     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3813
3814   },
3815
3816   /**
3817    * getCSSIntegerRGBA
3818    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3819    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3820    * b are integers in the range [0,255] and a is in the range [0,1].
3821    */
3822   getCSSIntegerRGBA : function(){
3823
3824     // get the integer RGB components
3825     var rgb = this.getIntegerRGB();
3826
3827     // return the CSS integer RGBA Color value
3828     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3829
3830   },
3831
3832   /**
3833    * getCSSPercentageRGB
3834    * @return {String} a string representing this Color as a CSS percentage RGB Color
3835    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3836    * b are in the range [0,100].
3837    */
3838   getCSSPercentageRGB : function(){
3839
3840     // get the percentage RGB components
3841     var rgb = this.getPercentageRGB();
3842
3843     // return the CSS RGB Color value
3844     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3845
3846   },
3847
3848   /**
3849    * getCSSPercentageRGBA
3850    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3851    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3852    * and b are in the range [0,100] and a is in the range [0,1].
3853    */
3854   getCSSPercentageRGBA : function(){
3855
3856     // get the percentage RGB components
3857     var rgb = this.getPercentageRGB();
3858
3859     // return the CSS percentage RGBA Color value
3860     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3861
3862   },
3863
3864   /**
3865    * getCSSHSL
3866    * @return {String} a string representing this Color as a CSS HSL Color value - that
3867    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3868    * s and l are in the range [0,100].
3869    */
3870   getCSSHSL : function(){
3871
3872     // get the HSL components
3873     var hsl = this.getHSL();
3874
3875     // return the CSS HSL Color value
3876     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3877
3878   },
3879
3880   /**
3881    * getCSSHSLA
3882    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3883    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3884    * s and l are in the range [0,100], and a is in the range [0,1].
3885    */
3886   getCSSHSLA : function(){
3887
3888     // get the HSL components
3889     var hsl = this.getHSL();
3890
3891     // return the CSS HSL Color value
3892     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3893
3894   },
3895
3896   /**
3897    * Sets the Color of the specified node to this Color. This functions sets
3898    * the CSS 'color' property for the node. The parameter is:
3899    * 
3900    * @param {DomElement} node - the node whose Color should be set
3901    */
3902   setNodeColor : function(node){
3903
3904     // set the Color of the node
3905     node.style.color = this.getCSSHexadecimalRGB();
3906
3907   },
3908
3909   /**
3910    * Sets the background Color of the specified node to this Color. This
3911    * functions sets the CSS 'background-color' property for the node. The
3912    * parameter is:
3913    *
3914    * @param {DomElement} node - the node whose background Color should be set
3915    */
3916   setNodeBackgroundColor : function(node){
3917
3918     // set the background Color of the node
3919     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3920
3921   },
3922   // convert between formats..
3923   toRGB: function()
3924   {
3925     var r = this.getIntegerRGB();
3926     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3927     
3928   },
3929   toHSL : function()
3930   {
3931      var hsl = this.getHSL();
3932   // return the CSS HSL Color value
3933     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3934     
3935   },
3936   
3937   toHSV : function()
3938   {
3939     var rgb = this.toRGB();
3940     var hsv = rgb.getHSV();
3941    // return the CSS HSL Color value
3942     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3943     
3944   },
3945   
3946   // modify  v = 0 ... 1 (eg. 0.5)
3947   saturate : function(v)
3948   {
3949       var rgb = this.toRGB();
3950       var hsv = rgb.getHSV();
3951       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3952       
3953   
3954   },
3955   
3956    
3957   /**
3958    * getRGB
3959    * @return {Object} the RGB and alpha components of this Color as an object with r,
3960    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3961    * the range [0,1].
3962    */
3963   getRGB: function(){
3964    
3965     // return the RGB components
3966     return {
3967       'r' : this.rgb.r,
3968       'g' : this.rgb.g,
3969       'b' : this.rgb.b,
3970       'a' : this.alpha
3971     };
3972
3973   },
3974
3975   /**
3976    * getHSV
3977    * @return {Object} the HSV and alpha components of this Color as an object with h,
3978    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3979    * [0,100], and a is in the range [0,1].
3980    */
3981   getHSV : function()
3982   {
3983     
3984     // calculate the HSV components if necessary
3985     if (this.hsv == null) {
3986       this.calculateHSV();
3987     }
3988
3989     // return the HSV components
3990     return {
3991       'h' : this.hsv.h,
3992       's' : this.hsv.s,
3993       'v' : this.hsv.v,
3994       'a' : this.alpha
3995     };
3996
3997   },
3998
3999   /**
4000    * getHSL
4001    * @return {Object} the HSL and alpha components of this Color as an object with h,
4002    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4003    * [0,100], and a is in the range [0,1].
4004    */
4005   getHSL : function(){
4006     
4007      
4008     // calculate the HSV components if necessary
4009     if (this.hsl == null) { this.calculateHSL(); }
4010
4011     // return the HSL components
4012     return {
4013       'h' : this.hsl.h,
4014       's' : this.hsl.s,
4015       'l' : this.hsl.l,
4016       'a' : this.alpha
4017     };
4018
4019   }
4020   
4021
4022 });
4023
4024
4025 /**
4026  * @class Roo.lib.RGBColor
4027  * @extends Roo.lib.Color
4028  * Creates a Color specified in the RGB Color space, with an optional alpha
4029  * component. The parameters are:
4030  * @constructor
4031  * 
4032
4033  * @param {Number} r - the red component, clipped to the range [0,255]
4034  * @param {Number} g - the green component, clipped to the range [0,255]
4035  * @param {Number} b - the blue component, clipped to the range [0,255]
4036  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4037  *     optional and defaults to 1
4038  */
4039 Roo.lib.RGBColor = function (r, g, b, a){
4040
4041   // store the alpha component after clipping it if necessary
4042   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4043
4044   // store the RGB components after clipping them if necessary
4045   this.rgb =
4046       {
4047         'r' : Math.max(0, Math.min(255, r)),
4048         'g' : Math.max(0, Math.min(255, g)),
4049         'b' : Math.max(0, Math.min(255, b))
4050       };
4051
4052   // initialise the HSV and HSL components to null
4053   
4054
4055   /* 
4056    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4057    * range [0,360). The parameters are:
4058    *
4059    * maximum - the maximum of the RGB component values
4060    * range   - the range of the RGB component values
4061    */
4062    
4063
4064 }
4065 // this does an 'exteds'
4066 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4067
4068   
4069     getHue  : function(maximum, range)
4070     {
4071       var rgb = this.rgb;
4072        
4073       // check whether the range is zero
4074       if (range == 0){
4075   
4076         // set the hue to zero (any hue is acceptable as the Color is grey)
4077         var hue = 0;
4078   
4079       }else{
4080   
4081         // determine which of the components has the highest value and set the hue
4082         switch (maximum){
4083   
4084           // red has the highest value
4085           case rgb.r:
4086             var hue = (rgb.g - rgb.b) / range * 60;
4087             if (hue < 0) { hue += 360; }
4088             break;
4089   
4090           // green has the highest value
4091           case rgb.g:
4092             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4093             break;
4094   
4095           // blue has the highest value
4096           case rgb.b:
4097             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4098             break;
4099   
4100         }
4101   
4102       }
4103   
4104       // return the hue
4105       return hue;
4106   
4107     },
4108
4109   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4110    * be returned be the getHSV function.
4111    */
4112    calculateHSV : function(){
4113     var rgb = this.rgb;
4114     // get the maximum and range of the RGB component values
4115     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4116     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4117
4118     // store the HSV components
4119     this.hsv =
4120         {
4121           'h' : this.getHue(maximum, range),
4122           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4123           'v' : maximum / 2.55
4124         };
4125
4126   },
4127
4128   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4129    * be returned be the getHSL function.
4130    */
4131    calculateHSL : function(){
4132     var rgb = this.rgb;
4133     // get the maximum and range of the RGB component values
4134     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4135     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4136
4137     // determine the lightness in the range [0,1]
4138     var l = maximum / 255 - range / 510;
4139
4140     // store the HSL components
4141     this.hsl =
4142         {
4143           'h' : this.getHue(maximum, range),
4144           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4145           'l' : 100 * l
4146         };
4147
4148   }
4149
4150 });
4151
4152 /**
4153  * @class Roo.lib.HSVColor
4154  * @extends Roo.lib.Color
4155  * Creates a Color specified in the HSV Color space, with an optional alpha
4156  * component. The parameters are:
4157  * @constructor
4158  *
4159  * @param {Number} h - the hue component, wrapped to the range [0,360)
4160  * @param {Number} s - the saturation component, clipped to the range [0,100]
4161  * @param {Number} v - the value component, clipped to the range [0,100]
4162  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4163  *     optional and defaults to 1
4164  */
4165 Roo.lib.HSVColor = function (h, s, v, a){
4166
4167   // store the alpha component after clipping it if necessary
4168   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4169
4170   // store the HSV components after clipping or wrapping them if necessary
4171   this.hsv =
4172       {
4173         'h' : (h % 360 + 360) % 360,
4174         's' : Math.max(0, Math.min(100, s)),
4175         'v' : Math.max(0, Math.min(100, v))
4176       };
4177
4178   // initialise the RGB and HSL components to null
4179   this.rgb = null;
4180   this.hsl = null;
4181 }
4182
4183 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4184   /* Calculates and stores the RGB components of this HSVColor so that they can
4185    * be returned be the getRGB function.
4186    */
4187   calculateRGB: function ()
4188   {
4189     var hsv = this.hsv;
4190     // check whether the saturation is zero
4191     if (hsv.s == 0){
4192
4193       // set the Color to the appropriate shade of grey
4194       var r = hsv.v;
4195       var g = hsv.v;
4196       var b = hsv.v;
4197
4198     }else{
4199
4200       // set some temporary values
4201       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4202       var p  = hsv.v * (1 - hsv.s / 100);
4203       var q  = hsv.v * (1 - hsv.s / 100 * f);
4204       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4205
4206       // set the RGB Color components to their temporary values
4207       switch (Math.floor(hsv.h / 60)){
4208         case 0: var r = hsv.v; var g = t; var b = p; break;
4209         case 1: var r = q; var g = hsv.v; var b = p; break;
4210         case 2: var r = p; var g = hsv.v; var b = t; break;
4211         case 3: var r = p; var g = q; var b = hsv.v; break;
4212         case 4: var r = t; var g = p; var b = hsv.v; break;
4213         case 5: var r = hsv.v; var g = p; var b = q; break;
4214       }
4215
4216     }
4217
4218     // store the RGB components
4219     this.rgb =
4220         {
4221           'r' : r * 2.55,
4222           'g' : g * 2.55,
4223           'b' : b * 2.55
4224         };
4225
4226   },
4227
4228   /* Calculates and stores the HSL components of this HSVColor so that they can
4229    * be returned be the getHSL function.
4230    */
4231   calculateHSL : function (){
4232
4233     var hsv = this.hsv;
4234     // determine the lightness in the range [0,100]
4235     var l = (2 - hsv.s / 100) * hsv.v / 2;
4236
4237     // store the HSL components
4238     this.hsl =
4239         {
4240           'h' : hsv.h,
4241           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4242           'l' : l
4243         };
4244
4245     // correct a division-by-zero error
4246     if (isNaN(hsl.s)) { hsl.s = 0; }
4247
4248   } 
4249  
4250
4251 });
4252  
4253
4254 /**
4255  * @class Roo.lib.HSLColor
4256  * @extends Roo.lib.Color
4257  *
4258  * @constructor
4259  * Creates a Color specified in the HSL Color space, with an optional alpha
4260  * component. The parameters are:
4261  *
4262  * @param {Number} h - the hue component, wrapped to the range [0,360)
4263  * @param {Number} s - the saturation component, clipped to the range [0,100]
4264  * @param {Number} l - the lightness component, clipped to the range [0,100]
4265  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4266  *     optional and defaults to 1
4267  */
4268
4269 Roo.lib.HSLColor = function(h, s, l, a){
4270
4271   // store the alpha component after clipping it if necessary
4272   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4273
4274   // store the HSL components after clipping or wrapping them if necessary
4275   this.hsl =
4276       {
4277         'h' : (h % 360 + 360) % 360,
4278         's' : Math.max(0, Math.min(100, s)),
4279         'l' : Math.max(0, Math.min(100, l))
4280       };
4281
4282   // initialise the RGB and HSV components to null
4283 }
4284
4285 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4286
4287   /* Calculates and stores the RGB components of this HSLColor so that they can
4288    * be returned be the getRGB function.
4289    */
4290   calculateRGB: function (){
4291
4292     // check whether the saturation is zero
4293     if (this.hsl.s == 0){
4294
4295       // store the RGB components representing the appropriate shade of grey
4296       this.rgb =
4297           {
4298             'r' : this.hsl.l * 2.55,
4299             'g' : this.hsl.l * 2.55,
4300             'b' : this.hsl.l * 2.55
4301           };
4302
4303     }else{
4304
4305       // set some temporary values
4306       var p = this.hsl.l < 50
4307             ? this.hsl.l * (1 + hsl.s / 100)
4308             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4309       var q = 2 * hsl.l - p;
4310
4311       // initialise the RGB components
4312       this.rgb =
4313           {
4314             'r' : (h + 120) / 60 % 6,
4315             'g' : h / 60,
4316             'b' : (h + 240) / 60 % 6
4317           };
4318
4319       // loop over the RGB components
4320       for (var key in this.rgb){
4321
4322         // ensure that the property is not inherited from the root object
4323         if (this.rgb.hasOwnProperty(key)){
4324
4325           // set the component to its value in the range [0,100]
4326           if (this.rgb[key] < 1){
4327             this.rgb[key] = q + (p - q) * this.rgb[key];
4328           }else if (this.rgb[key] < 3){
4329             this.rgb[key] = p;
4330           }else if (this.rgb[key] < 4){
4331             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4332           }else{
4333             this.rgb[key] = q;
4334           }
4335
4336           // set the component to its value in the range [0,255]
4337           this.rgb[key] *= 2.55;
4338
4339         }
4340
4341       }
4342
4343     }
4344
4345   },
4346
4347   /* Calculates and stores the HSV components of this HSLColor so that they can
4348    * be returned be the getHSL function.
4349    */
4350    calculateHSV : function(){
4351
4352     // set a temporary value
4353     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4354
4355     // store the HSV components
4356     this.hsv =
4357         {
4358           'h' : this.hsl.h,
4359           's' : 200 * t / (this.hsl.l + t),
4360           'v' : t + this.hsl.l
4361         };
4362
4363     // correct a division-by-zero error
4364     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4365
4366   }
4367  
4368
4369 });
4370 /*
4371  * Portions of this file are based on pieces of Yahoo User Interface Library
4372  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4373  * YUI licensed under the BSD License:
4374  * http://developer.yahoo.net/yui/license.txt
4375  * <script type="text/javascript">
4376  *
4377  */
4378 (function() {
4379
4380     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4381         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4382     };
4383
4384     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4385
4386     var fly = Roo.lib.AnimBase.fly;
4387     var Y = Roo.lib;
4388     var superclass = Y.ColorAnim.superclass;
4389     var proto = Y.ColorAnim.prototype;
4390
4391     proto.toString = function() {
4392         var el = this.getEl();
4393         var id = el.id || el.tagName;
4394         return ("ColorAnim " + id);
4395     };
4396
4397     proto.patterns.color = /color$/i;
4398     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4399     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4400     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4401     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4402
4403
4404     proto.parseColor = function(s) {
4405         if (s.length == 3) {
4406             return s;
4407         }
4408
4409         var c = this.patterns.hex.exec(s);
4410         if (c && c.length == 4) {
4411             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4412         }
4413
4414         c = this.patterns.rgb.exec(s);
4415         if (c && c.length == 4) {
4416             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4417         }
4418
4419         c = this.patterns.hex3.exec(s);
4420         if (c && c.length == 4) {
4421             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4422         }
4423
4424         return null;
4425     };
4426     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4427     proto.getAttribute = function(attr) {
4428         var el = this.getEl();
4429         if (this.patterns.color.test(attr)) {
4430             var val = fly(el).getStyle(attr);
4431
4432             if (this.patterns.transparent.test(val)) {
4433                 var parent = el.parentNode;
4434                 val = fly(parent).getStyle(attr);
4435
4436                 while (parent && this.patterns.transparent.test(val)) {
4437                     parent = parent.parentNode;
4438                     val = fly(parent).getStyle(attr);
4439                     if (parent.tagName.toUpperCase() == 'HTML') {
4440                         val = '#fff';
4441                     }
4442                 }
4443             }
4444         } else {
4445             val = superclass.getAttribute.call(this, attr);
4446         }
4447
4448         return val;
4449     };
4450     proto.getAttribute = function(attr) {
4451         var el = this.getEl();
4452         if (this.patterns.color.test(attr)) {
4453             var val = fly(el).getStyle(attr);
4454
4455             if (this.patterns.transparent.test(val)) {
4456                 var parent = el.parentNode;
4457                 val = fly(parent).getStyle(attr);
4458
4459                 while (parent && this.patterns.transparent.test(val)) {
4460                     parent = parent.parentNode;
4461                     val = fly(parent).getStyle(attr);
4462                     if (parent.tagName.toUpperCase() == 'HTML') {
4463                         val = '#fff';
4464                     }
4465                 }
4466             }
4467         } else {
4468             val = superclass.getAttribute.call(this, attr);
4469         }
4470
4471         return val;
4472     };
4473
4474     proto.doMethod = function(attr, start, end) {
4475         var val;
4476
4477         if (this.patterns.color.test(attr)) {
4478             val = [];
4479             for (var i = 0, len = start.length; i < len; ++i) {
4480                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4481             }
4482
4483             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4484         }
4485         else {
4486             val = superclass.doMethod.call(this, attr, start, end);
4487         }
4488
4489         return val;
4490     };
4491
4492     proto.setRuntimeAttribute = function(attr) {
4493         superclass.setRuntimeAttribute.call(this, attr);
4494
4495         if (this.patterns.color.test(attr)) {
4496             var attributes = this.attributes;
4497             var start = this.parseColor(this.runtimeAttributes[attr].start);
4498             var end = this.parseColor(this.runtimeAttributes[attr].end);
4499
4500             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4501                 end = this.parseColor(attributes[attr].by);
4502
4503                 for (var i = 0, len = start.length; i < len; ++i) {
4504                     end[i] = start[i] + end[i];
4505                 }
4506             }
4507
4508             this.runtimeAttributes[attr].start = start;
4509             this.runtimeAttributes[attr].end = end;
4510         }
4511     };
4512 })();
4513
4514 /*
4515  * Portions of this file are based on pieces of Yahoo User Interface Library
4516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4517  * YUI licensed under the BSD License:
4518  * http://developer.yahoo.net/yui/license.txt
4519  * <script type="text/javascript">
4520  *
4521  */
4522 Roo.lib.Easing = {
4523
4524
4525     easeNone: function (t, b, c, d) {
4526         return c * t / d + b;
4527     },
4528
4529
4530     easeIn: function (t, b, c, d) {
4531         return c * (t /= d) * t + b;
4532     },
4533
4534
4535     easeOut: function (t, b, c, d) {
4536         return -c * (t /= d) * (t - 2) + b;
4537     },
4538
4539
4540     easeBoth: function (t, b, c, d) {
4541         if ((t /= d / 2) < 1) {
4542             return c / 2 * t * t + b;
4543         }
4544
4545         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4546     },
4547
4548
4549     easeInStrong: function (t, b, c, d) {
4550         return c * (t /= d) * t * t * t + b;
4551     },
4552
4553
4554     easeOutStrong: function (t, b, c, d) {
4555         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4556     },
4557
4558
4559     easeBothStrong: function (t, b, c, d) {
4560         if ((t /= d / 2) < 1) {
4561             return c / 2 * t * t * t * t + b;
4562         }
4563
4564         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4565     },
4566
4567
4568
4569     elasticIn: function (t, b, c, d, a, p) {
4570         if (t == 0) {
4571             return b;
4572         }
4573         if ((t /= d) == 1) {
4574             return b + c;
4575         }
4576         if (!p) {
4577             p = d * .3;
4578         }
4579
4580         if (!a || a < Math.abs(c)) {
4581             a = c;
4582             var s = p / 4;
4583         }
4584         else {
4585             var s = p / (2 * Math.PI) * Math.asin(c / a);
4586         }
4587
4588         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4589     },
4590
4591
4592     elasticOut: function (t, b, c, d, a, p) {
4593         if (t == 0) {
4594             return b;
4595         }
4596         if ((t /= d) == 1) {
4597             return b + c;
4598         }
4599         if (!p) {
4600             p = d * .3;
4601         }
4602
4603         if (!a || a < Math.abs(c)) {
4604             a = c;
4605             var s = p / 4;
4606         }
4607         else {
4608             var s = p / (2 * Math.PI) * Math.asin(c / a);
4609         }
4610
4611         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4612     },
4613
4614
4615     elasticBoth: function (t, b, c, d, a, p) {
4616         if (t == 0) {
4617             return b;
4618         }
4619
4620         if ((t /= d / 2) == 2) {
4621             return b + c;
4622         }
4623
4624         if (!p) {
4625             p = d * (.3 * 1.5);
4626         }
4627
4628         if (!a || a < Math.abs(c)) {
4629             a = c;
4630             var s = p / 4;
4631         }
4632         else {
4633             var s = p / (2 * Math.PI) * Math.asin(c / a);
4634         }
4635
4636         if (t < 1) {
4637             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4638                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4639         }
4640         return a * Math.pow(2, -10 * (t -= 1)) *
4641                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4642     },
4643
4644
4645
4646     backIn: function (t, b, c, d, s) {
4647         if (typeof s == 'undefined') {
4648             s = 1.70158;
4649         }
4650         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4651     },
4652
4653
4654     backOut: function (t, b, c, d, s) {
4655         if (typeof s == 'undefined') {
4656             s = 1.70158;
4657         }
4658         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4659     },
4660
4661
4662     backBoth: function (t, b, c, d, s) {
4663         if (typeof s == 'undefined') {
4664             s = 1.70158;
4665         }
4666
4667         if ((t /= d / 2 ) < 1) {
4668             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4669         }
4670         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4671     },
4672
4673
4674     bounceIn: function (t, b, c, d) {
4675         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4676     },
4677
4678
4679     bounceOut: function (t, b, c, d) {
4680         if ((t /= d) < (1 / 2.75)) {
4681             return c * (7.5625 * t * t) + b;
4682         } else if (t < (2 / 2.75)) {
4683             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4684         } else if (t < (2.5 / 2.75)) {
4685             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4686         }
4687         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4688     },
4689
4690
4691     bounceBoth: function (t, b, c, d) {
4692         if (t < d / 2) {
4693             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4694         }
4695         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4696     }
4697 };/*
4698  * Portions of this file are based on pieces of Yahoo User Interface Library
4699  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4700  * YUI licensed under the BSD License:
4701  * http://developer.yahoo.net/yui/license.txt
4702  * <script type="text/javascript">
4703  *
4704  */
4705     (function() {
4706         Roo.lib.Motion = function(el, attributes, duration, method) {
4707             if (el) {
4708                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4709             }
4710         };
4711
4712         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4713
4714
4715         var Y = Roo.lib;
4716         var superclass = Y.Motion.superclass;
4717         var proto = Y.Motion.prototype;
4718
4719         proto.toString = function() {
4720             var el = this.getEl();
4721             var id = el.id || el.tagName;
4722             return ("Motion " + id);
4723         };
4724
4725         proto.patterns.points = /^points$/i;
4726
4727         proto.setAttribute = function(attr, val, unit) {
4728             if (this.patterns.points.test(attr)) {
4729                 unit = unit || 'px';
4730                 superclass.setAttribute.call(this, 'left', val[0], unit);
4731                 superclass.setAttribute.call(this, 'top', val[1], unit);
4732             } else {
4733                 superclass.setAttribute.call(this, attr, val, unit);
4734             }
4735         };
4736
4737         proto.getAttribute = function(attr) {
4738             if (this.patterns.points.test(attr)) {
4739                 var val = [
4740                         superclass.getAttribute.call(this, 'left'),
4741                         superclass.getAttribute.call(this, 'top')
4742                         ];
4743             } else {
4744                 val = superclass.getAttribute.call(this, attr);
4745             }
4746
4747             return val;
4748         };
4749
4750         proto.doMethod = function(attr, start, end) {
4751             var val = null;
4752
4753             if (this.patterns.points.test(attr)) {
4754                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4755                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4756             } else {
4757                 val = superclass.doMethod.call(this, attr, start, end);
4758             }
4759             return val;
4760         };
4761
4762         proto.setRuntimeAttribute = function(attr) {
4763             if (this.patterns.points.test(attr)) {
4764                 var el = this.getEl();
4765                 var attributes = this.attributes;
4766                 var start;
4767                 var control = attributes['points']['control'] || [];
4768                 var end;
4769                 var i, len;
4770
4771                 if (control.length > 0 && !(control[0] instanceof Array)) {
4772                     control = [control];
4773                 } else {
4774                     var tmp = [];
4775                     for (i = 0,len = control.length; i < len; ++i) {
4776                         tmp[i] = control[i];
4777                     }
4778                     control = tmp;
4779                 }
4780
4781                 Roo.fly(el).position();
4782
4783                 if (isset(attributes['points']['from'])) {
4784                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4785                 }
4786                 else {
4787                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4788                 }
4789
4790                 start = this.getAttribute('points');
4791
4792
4793                 if (isset(attributes['points']['to'])) {
4794                     end = translateValues.call(this, attributes['points']['to'], start);
4795
4796                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4797                     for (i = 0,len = control.length; i < len; ++i) {
4798                         control[i] = translateValues.call(this, control[i], start);
4799                     }
4800
4801
4802                 } else if (isset(attributes['points']['by'])) {
4803                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4804
4805                     for (i = 0,len = control.length; i < len; ++i) {
4806                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4807                     }
4808                 }
4809
4810                 this.runtimeAttributes[attr] = [start];
4811
4812                 if (control.length > 0) {
4813                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4814                 }
4815
4816                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4817             }
4818             else {
4819                 superclass.setRuntimeAttribute.call(this, attr);
4820             }
4821         };
4822
4823         var translateValues = function(val, start) {
4824             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4825             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4826
4827             return val;
4828         };
4829
4830         var isset = function(prop) {
4831             return (typeof prop !== 'undefined');
4832         };
4833     })();
4834 /*
4835  * Portions of this file are based on pieces of Yahoo User Interface Library
4836  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4837  * YUI licensed under the BSD License:
4838  * http://developer.yahoo.net/yui/license.txt
4839  * <script type="text/javascript">
4840  *
4841  */
4842     (function() {
4843         Roo.lib.Scroll = function(el, attributes, duration, method) {
4844             if (el) {
4845                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4846             }
4847         };
4848
4849         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4850
4851
4852         var Y = Roo.lib;
4853         var superclass = Y.Scroll.superclass;
4854         var proto = Y.Scroll.prototype;
4855
4856         proto.toString = function() {
4857             var el = this.getEl();
4858             var id = el.id || el.tagName;
4859             return ("Scroll " + id);
4860         };
4861
4862         proto.doMethod = function(attr, start, end) {
4863             var val = null;
4864
4865             if (attr == 'scroll') {
4866                 val = [
4867                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4868                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4869                         ];
4870
4871             } else {
4872                 val = superclass.doMethod.call(this, attr, start, end);
4873             }
4874             return val;
4875         };
4876
4877         proto.getAttribute = function(attr) {
4878             var val = null;
4879             var el = this.getEl();
4880
4881             if (attr == 'scroll') {
4882                 val = [ el.scrollLeft, el.scrollTop ];
4883             } else {
4884                 val = superclass.getAttribute.call(this, attr);
4885             }
4886
4887             return val;
4888         };
4889
4890         proto.setAttribute = function(attr, val, unit) {
4891             var el = this.getEl();
4892
4893             if (attr == 'scroll') {
4894                 el.scrollLeft = val[0];
4895                 el.scrollTop = val[1];
4896             } else {
4897                 superclass.setAttribute.call(this, attr, val, unit);
4898             }
4899         };
4900     })();
4901 /*
4902  * Based on:
4903  * Ext JS Library 1.1.1
4904  * Copyright(c) 2006-2007, Ext JS, LLC.
4905  *
4906  * Originally Released Under LGPL - original licence link has changed is not relivant.
4907  *
4908  * Fork - LGPL
4909  * <script type="text/javascript">
4910  */
4911
4912
4913 // nasty IE9 hack - what a pile of crap that is..
4914
4915  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4916     Range.prototype.createContextualFragment = function (html) {
4917         var doc = window.document;
4918         var container = doc.createElement("div");
4919         container.innerHTML = html;
4920         var frag = doc.createDocumentFragment(), n;
4921         while ((n = container.firstChild)) {
4922             frag.appendChild(n);
4923         }
4924         return frag;
4925     };
4926 }
4927
4928 /**
4929  * @class Roo.DomHelper
4930  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4931  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4932  * @static
4933  */
4934 Roo.DomHelper = function(){
4935     var tempTableEl = null;
4936     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4937     var tableRe = /^table|tbody|tr|td$/i;
4938     var xmlns = {};
4939     // build as innerHTML where available
4940     /** @ignore */
4941     var createHtml = function(o){
4942         if(typeof o == 'string'){
4943             return o;
4944         }
4945         var b = "";
4946         if(!o.tag){
4947             o.tag = "div";
4948         }
4949         b += "<" + o.tag;
4950         for(var attr in o){
4951             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4952             if(attr == "style"){
4953                 var s = o["style"];
4954                 if(typeof s == "function"){
4955                     s = s.call();
4956                 }
4957                 if(typeof s == "string"){
4958                     b += ' style="' + s + '"';
4959                 }else if(typeof s == "object"){
4960                     b += ' style="';
4961                     for(var key in s){
4962                         if(typeof s[key] != "function"){
4963                             b += key + ":" + s[key] + ";";
4964                         }
4965                     }
4966                     b += '"';
4967                 }
4968             }else{
4969                 if(attr == "cls"){
4970                     b += ' class="' + o["cls"] + '"';
4971                 }else if(attr == "htmlFor"){
4972                     b += ' for="' + o["htmlFor"] + '"';
4973                 }else{
4974                     b += " " + attr + '="' + o[attr] + '"';
4975                 }
4976             }
4977         }
4978         if(emptyTags.test(o.tag)){
4979             b += "/>";
4980         }else{
4981             b += ">";
4982             var cn = o.children || o.cn;
4983             if(cn){
4984                 //http://bugs.kde.org/show_bug.cgi?id=71506
4985                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4986                     for(var i = 0, len = cn.length; i < len; i++) {
4987                         b += createHtml(cn[i], b);
4988                     }
4989                 }else{
4990                     b += createHtml(cn, b);
4991                 }
4992             }
4993             if(o.html){
4994                 b += o.html;
4995             }
4996             b += "</" + o.tag + ">";
4997         }
4998         return b;
4999     };
5000
5001     // build as dom
5002     /** @ignore */
5003     var createDom = function(o, parentNode){
5004          
5005         // defininition craeted..
5006         var ns = false;
5007         if (o.ns && o.ns != 'html') {
5008                
5009             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5010                 xmlns[o.ns] = o.xmlns;
5011                 ns = o.xmlns;
5012             }
5013             if (typeof(xmlns[o.ns]) == 'undefined') {
5014                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5015             }
5016             ns = xmlns[o.ns];
5017         }
5018         
5019         
5020         if (typeof(o) == 'string') {
5021             return parentNode.appendChild(document.createTextNode(o));
5022         }
5023         o.tag = o.tag || div;
5024         if (o.ns && Roo.isIE) {
5025             ns = false;
5026             o.tag = o.ns + ':' + o.tag;
5027             
5028         }
5029         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5030         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5031         for(var attr in o){
5032             
5033             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5034                     attr == "style" || typeof o[attr] == "function") { continue; }
5035                     
5036             if(attr=="cls" && Roo.isIE){
5037                 el.className = o["cls"];
5038             }else{
5039                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5040                 else { 
5041                     el[attr] = o[attr];
5042                 }
5043             }
5044         }
5045         Roo.DomHelper.applyStyles(el, o.style);
5046         var cn = o.children || o.cn;
5047         if(cn){
5048             //http://bugs.kde.org/show_bug.cgi?id=71506
5049              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5050                 for(var i = 0, len = cn.length; i < len; i++) {
5051                     createDom(cn[i], el);
5052                 }
5053             }else{
5054                 createDom(cn, el);
5055             }
5056         }
5057         if(o.html){
5058             el.innerHTML = o.html;
5059         }
5060         if(parentNode){
5061            parentNode.appendChild(el);
5062         }
5063         return el;
5064     };
5065
5066     var ieTable = function(depth, s, h, e){
5067         tempTableEl.innerHTML = [s, h, e].join('');
5068         var i = -1, el = tempTableEl;
5069         while(++i < depth && el.firstChild){
5070             el = el.firstChild;
5071         }
5072         return el;
5073     };
5074
5075     // kill repeat to save bytes
5076     var ts = '<table>',
5077         te = '</table>',
5078         tbs = ts+'<tbody>',
5079         tbe = '</tbody>'+te,
5080         trs = tbs + '<tr>',
5081         tre = '</tr>'+tbe;
5082
5083     /**
5084      * @ignore
5085      * Nasty code for IE's broken table implementation
5086      */
5087     var insertIntoTable = function(tag, where, el, html){
5088         if(!tempTableEl){
5089             tempTableEl = document.createElement('div');
5090         }
5091         var node;
5092         var before = null;
5093         if(tag == 'td'){
5094             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5095                 return;
5096             }
5097             if(where == 'beforebegin'){
5098                 before = el;
5099                 el = el.parentNode;
5100             } else{
5101                 before = el.nextSibling;
5102                 el = el.parentNode;
5103             }
5104             node = ieTable(4, trs, html, tre);
5105         }
5106         else if(tag == 'tr'){
5107             if(where == 'beforebegin'){
5108                 before = el;
5109                 el = el.parentNode;
5110                 node = ieTable(3, tbs, html, tbe);
5111             } else if(where == 'afterend'){
5112                 before = el.nextSibling;
5113                 el = el.parentNode;
5114                 node = ieTable(3, tbs, html, tbe);
5115             } else{ // INTO a TR
5116                 if(where == 'afterbegin'){
5117                     before = el.firstChild;
5118                 }
5119                 node = ieTable(4, trs, html, tre);
5120             }
5121         } else if(tag == 'tbody'){
5122             if(where == 'beforebegin'){
5123                 before = el;
5124                 el = el.parentNode;
5125                 node = ieTable(2, ts, html, te);
5126             } else if(where == 'afterend'){
5127                 before = el.nextSibling;
5128                 el = el.parentNode;
5129                 node = ieTable(2, ts, html, te);
5130             } else{
5131                 if(where == 'afterbegin'){
5132                     before = el.firstChild;
5133                 }
5134                 node = ieTable(3, tbs, html, tbe);
5135             }
5136         } else{ // TABLE
5137             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5138                 return;
5139             }
5140             if(where == 'afterbegin'){
5141                 before = el.firstChild;
5142             }
5143             node = ieTable(2, ts, html, te);
5144         }
5145         el.insertBefore(node, before);
5146         return node;
5147     };
5148     
5149     // this is a bit like the react update code...
5150     // 
5151     
5152     var updateNode = function(from, to)
5153     {
5154         // should we handle non-standard elements?
5155         
5156         if (from.nodeType != to.nodeType) {
5157             from.parentNode.replaceChild(to, from);
5158         }
5159         
5160         if (from.nodeType == 3) {
5161             // assume it's text?!
5162             if (from.data == to.data) {
5163                 return;
5164             }
5165             from.data = to.data;
5166             return;
5167         }
5168         
5169         // assume 'to' doesnt have '1/3 nodetypes!
5170         if (from.nodeType !=1 || from.tagName != to.tagName) {
5171             from.parentNode.replaceChild(to, from);
5172             return;
5173         }
5174         // compare attributes
5175         var ar = Array.from(from.attributes);
5176         for(var i = 0; i< ar.length;i++) {
5177             if (to.hasAttribute(ar[i].name)) {
5178                 continue;
5179             }
5180             from.removeAttribute(ar[i].name);
5181         }
5182         ar = to.attributes;
5183         for(var i = 0; i< ar.length;i++) {
5184             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5185                 continue;
5186             }
5187             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5188         }
5189         // children
5190         var far = Array.from(from.childNodes);
5191         var tar = Array.from(to.childNodes);
5192         // if the lengths are different.. then it's probably a editable content change, rather than
5193         // a change of the block definition..
5194         if (from.innerHTML == to.innerHTML) {
5195             return;
5196         }
5197         if (far.length != tar.length) {
5198             from.innerHTML = to.innerHTML;
5199             return;
5200         }
5201         
5202         for(var i = 0; i < far.length; i++) {
5203             updateNode(far[i], tar[i]);
5204         }
5205         
5206         
5207     };
5208     
5209     
5210
5211     return {
5212         /** True to force the use of DOM instead of html fragments @type Boolean */
5213         useDom : false,
5214     
5215         /**
5216          * Returns the markup for the passed Element(s) config
5217          * @param {Object} o The Dom object spec (and children)
5218          * @return {String}
5219          */
5220         markup : function(o){
5221             return createHtml(o);
5222         },
5223     
5224         /**
5225          * Applies a style specification to an element
5226          * @param {String/HTMLElement} el The element to apply styles to
5227          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5228          * a function which returns such a specification.
5229          */
5230         applyStyles : function(el, styles){
5231             if(styles){
5232                el = Roo.fly(el);
5233                if(typeof styles == "string"){
5234                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5235                    var matches;
5236                    while ((matches = re.exec(styles)) != null){
5237                        el.setStyle(matches[1], matches[2]);
5238                    }
5239                }else if (typeof styles == "object"){
5240                    for (var style in styles){
5241                       el.setStyle(style, styles[style]);
5242                    }
5243                }else if (typeof styles == "function"){
5244                     Roo.DomHelper.applyStyles(el, styles.call());
5245                }
5246             }
5247         },
5248     
5249         /**
5250          * Inserts an HTML fragment into the Dom
5251          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5252          * @param {HTMLElement} el The context element
5253          * @param {String} html The HTML fragmenet
5254          * @return {HTMLElement} The new node
5255          */
5256         insertHtml : function(where, el, html){
5257             where = where.toLowerCase();
5258             if(el.insertAdjacentHTML){
5259                 if(tableRe.test(el.tagName)){
5260                     var rs;
5261                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5262                         return rs;
5263                     }
5264                 }
5265                 switch(where){
5266                     case "beforebegin":
5267                         el.insertAdjacentHTML('BeforeBegin', html);
5268                         return el.previousSibling;
5269                     case "afterbegin":
5270                         el.insertAdjacentHTML('AfterBegin', html);
5271                         return el.firstChild;
5272                     case "beforeend":
5273                         el.insertAdjacentHTML('BeforeEnd', html);
5274                         return el.lastChild;
5275                     case "afterend":
5276                         el.insertAdjacentHTML('AfterEnd', html);
5277                         return el.nextSibling;
5278                 }
5279                 throw 'Illegal insertion point -> "' + where + '"';
5280             }
5281             var range = el.ownerDocument.createRange();
5282             var frag;
5283             switch(where){
5284                  case "beforebegin":
5285                     range.setStartBefore(el);
5286                     frag = range.createContextualFragment(html);
5287                     el.parentNode.insertBefore(frag, el);
5288                     return el.previousSibling;
5289                  case "afterbegin":
5290                     if(el.firstChild){
5291                         range.setStartBefore(el.firstChild);
5292                         frag = range.createContextualFragment(html);
5293                         el.insertBefore(frag, el.firstChild);
5294                         return el.firstChild;
5295                     }else{
5296                         el.innerHTML = html;
5297                         return el.firstChild;
5298                     }
5299                 case "beforeend":
5300                     if(el.lastChild){
5301                         range.setStartAfter(el.lastChild);
5302                         frag = range.createContextualFragment(html);
5303                         el.appendChild(frag);
5304                         return el.lastChild;
5305                     }else{
5306                         el.innerHTML = html;
5307                         return el.lastChild;
5308                     }
5309                 case "afterend":
5310                     range.setStartAfter(el);
5311                     frag = range.createContextualFragment(html);
5312                     el.parentNode.insertBefore(frag, el.nextSibling);
5313                     return el.nextSibling;
5314                 }
5315                 throw 'Illegal insertion point -> "' + where + '"';
5316         },
5317     
5318         /**
5319          * Creates new Dom element(s) and inserts them before el
5320          * @param {String/HTMLElement/Element} el The context element
5321          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5322          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5323          * @return {HTMLElement/Roo.Element} The new node
5324          */
5325         insertBefore : function(el, o, returnElement){
5326             return this.doInsert(el, o, returnElement, "beforeBegin");
5327         },
5328     
5329         /**
5330          * Creates new Dom element(s) and inserts them after el
5331          * @param {String/HTMLElement/Element} el The context element
5332          * @param {Object} o The Dom object spec (and children)
5333          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5334          * @return {HTMLElement/Roo.Element} The new node
5335          */
5336         insertAfter : function(el, o, returnElement){
5337             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5338         },
5339     
5340         /**
5341          * Creates new Dom element(s) and inserts them as the first child of el
5342          * @param {String/HTMLElement/Element} el The context element
5343          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5344          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5345          * @return {HTMLElement/Roo.Element} The new node
5346          */
5347         insertFirst : function(el, o, returnElement){
5348             return this.doInsert(el, o, returnElement, "afterBegin");
5349         },
5350     
5351         // private
5352         doInsert : function(el, o, returnElement, pos, sibling){
5353             el = Roo.getDom(el);
5354             var newNode;
5355             if(this.useDom || o.ns){
5356                 newNode = createDom(o, null);
5357                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5358             }else{
5359                 var html = createHtml(o);
5360                 newNode = this.insertHtml(pos, el, html);
5361             }
5362             return returnElement ? Roo.get(newNode, true) : newNode;
5363         },
5364     
5365         /**
5366          * Creates new Dom element(s) and appends them to el
5367          * @param {String/HTMLElement/Element} el The context element
5368          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5369          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5370          * @return {HTMLElement/Roo.Element} The new node
5371          */
5372         append : function(el, o, returnElement){
5373             el = Roo.getDom(el);
5374             var newNode;
5375             if(this.useDom || o.ns){
5376                 newNode = createDom(o, null);
5377                 el.appendChild(newNode);
5378             }else{
5379                 var html = createHtml(o);
5380                 newNode = this.insertHtml("beforeEnd", el, html);
5381             }
5382             return returnElement ? Roo.get(newNode, true) : newNode;
5383         },
5384     
5385         /**
5386          * Creates new Dom element(s) and overwrites the contents of el with them
5387          * @param {String/HTMLElement/Element} el The context element
5388          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5389          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5390          * @return {HTMLElement/Roo.Element} The new node
5391          */
5392         overwrite : function(el, o, returnElement)
5393         {
5394             el = Roo.getDom(el);
5395             if (o.ns) {
5396               
5397                 while (el.childNodes.length) {
5398                     el.removeChild(el.firstChild);
5399                 }
5400                 createDom(o, el);
5401             } else {
5402                 el.innerHTML = createHtml(o);   
5403             }
5404             
5405             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5406         },
5407     
5408         /**
5409          * Creates a new Roo.DomHelper.Template from the Dom object spec
5410          * @param {Object} o The Dom object spec (and children)
5411          * @return {Roo.DomHelper.Template} The new template
5412          */
5413         createTemplate : function(o){
5414             var html = createHtml(o);
5415             return new Roo.Template(html);
5416         },
5417          /**
5418          * Updates the first element with the spec from the o (replacing if necessary)
5419          * This iterates through the children, and updates attributes / children etc..
5420          * @param {String/HTMLElement/Element} el The context element
5421          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5422          */
5423         
5424         update : function(el, o)
5425         {
5426             updateNode(Roo.getDom(el), createDom(o));
5427             
5428         }
5429         
5430         
5431     };
5432 }();
5433 /*
5434  * Based on:
5435  * Ext JS Library 1.1.1
5436  * Copyright(c) 2006-2007, Ext JS, LLC.
5437  *
5438  * Originally Released Under LGPL - original licence link has changed is not relivant.
5439  *
5440  * Fork - LGPL
5441  * <script type="text/javascript">
5442  */
5443  
5444 /**
5445 * @class Roo.Template
5446 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5447 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5448 * Usage:
5449 <pre><code>
5450 var t = new Roo.Template({
5451     html :  '&lt;div name="{id}"&gt;' + 
5452         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5453         '&lt;/div&gt;',
5454     myformat: function (value, allValues) {
5455         return 'XX' + value;
5456     }
5457 });
5458 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5459 </code></pre>
5460 * For more information see this blog post with examples:
5461 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5462      - Create Elements using DOM, HTML fragments and Templates</a>. 
5463 * @constructor
5464 * @param {Object} cfg - Configuration object.
5465 */
5466 Roo.Template = function(cfg){
5467     // BC!
5468     if(cfg instanceof Array){
5469         cfg = cfg.join("");
5470     }else if(arguments.length > 1){
5471         cfg = Array.prototype.join.call(arguments, "");
5472     }
5473     
5474     
5475     if (typeof(cfg) == 'object') {
5476         Roo.apply(this,cfg)
5477     } else {
5478         // bc
5479         this.html = cfg;
5480     }
5481     if (this.url) {
5482         this.load();
5483     }
5484     
5485 };
5486 Roo.Template.prototype = {
5487     
5488     /**
5489      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5490      */
5491     onLoad : false,
5492     
5493     
5494     /**
5495      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
5496      *                    it should be fixed so that template is observable...
5497      */
5498     url : false,
5499     /**
5500      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5501      */
5502     html : '',
5503     
5504     
5505     compiled : false,
5506     loaded : false,
5507     /**
5508      * Returns an HTML fragment of this template with the specified values applied.
5509      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5510      * @return {String} The HTML fragment
5511      */
5512     
5513    
5514     
5515     applyTemplate : function(values){
5516         //Roo.log(["applyTemplate", values]);
5517         try {
5518            
5519             if(this.compiled){
5520                 return this.compiled(values);
5521             }
5522             var useF = this.disableFormats !== true;
5523             var fm = Roo.util.Format, tpl = this;
5524             var fn = function(m, name, format, args){
5525                 if(format && useF){
5526                     if(format.substr(0, 5) == "this."){
5527                         return tpl.call(format.substr(5), values[name], values);
5528                     }else{
5529                         if(args){
5530                             // quoted values are required for strings in compiled templates, 
5531                             // but for non compiled we need to strip them
5532                             // quoted reversed for jsmin
5533                             var re = /^\s*['"](.*)["']\s*$/;
5534                             args = args.split(',');
5535                             for(var i = 0, len = args.length; i < len; i++){
5536                                 args[i] = args[i].replace(re, "$1");
5537                             }
5538                             args = [values[name]].concat(args);
5539                         }else{
5540                             args = [values[name]];
5541                         }
5542                         return fm[format].apply(fm, args);
5543                     }
5544                 }else{
5545                     return values[name] !== undefined ? values[name] : "";
5546                 }
5547             };
5548             return this.html.replace(this.re, fn);
5549         } catch (e) {
5550             Roo.log(e);
5551             throw e;
5552         }
5553          
5554     },
5555     
5556     loading : false,
5557       
5558     load : function ()
5559     {
5560          
5561         if (this.loading) {
5562             return;
5563         }
5564         var _t = this;
5565         
5566         this.loading = true;
5567         this.compiled = false;
5568         
5569         var cx = new Roo.data.Connection();
5570         cx.request({
5571             url : this.url,
5572             method : 'GET',
5573             success : function (response) {
5574                 _t.loading = false;
5575                 _t.url = false;
5576                 
5577                 _t.set(response.responseText,true);
5578                 _t.loaded = true;
5579                 if (_t.onLoad) {
5580                     _t.onLoad();
5581                 }
5582              },
5583             failure : function(response) {
5584                 Roo.log("Template failed to load from " + _t.url);
5585                 _t.loading = false;
5586             }
5587         });
5588     },
5589
5590     /**
5591      * Sets the HTML used as the template and optionally compiles it.
5592      * @param {String} html
5593      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5594      * @return {Roo.Template} this
5595      */
5596     set : function(html, compile){
5597         this.html = html;
5598         this.compiled = false;
5599         if(compile){
5600             this.compile();
5601         }
5602         return this;
5603     },
5604     
5605     /**
5606      * True to disable format functions (defaults to false)
5607      * @type Boolean
5608      */
5609     disableFormats : false,
5610     
5611     /**
5612     * The regular expression used to match template variables 
5613     * @type RegExp
5614     * @property 
5615     */
5616     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5617     
5618     /**
5619      * Compiles the template into an internal function, eliminating the RegEx overhead.
5620      * @return {Roo.Template} this
5621      */
5622     compile : function(){
5623         var fm = Roo.util.Format;
5624         var useF = this.disableFormats !== true;
5625         var sep = Roo.isGecko ? "+" : ",";
5626         var fn = function(m, name, format, args){
5627             if(format && useF){
5628                 args = args ? ',' + args : "";
5629                 if(format.substr(0, 5) != "this."){
5630                     format = "fm." + format + '(';
5631                 }else{
5632                     format = 'this.call("'+ format.substr(5) + '", ';
5633                     args = ", values";
5634                 }
5635             }else{
5636                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5637             }
5638             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5639         };
5640         var body;
5641         // branched to use + in gecko and [].join() in others
5642         if(Roo.isGecko){
5643             body = "this.compiled = function(values){ return '" +
5644                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5645                     "';};";
5646         }else{
5647             body = ["this.compiled = function(values){ return ['"];
5648             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5649             body.push("'].join('');};");
5650             body = body.join('');
5651         }
5652         /**
5653          * eval:var:values
5654          * eval:var:fm
5655          */
5656         eval(body);
5657         return this;
5658     },
5659     
5660     // private function used to call members
5661     call : function(fnName, value, allValues){
5662         return this[fnName](value, allValues);
5663     },
5664     
5665     /**
5666      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5667      * @param {String/HTMLElement/Roo.Element} el The context element
5668      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5669      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5670      * @return {HTMLElement/Roo.Element} The new node or Element
5671      */
5672     insertFirst: function(el, values, returnElement){
5673         return this.doInsert('afterBegin', el, values, returnElement);
5674     },
5675
5676     /**
5677      * Applies the supplied values to the template and inserts the new node(s) before el.
5678      * @param {String/HTMLElement/Roo.Element} el The context element
5679      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5680      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5681      * @return {HTMLElement/Roo.Element} The new node or Element
5682      */
5683     insertBefore: function(el, values, returnElement){
5684         return this.doInsert('beforeBegin', el, values, returnElement);
5685     },
5686
5687     /**
5688      * Applies the supplied values to the template and inserts the new node(s) after el.
5689      * @param {String/HTMLElement/Roo.Element} el The context element
5690      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5691      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5692      * @return {HTMLElement/Roo.Element} The new node or Element
5693      */
5694     insertAfter : function(el, values, returnElement){
5695         return this.doInsert('afterEnd', el, values, returnElement);
5696     },
5697     
5698     /**
5699      * Applies the supplied values to the template and appends the new node(s) to el.
5700      * @param {String/HTMLElement/Roo.Element} el The context element
5701      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5702      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5703      * @return {HTMLElement/Roo.Element} The new node or Element
5704      */
5705     append : function(el, values, returnElement){
5706         return this.doInsert('beforeEnd', el, values, returnElement);
5707     },
5708
5709     doInsert : function(where, el, values, returnEl){
5710         el = Roo.getDom(el);
5711         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5712         return returnEl ? Roo.get(newNode, true) : newNode;
5713     },
5714
5715     /**
5716      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5717      * @param {String/HTMLElement/Roo.Element} el The context element
5718      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
5719      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5720      * @return {HTMLElement/Roo.Element} The new node or Element
5721      */
5722     overwrite : function(el, values, returnElement){
5723         el = Roo.getDom(el);
5724         el.innerHTML = this.applyTemplate(values);
5725         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5726     }
5727 };
5728 /**
5729  * Alias for {@link #applyTemplate}
5730  * @method
5731  */
5732 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5733
5734 // backwards compat
5735 Roo.DomHelper.Template = Roo.Template;
5736
5737 /**
5738  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5739  * @param {String/HTMLElement} el A DOM element or its id
5740  * @returns {Roo.Template} The created template
5741  * @static
5742  */
5743 Roo.Template.from = function(el){
5744     el = Roo.getDom(el);
5745     return new Roo.Template(el.value || el.innerHTML);
5746 };/*
5747  * Based on:
5748  * Ext JS Library 1.1.1
5749  * Copyright(c) 2006-2007, Ext JS, LLC.
5750  *
5751  * Originally Released Under LGPL - original licence link has changed is not relivant.
5752  *
5753  * Fork - LGPL
5754  * <script type="text/javascript">
5755  */
5756  
5757
5758 /*
5759  * This is code is also distributed under MIT license for use
5760  * with jQuery and prototype JavaScript libraries.
5761  */
5762 /**
5763  * @class Roo.DomQuery
5764 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
5765 <p>
5766 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
5767
5768 <p>
5769 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
5770 </p>
5771 <h4>Element Selectors:</h4>
5772 <ul class="list">
5773     <li> <b>*</b> any element</li>
5774     <li> <b>E</b> an element with the tag E</li>
5775     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5776     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5777     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5778     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5779 </ul>
5780 <h4>Attribute Selectors:</h4>
5781 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5782 <ul class="list">
5783     <li> <b>E[foo]</b> has an attribute "foo"</li>
5784     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5785     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5786     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5787     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5788     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5789     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5790 </ul>
5791 <h4>Pseudo Classes:</h4>
5792 <ul class="list">
5793     <li> <b>E:first-child</b> E is the first child of its parent</li>
5794     <li> <b>E:last-child</b> E is the last child of its parent</li>
5795     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
5796     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5797     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5798     <li> <b>E:only-child</b> E is the only child of its parent</li>
5799     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
5800     <li> <b>E:first</b> the first E in the resultset</li>
5801     <li> <b>E:last</b> the last E in the resultset</li>
5802     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5803     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5804     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5805     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5806     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5807     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5808     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5809     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5810     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5811 </ul>
5812 <h4>CSS Value Selectors:</h4>
5813 <ul class="list">
5814     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5815     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5816     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5817     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5818     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5819     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5820 </ul>
5821  * @static
5822  */
5823 Roo.DomQuery = function(){
5824     var cache = {}, simpleCache = {}, valueCache = {};
5825     var nonSpace = /\S/;
5826     var trimRe = /^\s+|\s+$/g;
5827     var tplRe = /\{(\d+)\}/g;
5828     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5829     var tagTokenRe = /^(#)?([\w-\*]+)/;
5830     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5831
5832     function child(p, index){
5833         var i = 0;
5834         var n = p.firstChild;
5835         while(n){
5836             if(n.nodeType == 1){
5837                if(++i == index){
5838                    return n;
5839                }
5840             }
5841             n = n.nextSibling;
5842         }
5843         return null;
5844     };
5845
5846     function next(n){
5847         while((n = n.nextSibling) && n.nodeType != 1);
5848         return n;
5849     };
5850
5851     function prev(n){
5852         while((n = n.previousSibling) && n.nodeType != 1);
5853         return n;
5854     };
5855
5856     function children(d){
5857         var n = d.firstChild, ni = -1;
5858             while(n){
5859                 var nx = n.nextSibling;
5860                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5861                     d.removeChild(n);
5862                 }else{
5863                     n.nodeIndex = ++ni;
5864                 }
5865                 n = nx;
5866             }
5867             return this;
5868         };
5869
5870     function byClassName(c, a, v){
5871         if(!v){
5872             return c;
5873         }
5874         var r = [], ri = -1, cn;
5875         for(var i = 0, ci; ci = c[i]; i++){
5876             
5877             
5878             if((' '+
5879                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5880                  +' ').indexOf(v) != -1){
5881                 r[++ri] = ci;
5882             }
5883         }
5884         return r;
5885     };
5886
5887     function attrValue(n, attr){
5888         if(!n.tagName && typeof n.length != "undefined"){
5889             n = n[0];
5890         }
5891         if(!n){
5892             return null;
5893         }
5894         if(attr == "for"){
5895             return n.htmlFor;
5896         }
5897         if(attr == "class" || attr == "className"){
5898             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5899         }
5900         return n.getAttribute(attr) || n[attr];
5901
5902     };
5903
5904     function getNodes(ns, mode, tagName){
5905         var result = [], ri = -1, cs;
5906         if(!ns){
5907             return result;
5908         }
5909         tagName = tagName || "*";
5910         if(typeof ns.getElementsByTagName != "undefined"){
5911             ns = [ns];
5912         }
5913         if(!mode){
5914             for(var i = 0, ni; ni = ns[i]; i++){
5915                 cs = ni.getElementsByTagName(tagName);
5916                 for(var j = 0, ci; ci = cs[j]; j++){
5917                     result[++ri] = ci;
5918                 }
5919             }
5920         }else if(mode == "/" || mode == ">"){
5921             var utag = tagName.toUpperCase();
5922             for(var i = 0, ni, cn; ni = ns[i]; i++){
5923                 cn = ni.children || ni.childNodes;
5924                 for(var j = 0, cj; cj = cn[j]; j++){
5925                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5926                         result[++ri] = cj;
5927                     }
5928                 }
5929             }
5930         }else if(mode == "+"){
5931             var utag = tagName.toUpperCase();
5932             for(var i = 0, n; n = ns[i]; i++){
5933                 while((n = n.nextSibling) && n.nodeType != 1);
5934                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5935                     result[++ri] = n;
5936                 }
5937             }
5938         }else if(mode == "~"){
5939             for(var i = 0, n; n = ns[i]; i++){
5940                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5941                 if(n){
5942                     result[++ri] = n;
5943                 }
5944             }
5945         }
5946         return result;
5947     };
5948
5949     function concat(a, b){
5950         if(b.slice){
5951             return a.concat(b);
5952         }
5953         for(var i = 0, l = b.length; i < l; i++){
5954             a[a.length] = b[i];
5955         }
5956         return a;
5957     }
5958
5959     function byTag(cs, tagName){
5960         if(cs.tagName || cs == document){
5961             cs = [cs];
5962         }
5963         if(!tagName){
5964             return cs;
5965         }
5966         var r = [], ri = -1;
5967         tagName = tagName.toLowerCase();
5968         for(var i = 0, ci; ci = cs[i]; i++){
5969             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5970                 r[++ri] = ci;
5971             }
5972         }
5973         return r;
5974     };
5975
5976     function byId(cs, attr, id){
5977         if(cs.tagName || cs == document){
5978             cs = [cs];
5979         }
5980         if(!id){
5981             return cs;
5982         }
5983         var r = [], ri = -1;
5984         for(var i = 0,ci; ci = cs[i]; i++){
5985             if(ci && ci.id == id){
5986                 r[++ri] = ci;
5987                 return r;
5988             }
5989         }
5990         return r;
5991     };
5992
5993     function byAttribute(cs, attr, value, op, custom){
5994         var r = [], ri = -1, st = custom=="{";
5995         var f = Roo.DomQuery.operators[op];
5996         for(var i = 0, ci; ci = cs[i]; i++){
5997             var a;
5998             if(st){
5999                 a = Roo.DomQuery.getStyle(ci, attr);
6000             }
6001             else if(attr == "class" || attr == "className"){
6002                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6003             }else if(attr == "for"){
6004                 a = ci.htmlFor;
6005             }else if(attr == "href"){
6006                 a = ci.getAttribute("href", 2);
6007             }else{
6008                 a = ci.getAttribute(attr);
6009             }
6010             if((f && f(a, value)) || (!f && a)){
6011                 r[++ri] = ci;
6012             }
6013         }
6014         return r;
6015     };
6016
6017     function byPseudo(cs, name, value){
6018         return Roo.DomQuery.pseudos[name](cs, value);
6019     };
6020
6021     // This is for IE MSXML which does not support expandos.
6022     // IE runs the same speed using setAttribute, however FF slows way down
6023     // and Safari completely fails so they need to continue to use expandos.
6024     var isIE = window.ActiveXObject ? true : false;
6025
6026     // this eval is stop the compressor from
6027     // renaming the variable to something shorter
6028     
6029     /** eval:var:batch */
6030     var batch = 30803; 
6031
6032     var key = 30803;
6033
6034     function nodupIEXml(cs){
6035         var d = ++key;
6036         cs[0].setAttribute("_nodup", d);
6037         var r = [cs[0]];
6038         for(var i = 1, len = cs.length; i < len; i++){
6039             var c = cs[i];
6040             if(!c.getAttribute("_nodup") != d){
6041                 c.setAttribute("_nodup", d);
6042                 r[r.length] = c;
6043             }
6044         }
6045         for(var i = 0, len = cs.length; i < len; i++){
6046             cs[i].removeAttribute("_nodup");
6047         }
6048         return r;
6049     }
6050
6051     function nodup(cs){
6052         if(!cs){
6053             return [];
6054         }
6055         var len = cs.length, c, i, r = cs, cj, ri = -1;
6056         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6057             return cs;
6058         }
6059         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6060             return nodupIEXml(cs);
6061         }
6062         var d = ++key;
6063         cs[0]._nodup = d;
6064         for(i = 1; c = cs[i]; i++){
6065             if(c._nodup != d){
6066                 c._nodup = d;
6067             }else{
6068                 r = [];
6069                 for(var j = 0; j < i; j++){
6070                     r[++ri] = cs[j];
6071                 }
6072                 for(j = i+1; cj = cs[j]; j++){
6073                     if(cj._nodup != d){
6074                         cj._nodup = d;
6075                         r[++ri] = cj;
6076                     }
6077                 }
6078                 return r;
6079             }
6080         }
6081         return r;
6082     }
6083
6084     function quickDiffIEXml(c1, c2){
6085         var d = ++key;
6086         for(var i = 0, len = c1.length; i < len; i++){
6087             c1[i].setAttribute("_qdiff", d);
6088         }
6089         var r = [];
6090         for(var i = 0, len = c2.length; i < len; i++){
6091             if(c2[i].getAttribute("_qdiff") != d){
6092                 r[r.length] = c2[i];
6093             }
6094         }
6095         for(var i = 0, len = c1.length; i < len; i++){
6096            c1[i].removeAttribute("_qdiff");
6097         }
6098         return r;
6099     }
6100
6101     function quickDiff(c1, c2){
6102         var len1 = c1.length;
6103         if(!len1){
6104             return c2;
6105         }
6106         if(isIE && c1[0].selectSingleNode){
6107             return quickDiffIEXml(c1, c2);
6108         }
6109         var d = ++key;
6110         for(var i = 0; i < len1; i++){
6111             c1[i]._qdiff = d;
6112         }
6113         var r = [];
6114         for(var i = 0, len = c2.length; i < len; i++){
6115             if(c2[i]._qdiff != d){
6116                 r[r.length] = c2[i];
6117             }
6118         }
6119         return r;
6120     }
6121
6122     function quickId(ns, mode, root, id){
6123         if(ns == root){
6124            var d = root.ownerDocument || root;
6125            return d.getElementById(id);
6126         }
6127         ns = getNodes(ns, mode, "*");
6128         return byId(ns, null, id);
6129     }
6130
6131     return {
6132         getStyle : function(el, name){
6133             return Roo.fly(el).getStyle(name);
6134         },
6135         /**
6136          * Compiles a selector/xpath query into a reusable function. The returned function
6137          * takes one parameter "root" (optional), which is the context node from where the query should start.
6138          * @param {String} selector The selector/xpath query
6139          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6140          * @return {Function}
6141          */
6142         compile : function(path, type){
6143             type = type || "select";
6144             
6145             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6146             var q = path, mode, lq;
6147             var tk = Roo.DomQuery.matchers;
6148             var tklen = tk.length;
6149             var mm;
6150
6151             // accept leading mode switch
6152             var lmode = q.match(modeRe);
6153             if(lmode && lmode[1]){
6154                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6155                 q = q.replace(lmode[1], "");
6156             }
6157             // strip leading slashes
6158             while(path.substr(0, 1)=="/"){
6159                 path = path.substr(1);
6160             }
6161
6162             while(q && lq != q){
6163                 lq = q;
6164                 var tm = q.match(tagTokenRe);
6165                 if(type == "select"){
6166                     if(tm){
6167                         if(tm[1] == "#"){
6168                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6169                         }else{
6170                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6171                         }
6172                         q = q.replace(tm[0], "");
6173                     }else if(q.substr(0, 1) != '@'){
6174                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6175                     }
6176                 }else{
6177                     if(tm){
6178                         if(tm[1] == "#"){
6179                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6180                         }else{
6181                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6182                         }
6183                         q = q.replace(tm[0], "");
6184                     }
6185                 }
6186                 while(!(mm = q.match(modeRe))){
6187                     var matched = false;
6188                     for(var j = 0; j < tklen; j++){
6189                         var t = tk[j];
6190                         var m = q.match(t.re);
6191                         if(m){
6192                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6193                                                     return m[i];
6194                                                 });
6195                             q = q.replace(m[0], "");
6196                             matched = true;
6197                             break;
6198                         }
6199                     }
6200                     // prevent infinite loop on bad selector
6201                     if(!matched){
6202                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6203                     }
6204                 }
6205                 if(mm[1]){
6206                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6207                     q = q.replace(mm[1], "");
6208                 }
6209             }
6210             fn[fn.length] = "return nodup(n);\n}";
6211             
6212              /** 
6213               * list of variables that need from compression as they are used by eval.
6214              *  eval:var:batch 
6215              *  eval:var:nodup
6216              *  eval:var:byTag
6217              *  eval:var:ById
6218              *  eval:var:getNodes
6219              *  eval:var:quickId
6220              *  eval:var:mode
6221              *  eval:var:root
6222              *  eval:var:n
6223              *  eval:var:byClassName
6224              *  eval:var:byPseudo
6225              *  eval:var:byAttribute
6226              *  eval:var:attrValue
6227              * 
6228              **/ 
6229             eval(fn.join(""));
6230             return f;
6231         },
6232
6233         /**
6234          * Selects a group of elements.
6235          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6236          * @param {Node} root (optional) The start of the query (defaults to document).
6237          * @return {Array}
6238          */
6239         select : function(path, root, type){
6240             if(!root || root == document){
6241                 root = document;
6242             }
6243             if(typeof root == "string"){
6244                 root = document.getElementById(root);
6245             }
6246             var paths = path.split(",");
6247             var results = [];
6248             for(var i = 0, len = paths.length; i < len; i++){
6249                 var p = paths[i].replace(trimRe, "");
6250                 if(!cache[p]){
6251                     cache[p] = Roo.DomQuery.compile(p);
6252                     if(!cache[p]){
6253                         throw p + " is not a valid selector";
6254                     }
6255                 }
6256                 var result = cache[p](root);
6257                 if(result && result != document){
6258                     results = results.concat(result);
6259                 }
6260             }
6261             if(paths.length > 1){
6262                 return nodup(results);
6263             }
6264             return results;
6265         },
6266
6267         /**
6268          * Selects a single element.
6269          * @param {String} selector The selector/xpath query
6270          * @param {Node} root (optional) The start of the query (defaults to document).
6271          * @return {Element}
6272          */
6273         selectNode : function(path, root){
6274             return Roo.DomQuery.select(path, root)[0];
6275         },
6276
6277         /**
6278          * Selects the value of a node, optionally replacing null with the defaultValue.
6279          * @param {String} selector The selector/xpath query
6280          * @param {Node} root (optional) The start of the query (defaults to document).
6281          * @param {String} defaultValue
6282          */
6283         selectValue : function(path, root, defaultValue){
6284             path = path.replace(trimRe, "");
6285             if(!valueCache[path]){
6286                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6287             }
6288             var n = valueCache[path](root);
6289             n = n[0] ? n[0] : n;
6290             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6291             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6292         },
6293
6294         /**
6295          * Selects the value of a node, parsing integers and floats.
6296          * @param {String} selector The selector/xpath query
6297          * @param {Node} root (optional) The start of the query (defaults to document).
6298          * @param {Number} defaultValue
6299          * @return {Number}
6300          */
6301         selectNumber : function(path, root, defaultValue){
6302             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6303             return parseFloat(v);
6304         },
6305
6306         /**
6307          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6308          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6309          * @param {String} selector The simple selector to test
6310          * @return {Boolean}
6311          */
6312         is : function(el, ss){
6313             if(typeof el == "string"){
6314                 el = document.getElementById(el);
6315             }
6316             var isArray = (el instanceof Array);
6317             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6318             return isArray ? (result.length == el.length) : (result.length > 0);
6319         },
6320
6321         /**
6322          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6323          * @param {Array} el An array of elements to filter
6324          * @param {String} selector The simple selector to test
6325          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6326          * the selector instead of the ones that match
6327          * @return {Array}
6328          */
6329         filter : function(els, ss, nonMatches){
6330             ss = ss.replace(trimRe, "");
6331             if(!simpleCache[ss]){
6332                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6333             }
6334             var result = simpleCache[ss](els);
6335             return nonMatches ? quickDiff(result, els) : result;
6336         },
6337
6338         /**
6339          * Collection of matching regular expressions and code snippets.
6340          */
6341         matchers : [{
6342                 re: /^\.([\w-]+)/,
6343                 select: 'n = byClassName(n, null, " {1} ");'
6344             }, {
6345                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6346                 select: 'n = byPseudo(n, "{1}", "{2}");'
6347             },{
6348                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6349                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6350             }, {
6351                 re: /^#([\w-]+)/,
6352                 select: 'n = byId(n, null, "{1}");'
6353             },{
6354                 re: /^@([\w-]+)/,
6355                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6356             }
6357         ],
6358
6359         /**
6360          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6361          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
6362          */
6363         operators : {
6364             "=" : function(a, v){
6365                 return a == v;
6366             },
6367             "!=" : function(a, v){
6368                 return a != v;
6369             },
6370             "^=" : function(a, v){
6371                 return a && a.substr(0, v.length) == v;
6372             },
6373             "$=" : function(a, v){
6374                 return a && a.substr(a.length-v.length) == v;
6375             },
6376             "*=" : function(a, v){
6377                 return a && a.indexOf(v) !== -1;
6378             },
6379             "%=" : function(a, v){
6380                 return (a % v) == 0;
6381             },
6382             "|=" : function(a, v){
6383                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6384             },
6385             "~=" : function(a, v){
6386                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6387             }
6388         },
6389
6390         /**
6391          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6392          * and the argument (if any) supplied in the selector.
6393          */
6394         pseudos : {
6395             "first-child" : function(c){
6396                 var r = [], ri = -1, n;
6397                 for(var i = 0, ci; ci = n = c[i]; i++){
6398                     while((n = n.previousSibling) && n.nodeType != 1);
6399                     if(!n){
6400                         r[++ri] = ci;
6401                     }
6402                 }
6403                 return r;
6404             },
6405
6406             "last-child" : function(c){
6407                 var r = [], ri = -1, n;
6408                 for(var i = 0, ci; ci = n = c[i]; i++){
6409                     while((n = n.nextSibling) && n.nodeType != 1);
6410                     if(!n){
6411                         r[++ri] = ci;
6412                     }
6413                 }
6414                 return r;
6415             },
6416
6417             "nth-child" : function(c, a) {
6418                 var r = [], ri = -1;
6419                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6420                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6421                 for(var i = 0, n; n = c[i]; i++){
6422                     var pn = n.parentNode;
6423                     if (batch != pn._batch) {
6424                         var j = 0;
6425                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6426                             if(cn.nodeType == 1){
6427                                cn.nodeIndex = ++j;
6428                             }
6429                         }
6430                         pn._batch = batch;
6431                     }
6432                     if (f == 1) {
6433                         if (l == 0 || n.nodeIndex == l){
6434                             r[++ri] = n;
6435                         }
6436                     } else if ((n.nodeIndex + l) % f == 0){
6437                         r[++ri] = n;
6438                     }
6439                 }
6440
6441                 return r;
6442             },
6443
6444             "only-child" : function(c){
6445                 var r = [], ri = -1;;
6446                 for(var i = 0, ci; ci = c[i]; i++){
6447                     if(!prev(ci) && !next(ci)){
6448                         r[++ri] = ci;
6449                     }
6450                 }
6451                 return r;
6452             },
6453
6454             "empty" : function(c){
6455                 var r = [], ri = -1;
6456                 for(var i = 0, ci; ci = c[i]; i++){
6457                     var cns = ci.childNodes, j = 0, cn, empty = true;
6458                     while(cn = cns[j]){
6459                         ++j;
6460                         if(cn.nodeType == 1 || cn.nodeType == 3){
6461                             empty = false;
6462                             break;
6463                         }
6464                     }
6465                     if(empty){
6466                         r[++ri] = ci;
6467                     }
6468                 }
6469                 return r;
6470             },
6471
6472             "contains" : function(c, v){
6473                 var r = [], ri = -1;
6474                 for(var i = 0, ci; ci = c[i]; i++){
6475                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6476                         r[++ri] = ci;
6477                     }
6478                 }
6479                 return r;
6480             },
6481
6482             "nodeValue" : function(c, v){
6483                 var r = [], ri = -1;
6484                 for(var i = 0, ci; ci = c[i]; i++){
6485                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6486                         r[++ri] = ci;
6487                     }
6488                 }
6489                 return r;
6490             },
6491
6492             "checked" : function(c){
6493                 var r = [], ri = -1;
6494                 for(var i = 0, ci; ci = c[i]; i++){
6495                     if(ci.checked == true){
6496                         r[++ri] = ci;
6497                     }
6498                 }
6499                 return r;
6500             },
6501
6502             "not" : function(c, ss){
6503                 return Roo.DomQuery.filter(c, ss, true);
6504             },
6505
6506             "odd" : function(c){
6507                 return this["nth-child"](c, "odd");
6508             },
6509
6510             "even" : function(c){
6511                 return this["nth-child"](c, "even");
6512             },
6513
6514             "nth" : function(c, a){
6515                 return c[a-1] || [];
6516             },
6517
6518             "first" : function(c){
6519                 return c[0] || [];
6520             },
6521
6522             "last" : function(c){
6523                 return c[c.length-1] || [];
6524             },
6525
6526             "has" : function(c, ss){
6527                 var s = Roo.DomQuery.select;
6528                 var r = [], ri = -1;
6529                 for(var i = 0, ci; ci = c[i]; i++){
6530                     if(s(ss, ci).length > 0){
6531                         r[++ri] = ci;
6532                     }
6533                 }
6534                 return r;
6535             },
6536
6537             "next" : function(c, ss){
6538                 var is = Roo.DomQuery.is;
6539                 var r = [], ri = -1;
6540                 for(var i = 0, ci; ci = c[i]; i++){
6541                     var n = next(ci);
6542                     if(n && is(n, ss)){
6543                         r[++ri] = ci;
6544                     }
6545                 }
6546                 return r;
6547             },
6548
6549             "prev" : function(c, ss){
6550                 var is = Roo.DomQuery.is;
6551                 var r = [], ri = -1;
6552                 for(var i = 0, ci; ci = c[i]; i++){
6553                     var n = prev(ci);
6554                     if(n && is(n, ss)){
6555                         r[++ri] = ci;
6556                     }
6557                 }
6558                 return r;
6559             }
6560         }
6561     };
6562 }();
6563
6564 /**
6565  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6566  * @param {String} path The selector/xpath query
6567  * @param {Node} root (optional) The start of the query (defaults to document).
6568  * @return {Array}
6569  * @member Roo
6570  * @method query
6571  */
6572 Roo.query = Roo.DomQuery.select;
6573 /*
6574  * Based on:
6575  * Ext JS Library 1.1.1
6576  * Copyright(c) 2006-2007, Ext JS, LLC.
6577  *
6578  * Originally Released Under LGPL - original licence link has changed is not relivant.
6579  *
6580  * Fork - LGPL
6581  * <script type="text/javascript">
6582  */
6583
6584 /**
6585  * @class Roo.util.Observable
6586  * Base class that provides a common interface for publishing events. Subclasses are expected to
6587  * to have a property "events" with all the events defined.<br>
6588  * For example:
6589  * <pre><code>
6590  Employee = function(name){
6591     this.name = name;
6592     this.addEvents({
6593         "fired" : true,
6594         "quit" : true
6595     });
6596  }
6597  Roo.extend(Employee, Roo.util.Observable);
6598 </code></pre>
6599  * @param {Object} config properties to use (incuding events / listeners)
6600  */
6601
6602 Roo.util.Observable = function(cfg){
6603     
6604     cfg = cfg|| {};
6605     this.addEvents(cfg.events || {});
6606     if (cfg.events) {
6607         delete cfg.events; // make sure
6608     }
6609      
6610     Roo.apply(this, cfg);
6611     
6612     if(this.listeners){
6613         this.on(this.listeners);
6614         delete this.listeners;
6615     }
6616 };
6617 Roo.util.Observable.prototype = {
6618     /** 
6619  * @cfg {Object} listeners  list of events and functions to call for this object, 
6620  * For example :
6621  * <pre><code>
6622     listeners :  { 
6623        'click' : function(e) {
6624            ..... 
6625         } ,
6626         .... 
6627     } 
6628   </code></pre>
6629  */
6630     
6631     
6632     /**
6633      * Fires the specified event with the passed parameters (minus the event name).
6634      * @param {String} eventName
6635      * @param {Object...} args Variable number of parameters are passed to handlers
6636      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6637      */
6638     fireEvent : function(){
6639         var ce = this.events[arguments[0].toLowerCase()];
6640         if(typeof ce == "object"){
6641             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6642         }else{
6643             return true;
6644         }
6645     },
6646
6647     // private
6648     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6649
6650     /**
6651      * Appends an event handler to this component
6652      * @param {String}   eventName The type of event to listen for
6653      * @param {Function} handler The method the event invokes
6654      * @param {Object}   scope (optional) The scope in which to execute the handler
6655      * function. The handler function's "this" context.
6656      * @param {Object}   options (optional) An object containing handler configuration
6657      * properties. This may contain any of the following properties:<ul>
6658      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6659      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6660      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6661      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6662      * by the specified number of milliseconds. If the event fires again within that time, the original
6663      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6664      * </ul><br>
6665      * <p>
6666      * <b>Combining Options</b><br>
6667      * Using the options argument, it is possible to combine different types of listeners:<br>
6668      * <br>
6669      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6670                 <pre><code>
6671                 el.on('click', this.onClick, this, {
6672                         single: true,
6673                 delay: 100,
6674                 forumId: 4
6675                 });
6676                 </code></pre>
6677      * <p>
6678      * <b>Attaching multiple handlers in 1 call</b><br>
6679      * The method also allows for a single argument to be passed which is a config object containing properties
6680      * which specify multiple handlers.
6681      * <pre><code>
6682                 el.on({
6683                         'click': {
6684                         fn: this.onClick,
6685                         scope: this,
6686                         delay: 100
6687                 }, 
6688                 'mouseover': {
6689                         fn: this.onMouseOver,
6690                         scope: this
6691                 },
6692                 'mouseout': {
6693                         fn: this.onMouseOut,
6694                         scope: this
6695                 }
6696                 });
6697                 </code></pre>
6698      * <p>
6699      * Or a shorthand syntax which passes the same scope object to all handlers:
6700         <pre><code>
6701                 el.on({
6702                         'click': this.onClick,
6703                 'mouseover': this.onMouseOver,
6704                 'mouseout': this.onMouseOut,
6705                 scope: this
6706                 });
6707                 </code></pre>
6708      */
6709     addListener : function(eventName, fn, scope, o){
6710         if(typeof eventName == "object"){
6711             o = eventName;
6712             for(var e in o){
6713                 if(this.filterOptRe.test(e)){
6714                     continue;
6715                 }
6716                 if(typeof o[e] == "function"){
6717                     // shared options
6718                     this.addListener(e, o[e], o.scope,  o);
6719                 }else{
6720                     // individual options
6721                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6722                 }
6723             }
6724             return;
6725         }
6726         o = (!o || typeof o == "boolean") ? {} : o;
6727         eventName = eventName.toLowerCase();
6728         var ce = this.events[eventName] || true;
6729         if(typeof ce == "boolean"){
6730             ce = new Roo.util.Event(this, eventName);
6731             this.events[eventName] = ce;
6732         }
6733         ce.addListener(fn, scope, o);
6734     },
6735
6736     /**
6737      * Removes a listener
6738      * @param {String}   eventName     The type of event to listen for
6739      * @param {Function} handler        The handler to remove
6740      * @param {Object}   scope  (optional) The scope (this object) for the handler
6741      */
6742     removeListener : function(eventName, fn, scope){
6743         var ce = this.events[eventName.toLowerCase()];
6744         if(typeof ce == "object"){
6745             ce.removeListener(fn, scope);
6746         }
6747     },
6748
6749     /**
6750      * Removes all listeners for this object
6751      */
6752     purgeListeners : function(){
6753         for(var evt in this.events){
6754             if(typeof this.events[evt] == "object"){
6755                  this.events[evt].clearListeners();
6756             }
6757         }
6758     },
6759
6760     relayEvents : function(o, events){
6761         var createHandler = function(ename){
6762             return function(){
6763                  
6764                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6765             };
6766         };
6767         for(var i = 0, len = events.length; i < len; i++){
6768             var ename = events[i];
6769             if(!this.events[ename]){
6770                 this.events[ename] = true;
6771             };
6772             o.on(ename, createHandler(ename), this);
6773         }
6774     },
6775
6776     /**
6777      * Used to define events on this Observable
6778      * @param {Object} object The object with the events defined
6779      */
6780     addEvents : function(o){
6781         if(!this.events){
6782             this.events = {};
6783         }
6784         Roo.applyIf(this.events, o);
6785     },
6786
6787     /**
6788      * Checks to see if this object has any listeners for a specified event
6789      * @param {String} eventName The name of the event to check for
6790      * @return {Boolean} True if the event is being listened for, else false
6791      */
6792     hasListener : function(eventName){
6793         var e = this.events[eventName];
6794         return typeof e == "object" && e.listeners.length > 0;
6795     }
6796 };
6797 /**
6798  * Appends an event handler to this element (shorthand for addListener)
6799  * @param {String}   eventName     The type of event to listen for
6800  * @param {Function} handler        The method the event invokes
6801  * @param {Object}   scope (optional) The scope in which to execute the handler
6802  * function. The handler function's "this" context.
6803  * @param {Object}   options  (optional)
6804  * @method
6805  */
6806 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6807 /**
6808  * Removes a listener (shorthand for removeListener)
6809  * @param {String}   eventName     The type of event to listen for
6810  * @param {Function} handler        The handler to remove
6811  * @param {Object}   scope  (optional) The scope (this object) for the handler
6812  * @method
6813  */
6814 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6815
6816 /**
6817  * Starts capture on the specified Observable. All events will be passed
6818  * to the supplied function with the event name + standard signature of the event
6819  * <b>before</b> the event is fired. If the supplied function returns false,
6820  * the event will not fire.
6821  * @param {Observable} o The Observable to capture
6822  * @param {Function} fn The function to call
6823  * @param {Object} scope (optional) The scope (this object) for the fn
6824  * @static
6825  */
6826 Roo.util.Observable.capture = function(o, fn, scope){
6827     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6828 };
6829
6830 /**
6831  * Removes <b>all</b> added captures from the Observable.
6832  * @param {Observable} o The Observable to release
6833  * @static
6834  */
6835 Roo.util.Observable.releaseCapture = function(o){
6836     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6837 };
6838
6839 (function(){
6840
6841     var createBuffered = function(h, o, scope){
6842         var task = new Roo.util.DelayedTask();
6843         return function(){
6844             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6845         };
6846     };
6847
6848     var createSingle = function(h, e, fn, scope){
6849         return function(){
6850             e.removeListener(fn, scope);
6851             return h.apply(scope, arguments);
6852         };
6853     };
6854
6855     var createDelayed = function(h, o, scope){
6856         return function(){
6857             var args = Array.prototype.slice.call(arguments, 0);
6858             setTimeout(function(){
6859                 h.apply(scope, args);
6860             }, o.delay || 10);
6861         };
6862     };
6863
6864     Roo.util.Event = function(obj, name){
6865         this.name = name;
6866         this.obj = obj;
6867         this.listeners = [];
6868     };
6869
6870     Roo.util.Event.prototype = {
6871         addListener : function(fn, scope, options){
6872             var o = options || {};
6873             scope = scope || this.obj;
6874             if(!this.isListening(fn, scope)){
6875                 var l = {fn: fn, scope: scope, options: o};
6876                 var h = fn;
6877                 if(o.delay){
6878                     h = createDelayed(h, o, scope);
6879                 }
6880                 if(o.single){
6881                     h = createSingle(h, this, fn, scope);
6882                 }
6883                 if(o.buffer){
6884                     h = createBuffered(h, o, scope);
6885                 }
6886                 l.fireFn = h;
6887                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6888                     this.listeners.push(l);
6889                 }else{
6890                     this.listeners = this.listeners.slice(0);
6891                     this.listeners.push(l);
6892                 }
6893             }
6894         },
6895
6896         findListener : function(fn, scope){
6897             scope = scope || this.obj;
6898             var ls = this.listeners;
6899             for(var i = 0, len = ls.length; i < len; i++){
6900                 var l = ls[i];
6901                 if(l.fn == fn && l.scope == scope){
6902                     return i;
6903                 }
6904             }
6905             return -1;
6906         },
6907
6908         isListening : function(fn, scope){
6909             return this.findListener(fn, scope) != -1;
6910         },
6911
6912         removeListener : function(fn, scope){
6913             var index;
6914             if((index = this.findListener(fn, scope)) != -1){
6915                 if(!this.firing){
6916                     this.listeners.splice(index, 1);
6917                 }else{
6918                     this.listeners = this.listeners.slice(0);
6919                     this.listeners.splice(index, 1);
6920                 }
6921                 return true;
6922             }
6923             return false;
6924         },
6925
6926         clearListeners : function(){
6927             this.listeners = [];
6928         },
6929
6930         fire : function(){
6931             var ls = this.listeners, scope, len = ls.length;
6932             if(len > 0){
6933                 this.firing = true;
6934                 var args = Array.prototype.slice.call(arguments, 0);                
6935                 for(var i = 0; i < len; i++){
6936                     var l = ls[i];
6937                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6938                         this.firing = false;
6939                         return false;
6940                     }
6941                 }
6942                 this.firing = false;
6943             }
6944             return true;
6945         }
6946     };
6947 })();/*
6948  * RooJS Library 
6949  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6950  *
6951  * Licence LGPL 
6952  *
6953  */
6954  
6955 /**
6956  * @class Roo.Document
6957  * @extends Roo.util.Observable
6958  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6959  * 
6960  * @param {Object} config the methods and properties of the 'base' class for the application.
6961  * 
6962  *  Generic Page handler - implement this to start your app..
6963  * 
6964  * eg.
6965  *  MyProject = new Roo.Document({
6966         events : {
6967             'load' : true // your events..
6968         },
6969         listeners : {
6970             'ready' : function() {
6971                 // fired on Roo.onReady()
6972             }
6973         }
6974  * 
6975  */
6976 Roo.Document = function(cfg) {
6977      
6978     this.addEvents({ 
6979         'ready' : true
6980     });
6981     Roo.util.Observable.call(this,cfg);
6982     
6983     var _this = this;
6984     
6985     Roo.onReady(function() {
6986         _this.fireEvent('ready');
6987     },null,false);
6988     
6989     
6990 }
6991
6992 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6993  * Based on:
6994  * Ext JS Library 1.1.1
6995  * Copyright(c) 2006-2007, Ext JS, LLC.
6996  *
6997  * Originally Released Under LGPL - original licence link has changed is not relivant.
6998  *
6999  * Fork - LGPL
7000  * <script type="text/javascript">
7001  */
7002
7003 /**
7004  * @class Roo.EventManager
7005  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7006  * several useful events directly.
7007  * See {@link Roo.EventObject} for more details on normalized event objects.
7008  * @static
7009  */
7010 Roo.EventManager = function(){
7011     var docReadyEvent, docReadyProcId, docReadyState = false;
7012     var resizeEvent, resizeTask, textEvent, textSize;
7013     var E = Roo.lib.Event;
7014     var D = Roo.lib.Dom;
7015
7016     
7017     
7018
7019     var fireDocReady = function(){
7020         if(!docReadyState){
7021             docReadyState = true;
7022             Roo.isReady = true;
7023             if(docReadyProcId){
7024                 clearInterval(docReadyProcId);
7025             }
7026             if(Roo.isGecko || Roo.isOpera) {
7027                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7028             }
7029             if(Roo.isIE){
7030                 var defer = document.getElementById("ie-deferred-loader");
7031                 if(defer){
7032                     defer.onreadystatechange = null;
7033                     defer.parentNode.removeChild(defer);
7034                 }
7035             }
7036             if(docReadyEvent){
7037                 docReadyEvent.fire();
7038                 docReadyEvent.clearListeners();
7039             }
7040         }
7041     };
7042     
7043     var initDocReady = function(){
7044         docReadyEvent = new Roo.util.Event();
7045         if(Roo.isGecko || Roo.isOpera) {
7046             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7047         }else if(Roo.isIE){
7048             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7049             var defer = document.getElementById("ie-deferred-loader");
7050             defer.onreadystatechange = function(){
7051                 if(this.readyState == "complete"){
7052                     fireDocReady();
7053                 }
7054             };
7055         }else if(Roo.isSafari){ 
7056             docReadyProcId = setInterval(function(){
7057                 var rs = document.readyState;
7058                 if(rs == "complete") {
7059                     fireDocReady();     
7060                  }
7061             }, 10);
7062         }
7063         // no matter what, make sure it fires on load
7064         E.on(window, "load", fireDocReady);
7065     };
7066
7067     var createBuffered = function(h, o){
7068         var task = new Roo.util.DelayedTask(h);
7069         return function(e){
7070             // create new event object impl so new events don't wipe out properties
7071             e = new Roo.EventObjectImpl(e);
7072             task.delay(o.buffer, h, null, [e]);
7073         };
7074     };
7075
7076     var createSingle = function(h, el, ename, fn){
7077         return function(e){
7078             Roo.EventManager.removeListener(el, ename, fn);
7079             h(e);
7080         };
7081     };
7082
7083     var createDelayed = function(h, o){
7084         return function(e){
7085             // create new event object impl so new events don't wipe out properties
7086             e = new Roo.EventObjectImpl(e);
7087             setTimeout(function(){
7088                 h(e);
7089             }, o.delay || 10);
7090         };
7091     };
7092     var transitionEndVal = false;
7093     
7094     var transitionEnd = function()
7095     {
7096         if (transitionEndVal) {
7097             return transitionEndVal;
7098         }
7099         var el = document.createElement('div');
7100
7101         var transEndEventNames = {
7102             WebkitTransition : 'webkitTransitionEnd',
7103             MozTransition    : 'transitionend',
7104             OTransition      : 'oTransitionEnd otransitionend',
7105             transition       : 'transitionend'
7106         };
7107     
7108         for (var name in transEndEventNames) {
7109             if (el.style[name] !== undefined) {
7110                 transitionEndVal = transEndEventNames[name];
7111                 return  transitionEndVal ;
7112             }
7113         }
7114     }
7115     
7116   
7117
7118     var listen = function(element, ename, opt, fn, scope)
7119     {
7120         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7121         fn = fn || o.fn; scope = scope || o.scope;
7122         var el = Roo.getDom(element);
7123         
7124         
7125         if(!el){
7126             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7127         }
7128         
7129         if (ename == 'transitionend') {
7130             ename = transitionEnd();
7131         }
7132         var h = function(e){
7133             e = Roo.EventObject.setEvent(e);
7134             var t;
7135             if(o.delegate){
7136                 t = e.getTarget(o.delegate, el);
7137                 if(!t){
7138                     return;
7139                 }
7140             }else{
7141                 t = e.target;
7142             }
7143             if(o.stopEvent === true){
7144                 e.stopEvent();
7145             }
7146             if(o.preventDefault === true){
7147                e.preventDefault();
7148             }
7149             if(o.stopPropagation === true){
7150                 e.stopPropagation();
7151             }
7152
7153             if(o.normalized === false){
7154                 e = e.browserEvent;
7155             }
7156
7157             fn.call(scope || el, e, t, o);
7158         };
7159         if(o.delay){
7160             h = createDelayed(h, o);
7161         }
7162         if(o.single){
7163             h = createSingle(h, el, ename, fn);
7164         }
7165         if(o.buffer){
7166             h = createBuffered(h, o);
7167         }
7168         
7169         fn._handlers = fn._handlers || [];
7170         
7171         
7172         fn._handlers.push([Roo.id(el), ename, h]);
7173         
7174         
7175          
7176         E.on(el, ename, h); // this adds the actuall listener to the object..
7177         
7178         
7179         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7180             el.addEventListener("DOMMouseScroll", h, false);
7181             E.on(window, 'unload', function(){
7182                 el.removeEventListener("DOMMouseScroll", h, false);
7183             });
7184         }
7185         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7186             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7187         }
7188         return h;
7189     };
7190
7191     var stopListening = function(el, ename, fn){
7192         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7193         if(hds){
7194             for(var i = 0, len = hds.length; i < len; i++){
7195                 var h = hds[i];
7196                 if(h[0] == id && h[1] == ename){
7197                     hd = h[2];
7198                     hds.splice(i, 1);
7199                     break;
7200                 }
7201             }
7202         }
7203         E.un(el, ename, hd);
7204         el = Roo.getDom(el);
7205         if(ename == "mousewheel" && el.addEventListener){
7206             el.removeEventListener("DOMMouseScroll", hd, false);
7207         }
7208         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7209             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7210         }
7211     };
7212
7213     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7214     
7215     var pub = {
7216         
7217         
7218         /** 
7219          * Fix for doc tools
7220          * @scope Roo.EventManager
7221          */
7222         
7223         
7224         /** 
7225          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7226          * object with a Roo.EventObject
7227          * @param {Function} fn        The method the event invokes
7228          * @param {Object}   scope    An object that becomes the scope of the handler
7229          * @param {boolean}  override If true, the obj passed in becomes
7230          *                             the execution scope of the listener
7231          * @return {Function} The wrapped function
7232          * @deprecated
7233          */
7234         wrap : function(fn, scope, override){
7235             return function(e){
7236                 Roo.EventObject.setEvent(e);
7237                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7238             };
7239         },
7240         
7241         /**
7242      * Appends an event handler to an element (shorthand for addListener)
7243      * @param {String/HTMLElement}   element        The html element or id to assign the
7244      * @param {String}   eventName The type of event to listen for
7245      * @param {Function} handler The method the event invokes
7246      * @param {Object}   scope (optional) The scope in which to execute the handler
7247      * function. The handler function's "this" context.
7248      * @param {Object}   options (optional) An object containing handler configuration
7249      * properties. This may contain any of the following properties:<ul>
7250      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7251      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7252      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7253      * <li>preventDefault {Boolean} True to prevent the default action</li>
7254      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7255      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7256      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7257      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7258      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7259      * by the specified number of milliseconds. If the event fires again within that time, the original
7260      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7261      * </ul><br>
7262      * <p>
7263      * <b>Combining Options</b><br>
7264      * Using the options argument, it is possible to combine different types of listeners:<br>
7265      * <br>
7266      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7267      * Code:<pre><code>
7268 el.on('click', this.onClick, this, {
7269     single: true,
7270     delay: 100,
7271     stopEvent : true,
7272     forumId: 4
7273 });</code></pre>
7274      * <p>
7275      * <b>Attaching multiple handlers in 1 call</b><br>
7276       * The method also allows for a single argument to be passed which is a config object containing properties
7277      * which specify multiple handlers.
7278      * <p>
7279      * Code:<pre><code>
7280 el.on({
7281     'click' : {
7282         fn: this.onClick
7283         scope: this,
7284         delay: 100
7285     },
7286     'mouseover' : {
7287         fn: this.onMouseOver
7288         scope: this
7289     },
7290     'mouseout' : {
7291         fn: this.onMouseOut
7292         scope: this
7293     }
7294 });</code></pre>
7295      * <p>
7296      * Or a shorthand syntax:<br>
7297      * Code:<pre><code>
7298 el.on({
7299     'click' : this.onClick,
7300     'mouseover' : this.onMouseOver,
7301     'mouseout' : this.onMouseOut
7302     scope: this
7303 });</code></pre>
7304      */
7305         addListener : function(element, eventName, fn, scope, options){
7306             if(typeof eventName == "object"){
7307                 var o = eventName;
7308                 for(var e in o){
7309                     if(propRe.test(e)){
7310                         continue;
7311                     }
7312                     if(typeof o[e] == "function"){
7313                         // shared options
7314                         listen(element, e, o, o[e], o.scope);
7315                     }else{
7316                         // individual options
7317                         listen(element, e, o[e]);
7318                     }
7319                 }
7320                 return;
7321             }
7322             return listen(element, eventName, options, fn, scope);
7323         },
7324         
7325         /**
7326          * Removes an event handler
7327          *
7328          * @param {String/HTMLElement}   element        The id or html element to remove the 
7329          *                             event from
7330          * @param {String}   eventName     The type of event
7331          * @param {Function} fn
7332          * @return {Boolean} True if a listener was actually removed
7333          */
7334         removeListener : function(element, eventName, fn){
7335             return stopListening(element, eventName, fn);
7336         },
7337         
7338         /**
7339          * Fires when the document is ready (before onload and before images are loaded). Can be 
7340          * accessed shorthanded Roo.onReady().
7341          * @param {Function} fn        The method the event invokes
7342          * @param {Object}   scope    An  object that becomes the scope of the handler
7343          * @param {boolean}  options
7344          */
7345         onDocumentReady : function(fn, scope, options){
7346             if(docReadyState){ // if it already fired
7347                 docReadyEvent.addListener(fn, scope, options);
7348                 docReadyEvent.fire();
7349                 docReadyEvent.clearListeners();
7350                 return;
7351             }
7352             if(!docReadyEvent){
7353                 initDocReady();
7354             }
7355             docReadyEvent.addListener(fn, scope, options);
7356         },
7357         
7358         /**
7359          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7360          * @param {Function} fn        The method the event invokes
7361          * @param {Object}   scope    An object that becomes the scope of the handler
7362          * @param {boolean}  options
7363          */
7364         onWindowResize : function(fn, scope, options)
7365         {
7366             if(!resizeEvent){
7367                 resizeEvent = new Roo.util.Event();
7368                 resizeTask = new Roo.util.DelayedTask(function(){
7369                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7370                 });
7371                 E.on(window, "resize", function()
7372                 {
7373                     if (Roo.isIE) {
7374                         resizeTask.delay(50);
7375                     } else {
7376                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7377                     }
7378                 });
7379             }
7380             resizeEvent.addListener(fn, scope, options);
7381         },
7382
7383         /**
7384          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7385          * @param {Function} fn        The method the event invokes
7386          * @param {Object}   scope    An object that becomes the scope of the handler
7387          * @param {boolean}  options
7388          */
7389         onTextResize : function(fn, scope, options){
7390             if(!textEvent){
7391                 textEvent = new Roo.util.Event();
7392                 var textEl = new Roo.Element(document.createElement('div'));
7393                 textEl.dom.className = 'x-text-resize';
7394                 textEl.dom.innerHTML = 'X';
7395                 textEl.appendTo(document.body);
7396                 textSize = textEl.dom.offsetHeight;
7397                 setInterval(function(){
7398                     if(textEl.dom.offsetHeight != textSize){
7399                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7400                     }
7401                 }, this.textResizeInterval);
7402             }
7403             textEvent.addListener(fn, scope, options);
7404         },
7405
7406         /**
7407          * Removes the passed window resize listener.
7408          * @param {Function} fn        The method the event invokes
7409          * @param {Object}   scope    The scope of handler
7410          */
7411         removeResizeListener : function(fn, scope){
7412             if(resizeEvent){
7413                 resizeEvent.removeListener(fn, scope);
7414             }
7415         },
7416
7417         // private
7418         fireResize : function(){
7419             if(resizeEvent){
7420                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7421             }   
7422         },
7423         /**
7424          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7425          */
7426         ieDeferSrc : false,
7427         /**
7428          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7429          */
7430         textResizeInterval : 50
7431     };
7432     
7433     /**
7434      * Fix for doc tools
7435      * @scopeAlias pub=Roo.EventManager
7436      */
7437     
7438      /**
7439      * Appends an event handler to an element (shorthand for addListener)
7440      * @param {String/HTMLElement}   element        The html element or id to assign the
7441      * @param {String}   eventName The type of event to listen for
7442      * @param {Function} handler The method the event invokes
7443      * @param {Object}   scope (optional) The scope in which to execute the handler
7444      * function. The handler function's "this" context.
7445      * @param {Object}   options (optional) An object containing handler configuration
7446      * properties. This may contain any of the following properties:<ul>
7447      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7448      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7449      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7450      * <li>preventDefault {Boolean} True to prevent the default action</li>
7451      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7452      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7453      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7454      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7455      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7456      * by the specified number of milliseconds. If the event fires again within that time, the original
7457      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7458      * </ul><br>
7459      * <p>
7460      * <b>Combining Options</b><br>
7461      * Using the options argument, it is possible to combine different types of listeners:<br>
7462      * <br>
7463      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7464      * Code:<pre><code>
7465 el.on('click', this.onClick, this, {
7466     single: true,
7467     delay: 100,
7468     stopEvent : true,
7469     forumId: 4
7470 });</code></pre>
7471      * <p>
7472      * <b>Attaching multiple handlers in 1 call</b><br>
7473       * The method also allows for a single argument to be passed which is a config object containing properties
7474      * which specify multiple handlers.
7475      * <p>
7476      * Code:<pre><code>
7477 el.on({
7478     'click' : {
7479         fn: this.onClick
7480         scope: this,
7481         delay: 100
7482     },
7483     'mouseover' : {
7484         fn: this.onMouseOver
7485         scope: this
7486     },
7487     'mouseout' : {
7488         fn: this.onMouseOut
7489         scope: this
7490     }
7491 });</code></pre>
7492      * <p>
7493      * Or a shorthand syntax:<br>
7494      * Code:<pre><code>
7495 el.on({
7496     'click' : this.onClick,
7497     'mouseover' : this.onMouseOver,
7498     'mouseout' : this.onMouseOut
7499     scope: this
7500 });</code></pre>
7501      */
7502     pub.on = pub.addListener;
7503     pub.un = pub.removeListener;
7504
7505     pub.stoppedMouseDownEvent = new Roo.util.Event();
7506     return pub;
7507 }();
7508 /**
7509   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7510   * @param {Function} fn        The method the event invokes
7511   * @param {Object}   scope    An  object that becomes the scope of the handler
7512   * @param {boolean}  override If true, the obj passed in becomes
7513   *                             the execution scope of the listener
7514   * @member Roo
7515   * @method onReady
7516  */
7517 Roo.onReady = Roo.EventManager.onDocumentReady;
7518
7519 Roo.onReady(function(){
7520     var bd = Roo.get(document.body);
7521     if(!bd){ return; }
7522
7523     var cls = [
7524             Roo.isIE ? "roo-ie"
7525             : Roo.isIE11 ? "roo-ie11"
7526             : Roo.isEdge ? "roo-edge"
7527             : Roo.isGecko ? "roo-gecko"
7528             : Roo.isOpera ? "roo-opera"
7529             : Roo.isSafari ? "roo-safari" : ""];
7530
7531     if(Roo.isMac){
7532         cls.push("roo-mac");
7533     }
7534     if(Roo.isLinux){
7535         cls.push("roo-linux");
7536     }
7537     if(Roo.isIOS){
7538         cls.push("roo-ios");
7539     }
7540     if(Roo.isTouch){
7541         cls.push("roo-touch");
7542     }
7543     if(Roo.isBorderBox){
7544         cls.push('roo-border-box');
7545     }
7546     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7547         var p = bd.dom.parentNode;
7548         if(p){
7549             p.className += ' roo-strict';
7550         }
7551     }
7552     bd.addClass(cls.join(' '));
7553 });
7554
7555 /**
7556  * @class Roo.EventObject
7557  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7558  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7559  * Example:
7560  * <pre><code>
7561  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7562     e.preventDefault();
7563     var target = e.getTarget();
7564     ...
7565  }
7566  var myDiv = Roo.get("myDiv");
7567  myDiv.on("click", handleClick);
7568  //or
7569  Roo.EventManager.on("myDiv", 'click', handleClick);
7570  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7571  </code></pre>
7572  * @static
7573  */
7574 Roo.EventObject = function(){
7575     
7576     var E = Roo.lib.Event;
7577     
7578     // safari keypress events for special keys return bad keycodes
7579     var safariKeys = {
7580         63234 : 37, // left
7581         63235 : 39, // right
7582         63232 : 38, // up
7583         63233 : 40, // down
7584         63276 : 33, // page up
7585         63277 : 34, // page down
7586         63272 : 46, // delete
7587         63273 : 36, // home
7588         63275 : 35  // end
7589     };
7590
7591     // normalize button clicks
7592     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7593                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7594
7595     Roo.EventObjectImpl = function(e){
7596         if(e){
7597             this.setEvent(e.browserEvent || e);
7598         }
7599     };
7600     Roo.EventObjectImpl.prototype = {
7601         /**
7602          * Used to fix doc tools.
7603          * @scope Roo.EventObject.prototype
7604          */
7605             
7606
7607         
7608         
7609         /** The normal browser event */
7610         browserEvent : null,
7611         /** The button pressed in a mouse event */
7612         button : -1,
7613         /** True if the shift key was down during the event */
7614         shiftKey : false,
7615         /** True if the control key was down during the event */
7616         ctrlKey : false,
7617         /** True if the alt key was down during the event */
7618         altKey : false,
7619
7620         /** Key constant 
7621         * @type Number */
7622         BACKSPACE : 8,
7623         /** Key constant 
7624         * @type Number */
7625         TAB : 9,
7626         /** Key constant 
7627         * @type Number */
7628         RETURN : 13,
7629         /** Key constant 
7630         * @type Number */
7631         ENTER : 13,
7632         /** Key constant 
7633         * @type Number */
7634         SHIFT : 16,
7635         /** Key constant 
7636         * @type Number */
7637         CONTROL : 17,
7638         /** Key constant 
7639         * @type Number */
7640         ESC : 27,
7641         /** Key constant 
7642         * @type Number */
7643         SPACE : 32,
7644         /** Key constant 
7645         * @type Number */
7646         PAGEUP : 33,
7647         /** Key constant 
7648         * @type Number */
7649         PAGEDOWN : 34,
7650         /** Key constant 
7651         * @type Number */
7652         END : 35,
7653         /** Key constant 
7654         * @type Number */
7655         HOME : 36,
7656         /** Key constant 
7657         * @type Number */
7658         LEFT : 37,
7659         /** Key constant 
7660         * @type Number */
7661         UP : 38,
7662         /** Key constant 
7663         * @type Number */
7664         RIGHT : 39,
7665         /** Key constant 
7666         * @type Number */
7667         DOWN : 40,
7668         /** Key constant 
7669         * @type Number */
7670         DELETE : 46,
7671         /** Key constant 
7672         * @type Number */
7673         F5 : 116,
7674
7675            /** @private */
7676         setEvent : function(e){
7677             if(e == this || (e && e.browserEvent)){ // already wrapped
7678                 return e;
7679             }
7680             this.browserEvent = e;
7681             if(e){
7682                 // normalize buttons
7683                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7684                 if(e.type == 'click' && this.button == -1){
7685                     this.button = 0;
7686                 }
7687                 this.type = e.type;
7688                 this.shiftKey = e.shiftKey;
7689                 // mac metaKey behaves like ctrlKey
7690                 this.ctrlKey = e.ctrlKey || e.metaKey;
7691                 this.altKey = e.altKey;
7692                 // in getKey these will be normalized for the mac
7693                 this.keyCode = e.keyCode;
7694                 // keyup warnings on firefox.
7695                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7696                 // cache the target for the delayed and or buffered events
7697                 this.target = E.getTarget(e);
7698                 // same for XY
7699                 this.xy = E.getXY(e);
7700             }else{
7701                 this.button = -1;
7702                 this.shiftKey = false;
7703                 this.ctrlKey = false;
7704                 this.altKey = false;
7705                 this.keyCode = 0;
7706                 this.charCode =0;
7707                 this.target = null;
7708                 this.xy = [0, 0];
7709             }
7710             return this;
7711         },
7712
7713         /**
7714          * Stop the event (preventDefault and stopPropagation)
7715          */
7716         stopEvent : function(){
7717             if(this.browserEvent){
7718                 if(this.browserEvent.type == 'mousedown'){
7719                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7720                 }
7721                 E.stopEvent(this.browserEvent);
7722             }
7723         },
7724
7725         /**
7726          * Prevents the browsers default handling of the event.
7727          */
7728         preventDefault : function(){
7729             if(this.browserEvent){
7730                 E.preventDefault(this.browserEvent);
7731             }
7732         },
7733
7734         /** @private */
7735         isNavKeyPress : function(){
7736             var k = this.keyCode;
7737             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7738             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7739         },
7740
7741         isSpecialKey : function(){
7742             var k = this.keyCode;
7743             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7744             (k == 16) || (k == 17) ||
7745             (k >= 18 && k <= 20) ||
7746             (k >= 33 && k <= 35) ||
7747             (k >= 36 && k <= 39) ||
7748             (k >= 44 && k <= 45);
7749         },
7750         /**
7751          * Cancels bubbling of the event.
7752          */
7753         stopPropagation : function(){
7754             if(this.browserEvent){
7755                 if(this.type == 'mousedown'){
7756                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7757                 }
7758                 E.stopPropagation(this.browserEvent);
7759             }
7760         },
7761
7762         /**
7763          * Gets the key code for the event.
7764          * @return {Number}
7765          */
7766         getCharCode : function(){
7767             return this.charCode || this.keyCode;
7768         },
7769
7770         /**
7771          * Returns a normalized keyCode for the event.
7772          * @return {Number} The key code
7773          */
7774         getKey : function(){
7775             var k = this.keyCode || this.charCode;
7776             return Roo.isSafari ? (safariKeys[k] || k) : k;
7777         },
7778
7779         /**
7780          * Gets the x coordinate of the event.
7781          * @return {Number}
7782          */
7783         getPageX : function(){
7784             return this.xy[0];
7785         },
7786
7787         /**
7788          * Gets the y coordinate of the event.
7789          * @return {Number}
7790          */
7791         getPageY : function(){
7792             return this.xy[1];
7793         },
7794
7795         /**
7796          * Gets the time of the event.
7797          * @return {Number}
7798          */
7799         getTime : function(){
7800             if(this.browserEvent){
7801                 return E.getTime(this.browserEvent);
7802             }
7803             return null;
7804         },
7805
7806         /**
7807          * Gets the page coordinates of the event.
7808          * @return {Array} The xy values like [x, y]
7809          */
7810         getXY : function(){
7811             return this.xy;
7812         },
7813
7814         /**
7815          * Gets the target for the event.
7816          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7817          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7818                 search as a number or element (defaults to 10 || document.body)
7819          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7820          * @return {HTMLelement}
7821          */
7822         getTarget : function(selector, maxDepth, returnEl){
7823             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7824         },
7825         /**
7826          * Gets the related target.
7827          * @return {HTMLElement}
7828          */
7829         getRelatedTarget : function(){
7830             if(this.browserEvent){
7831                 return E.getRelatedTarget(this.browserEvent);
7832             }
7833             return null;
7834         },
7835
7836         /**
7837          * Normalizes mouse wheel delta across browsers
7838          * @return {Number} The delta
7839          */
7840         getWheelDelta : function(){
7841             var e = this.browserEvent;
7842             var delta = 0;
7843             if(e.wheelDelta){ /* IE/Opera. */
7844                 delta = e.wheelDelta/120;
7845             }else if(e.detail){ /* Mozilla case. */
7846                 delta = -e.detail/3;
7847             }
7848             return delta;
7849         },
7850
7851         /**
7852          * Returns true if the control, meta, shift or alt key was pressed during this event.
7853          * @return {Boolean}
7854          */
7855         hasModifier : function(){
7856             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7857         },
7858
7859         /**
7860          * Returns true if the target of this event equals el or is a child of el
7861          * @param {String/HTMLElement/Element} el
7862          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7863          * @return {Boolean}
7864          */
7865         within : function(el, related){
7866             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7867             return t && Roo.fly(el).contains(t);
7868         },
7869
7870         getPoint : function(){
7871             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7872         }
7873     };
7874
7875     return new Roo.EventObjectImpl();
7876 }();
7877             
7878     /*
7879  * Based on:
7880  * Ext JS Library 1.1.1
7881  * Copyright(c) 2006-2007, Ext JS, LLC.
7882  *
7883  * Originally Released Under LGPL - original licence link has changed is not relivant.
7884  *
7885  * Fork - LGPL
7886  * <script type="text/javascript">
7887  */
7888
7889  
7890 // was in Composite Element!??!?!
7891  
7892 (function(){
7893     var D = Roo.lib.Dom;
7894     var E = Roo.lib.Event;
7895     var A = Roo.lib.Anim;
7896
7897     // local style camelizing for speed
7898     var propCache = {};
7899     var camelRe = /(-[a-z])/gi;
7900     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7901     var view = document.defaultView;
7902
7903 /**
7904  * @class Roo.Element
7905  * Represents an Element in the DOM.<br><br>
7906  * Usage:<br>
7907 <pre><code>
7908 var el = Roo.get("my-div");
7909
7910 // or with getEl
7911 var el = getEl("my-div");
7912
7913 // or with a DOM element
7914 var el = Roo.get(myDivElement);
7915 </code></pre>
7916  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7917  * each call instead of constructing a new one.<br><br>
7918  * <b>Animations</b><br />
7919  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7920  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7921 <pre>
7922 Option    Default   Description
7923 --------- --------  ---------------------------------------------
7924 duration  .35       The duration of the animation in seconds
7925 easing    easeOut   The YUI easing method
7926 callback  none      A function to execute when the anim completes
7927 scope     this      The scope (this) of the callback function
7928 </pre>
7929 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7930 * manipulate the animation. Here's an example:
7931 <pre><code>
7932 var el = Roo.get("my-div");
7933
7934 // no animation
7935 el.setWidth(100);
7936
7937 // default animation
7938 el.setWidth(100, true);
7939
7940 // animation with some options set
7941 el.setWidth(100, {
7942     duration: 1,
7943     callback: this.foo,
7944     scope: this
7945 });
7946
7947 // using the "anim" property to get the Anim object
7948 var opt = {
7949     duration: 1,
7950     callback: this.foo,
7951     scope: this
7952 };
7953 el.setWidth(100, opt);
7954 ...
7955 if(opt.anim.isAnimated()){
7956     opt.anim.stop();
7957 }
7958 </code></pre>
7959 * <b> Composite (Collections of) Elements</b><br />
7960  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7961  * @constructor Create a new Element directly.
7962  * @param {String/HTMLElement} element
7963  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7964  */
7965     Roo.Element = function(element, forceNew)
7966     {
7967         var dom = typeof element == "string" ?
7968                 document.getElementById(element) : element;
7969         
7970         this.listeners = {};
7971         
7972         if(!dom){ // invalid id/element
7973             return null;
7974         }
7975         var id = dom.id;
7976         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7977             return Roo.Element.cache[id];
7978         }
7979
7980         /**
7981          * The DOM element
7982          * @type HTMLElement
7983          */
7984         this.dom = dom;
7985
7986         /**
7987          * The DOM element ID
7988          * @type String
7989          */
7990         this.id = id || Roo.id(dom);
7991         
7992         return this; // assumed for cctor?
7993     };
7994
7995     var El = Roo.Element;
7996
7997     El.prototype = {
7998         /**
7999          * The element's default display mode  (defaults to "") 
8000          * @type String
8001          */
8002         originalDisplay : "",
8003
8004         
8005         // note this is overridden in BS version..
8006         visibilityMode : 1, 
8007         /**
8008          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8009          * @type String
8010          */
8011         defaultUnit : "px",
8012         
8013         /**
8014          * Sets the element's visibility mode. When setVisible() is called it
8015          * will use this to determine whether to set the visibility or the display property.
8016          * @param visMode Element.VISIBILITY or Element.DISPLAY
8017          * @return {Roo.Element} this
8018          */
8019         setVisibilityMode : function(visMode){
8020             this.visibilityMode = visMode;
8021             return this;
8022         },
8023         /**
8024          * Convenience method for setVisibilityMode(Element.DISPLAY)
8025          * @param {String} display (optional) What to set display to when visible
8026          * @return {Roo.Element} this
8027          */
8028         enableDisplayMode : function(display){
8029             this.setVisibilityMode(El.DISPLAY);
8030             if(typeof display != "undefined") { this.originalDisplay = display; }
8031             return this;
8032         },
8033
8034         /**
8035          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8036          * @param {String} selector The simple selector to test
8037          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8038                 search as a number or element (defaults to 10 || document.body)
8039          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8040          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8041          */
8042         findParent : function(simpleSelector, maxDepth, returnEl){
8043             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8044             maxDepth = maxDepth || 50;
8045             if(typeof maxDepth != "number"){
8046                 stopEl = Roo.getDom(maxDepth);
8047                 maxDepth = 10;
8048             }
8049             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8050                 if(dq.is(p, simpleSelector)){
8051                     return returnEl ? Roo.get(p) : p;
8052                 }
8053                 depth++;
8054                 p = p.parentNode;
8055             }
8056             return null;
8057         },
8058
8059
8060         /**
8061          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8062          * @param {String} selector The simple selector to test
8063          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8064                 search as a number or element (defaults to 10 || document.body)
8065          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8066          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8067          */
8068         findParentNode : function(simpleSelector, maxDepth, returnEl){
8069             var p = Roo.fly(this.dom.parentNode, '_internal');
8070             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8071         },
8072         
8073         /**
8074          * Looks at  the scrollable parent element
8075          */
8076         findScrollableParent : function()
8077         {
8078             var overflowRegex = /(auto|scroll)/;
8079             
8080             if(this.getStyle('position') === 'fixed'){
8081                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8082             }
8083             
8084             var excludeStaticParent = this.getStyle('position') === "absolute";
8085             
8086             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8087                 
8088                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8089                     continue;
8090                 }
8091                 
8092                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8093                     return parent;
8094                 }
8095                 
8096                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8097                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8098                 }
8099             }
8100             
8101             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8102         },
8103
8104         /**
8105          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8106          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8107          * @param {String} selector The simple selector to test
8108          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8109                 search as a number or element (defaults to 10 || document.body)
8110          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8111          */
8112         up : function(simpleSelector, maxDepth){
8113             return this.findParentNode(simpleSelector, maxDepth, true);
8114         },
8115
8116
8117
8118         /**
8119          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8120          * @param {String} selector The simple selector to test
8121          * @return {Boolean} True if this element matches the selector, else false
8122          */
8123         is : function(simpleSelector){
8124             return Roo.DomQuery.is(this.dom, simpleSelector);
8125         },
8126
8127         /**
8128          * Perform animation on this element.
8129          * @param {Object} args The YUI animation control args
8130          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8131          * @param {Function} onComplete (optional) Function to call when animation completes
8132          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8133          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8134          * @return {Roo.Element} this
8135          */
8136         animate : function(args, duration, onComplete, easing, animType){
8137             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8138             return this;
8139         },
8140
8141         /*
8142          * @private Internal animation call
8143          */
8144         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8145             animType = animType || 'run';
8146             opt = opt || {};
8147             var anim = Roo.lib.Anim[animType](
8148                 this.dom, args,
8149                 (opt.duration || defaultDur) || .35,
8150                 (opt.easing || defaultEase) || 'easeOut',
8151                 function(){
8152                     Roo.callback(cb, this);
8153                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8154                 },
8155                 this
8156             );
8157             opt.anim = anim;
8158             return anim;
8159         },
8160
8161         // private legacy anim prep
8162         preanim : function(a, i){
8163             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8164         },
8165
8166         /**
8167          * Removes worthless text nodes
8168          * @param {Boolean} forceReclean (optional) By default the element
8169          * keeps track if it has been cleaned already so
8170          * you can call this over and over. However, if you update the element and
8171          * need to force a reclean, you can pass true.
8172          */
8173         clean : function(forceReclean){
8174             if(this.isCleaned && forceReclean !== true){
8175                 return this;
8176             }
8177             var ns = /\S/;
8178             var d = this.dom, n = d.firstChild, ni = -1;
8179             while(n){
8180                 var nx = n.nextSibling;
8181                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8182                     d.removeChild(n);
8183                 }else{
8184                     n.nodeIndex = ++ni;
8185                 }
8186                 n = nx;
8187             }
8188             this.isCleaned = true;
8189             return this;
8190         },
8191
8192         // private
8193         calcOffsetsTo : function(el){
8194             el = Roo.get(el);
8195             var d = el.dom;
8196             var restorePos = false;
8197             if(el.getStyle('position') == 'static'){
8198                 el.position('relative');
8199                 restorePos = true;
8200             }
8201             var x = 0, y =0;
8202             var op = this.dom;
8203             while(op && op != d && op.tagName != 'HTML'){
8204                 x+= op.offsetLeft;
8205                 y+= op.offsetTop;
8206                 op = op.offsetParent;
8207             }
8208             if(restorePos){
8209                 el.position('static');
8210             }
8211             return [x, y];
8212         },
8213
8214         /**
8215          * Scrolls this element into view within the passed container.
8216          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8217          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8218          * @return {Roo.Element} this
8219          */
8220         scrollIntoView : function(container, hscroll){
8221             var c = Roo.getDom(container) || document.body;
8222             var el = this.dom;
8223
8224             var o = this.calcOffsetsTo(c),
8225                 l = o[0],
8226                 t = o[1],
8227                 b = t+el.offsetHeight,
8228                 r = l+el.offsetWidth;
8229
8230             var ch = c.clientHeight;
8231             var ct = parseInt(c.scrollTop, 10);
8232             var cl = parseInt(c.scrollLeft, 10);
8233             var cb = ct + ch;
8234             var cr = cl + c.clientWidth;
8235
8236             if(t < ct){
8237                 c.scrollTop = t;
8238             }else if(b > cb){
8239                 c.scrollTop = b-ch;
8240             }
8241
8242             if(hscroll !== false){
8243                 if(l < cl){
8244                     c.scrollLeft = l;
8245                 }else if(r > cr){
8246                     c.scrollLeft = r-c.clientWidth;
8247                 }
8248             }
8249             return this;
8250         },
8251
8252         // private
8253         scrollChildIntoView : function(child, hscroll){
8254             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8255         },
8256
8257         /**
8258          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8259          * the new height may not be available immediately.
8260          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8261          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8262          * @param {Function} onComplete (optional) Function to call when animation completes
8263          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8264          * @return {Roo.Element} this
8265          */
8266         autoHeight : function(animate, duration, onComplete, easing){
8267             var oldHeight = this.getHeight();
8268             this.clip();
8269             this.setHeight(1); // force clipping
8270             setTimeout(function(){
8271                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8272                 if(!animate){
8273                     this.setHeight(height);
8274                     this.unclip();
8275                     if(typeof onComplete == "function"){
8276                         onComplete();
8277                     }
8278                 }else{
8279                     this.setHeight(oldHeight); // restore original height
8280                     this.setHeight(height, animate, duration, function(){
8281                         this.unclip();
8282                         if(typeof onComplete == "function") { onComplete(); }
8283                     }.createDelegate(this), easing);
8284                 }
8285             }.createDelegate(this), 0);
8286             return this;
8287         },
8288
8289         /**
8290          * Returns true if this element is an ancestor of the passed element
8291          * @param {HTMLElement/String} el The element to check
8292          * @return {Boolean} True if this element is an ancestor of el, else false
8293          */
8294         contains : function(el){
8295             if(!el){return false;}
8296             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8297         },
8298
8299         /**
8300          * Checks whether the element is currently visible using both visibility and display properties.
8301          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8302          * @return {Boolean} True if the element is currently visible, else false
8303          */
8304         isVisible : function(deep) {
8305             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8306             if(deep !== true || !vis){
8307                 return vis;
8308             }
8309             var p = this.dom.parentNode;
8310             while(p && p.tagName.toLowerCase() != "body"){
8311                 if(!Roo.fly(p, '_isVisible').isVisible()){
8312                     return false;
8313                 }
8314                 p = p.parentNode;
8315             }
8316             return true;
8317         },
8318
8319         /**
8320          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8321          * @param {String} selector The CSS selector
8322          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8323          * @return {CompositeElement/CompositeElementLite} The composite element
8324          */
8325         select : function(selector, unique){
8326             return El.select(selector, unique, this.dom);
8327         },
8328
8329         /**
8330          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8331          * @param {String} selector The CSS selector
8332          * @return {Array} An array of the matched nodes
8333          */
8334         query : function(selector, unique){
8335             return Roo.DomQuery.select(selector, this.dom);
8336         },
8337
8338         /**
8339          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8340          * @param {String} selector The CSS selector
8341          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8342          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8343          */
8344         child : function(selector, returnDom){
8345             var n = Roo.DomQuery.selectNode(selector, this.dom);
8346             return returnDom ? n : Roo.get(n);
8347         },
8348
8349         /**
8350          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8351          * @param {String} selector The CSS selector
8352          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8353          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8354          */
8355         down : function(selector, returnDom){
8356             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8357             return returnDom ? n : Roo.get(n);
8358         },
8359
8360         /**
8361          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8362          * @param {String} group The group the DD object is member of
8363          * @param {Object} config The DD config object
8364          * @param {Object} overrides An object containing methods to override/implement on the DD object
8365          * @return {Roo.dd.DD} The DD object
8366          */
8367         initDD : function(group, config, overrides){
8368             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8369             return Roo.apply(dd, overrides);
8370         },
8371
8372         /**
8373          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8374          * @param {String} group The group the DDProxy object is member of
8375          * @param {Object} config The DDProxy config object
8376          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8377          * @return {Roo.dd.DDProxy} The DDProxy object
8378          */
8379         initDDProxy : function(group, config, overrides){
8380             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8381             return Roo.apply(dd, overrides);
8382         },
8383
8384         /**
8385          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8386          * @param {String} group The group the DDTarget object is member of
8387          * @param {Object} config The DDTarget config object
8388          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8389          * @return {Roo.dd.DDTarget} The DDTarget object
8390          */
8391         initDDTarget : function(group, config, overrides){
8392             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8393             return Roo.apply(dd, overrides);
8394         },
8395
8396         /**
8397          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8398          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8399          * @param {Boolean} visible Whether the element is visible
8400          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8401          * @return {Roo.Element} this
8402          */
8403          setVisible : function(visible, animate){
8404             if(!animate || !A){
8405                 if(this.visibilityMode == El.DISPLAY){
8406                     this.setDisplayed(visible);
8407                 }else{
8408                     this.fixDisplay();
8409                     this.dom.style.visibility = visible ? "visible" : "hidden";
8410                 }
8411             }else{
8412                 // closure for composites
8413                 var dom = this.dom;
8414                 var visMode = this.visibilityMode;
8415                 if(visible){
8416                     this.setOpacity(.01);
8417                     this.setVisible(true);
8418                 }
8419                 this.anim({opacity: { to: (visible?1:0) }},
8420                       this.preanim(arguments, 1),
8421                       null, .35, 'easeIn', function(){
8422                          if(!visible){
8423                              if(visMode == El.DISPLAY){
8424                                  dom.style.display = "none";
8425                              }else{
8426                                  dom.style.visibility = "hidden";
8427                              }
8428                              Roo.get(dom).setOpacity(1);
8429                          }
8430                      });
8431             }
8432             return this;
8433         },
8434
8435         /**
8436          * Returns true if display is not "none"
8437          * @return {Boolean}
8438          */
8439         isDisplayed : function() {
8440             return this.getStyle("display") != "none";
8441         },
8442
8443         /**
8444          * Toggles the element's visibility or display, depending on visibility mode.
8445          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8446          * @return {Roo.Element} this
8447          */
8448         toggle : function(animate){
8449             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8450             return this;
8451         },
8452
8453         /**
8454          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8455          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8456          * @return {Roo.Element} this
8457          */
8458         setDisplayed : function(value) {
8459             if(typeof value == "boolean"){
8460                value = value ? this.originalDisplay : "none";
8461             }
8462             this.setStyle("display", value);
8463             return this;
8464         },
8465
8466         /**
8467          * Tries to focus the element. Any exceptions are caught and ignored.
8468          * @return {Roo.Element} this
8469          */
8470         focus : function() {
8471             try{
8472                 this.dom.focus();
8473             }catch(e){}
8474             return this;
8475         },
8476
8477         /**
8478          * Tries to blur the element. Any exceptions are caught and ignored.
8479          * @return {Roo.Element} this
8480          */
8481         blur : function() {
8482             try{
8483                 this.dom.blur();
8484             }catch(e){}
8485             return this;
8486         },
8487
8488         /**
8489          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8490          * @param {String/Array} className The CSS class to add, or an array of classes
8491          * @return {Roo.Element} this
8492          */
8493         addClass : function(className){
8494             if(className instanceof Array){
8495                 for(var i = 0, len = className.length; i < len; i++) {
8496                     this.addClass(className[i]);
8497                 }
8498             }else{
8499                 if(className && !this.hasClass(className)){
8500                     if (this.dom instanceof SVGElement) {
8501                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8502                     } else {
8503                         this.dom.className = this.dom.className + " " + className;
8504                     }
8505                 }
8506             }
8507             return this;
8508         },
8509
8510         /**
8511          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8512          * @param {String/Array} className The CSS class to add, or an array of classes
8513          * @return {Roo.Element} this
8514          */
8515         radioClass : function(className){
8516             var siblings = this.dom.parentNode.childNodes;
8517             for(var i = 0; i < siblings.length; i++) {
8518                 var s = siblings[i];
8519                 if(s.nodeType == 1){
8520                     Roo.get(s).removeClass(className);
8521                 }
8522             }
8523             this.addClass(className);
8524             return this;
8525         },
8526
8527         /**
8528          * Removes one or more CSS classes from the element.
8529          * @param {String/Array} className The CSS class to remove, or an array of classes
8530          * @return {Roo.Element} this
8531          */
8532         removeClass : function(className){
8533             
8534             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8535             if(!className || !cn){
8536                 return this;
8537             }
8538             if(className instanceof Array){
8539                 for(var i = 0, len = className.length; i < len; i++) {
8540                     this.removeClass(className[i]);
8541                 }
8542             }else{
8543                 if(this.hasClass(className)){
8544                     var re = this.classReCache[className];
8545                     if (!re) {
8546                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8547                        this.classReCache[className] = re;
8548                     }
8549                     if (this.dom instanceof SVGElement) {
8550                         this.dom.className.baseVal = cn.replace(re, " ");
8551                     } else {
8552                         this.dom.className = cn.replace(re, " ");
8553                     }
8554                 }
8555             }
8556             return this;
8557         },
8558
8559         // private
8560         classReCache: {},
8561
8562         /**
8563          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8564          * @param {String} className The CSS class to toggle
8565          * @return {Roo.Element} this
8566          */
8567         toggleClass : function(className){
8568             if(this.hasClass(className)){
8569                 this.removeClass(className);
8570             }else{
8571                 this.addClass(className);
8572             }
8573             return this;
8574         },
8575
8576         /**
8577          * Checks if the specified CSS class exists on this element's DOM node.
8578          * @param {String} className The CSS class to check for
8579          * @return {Boolean} True if the class exists, else false
8580          */
8581         hasClass : function(className){
8582             if (this.dom instanceof SVGElement) {
8583                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8584             } 
8585             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8586         },
8587
8588         /**
8589          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8590          * @param {String} oldClassName The CSS class to replace
8591          * @param {String} newClassName The replacement CSS class
8592          * @return {Roo.Element} this
8593          */
8594         replaceClass : function(oldClassName, newClassName){
8595             this.removeClass(oldClassName);
8596             this.addClass(newClassName);
8597             return this;
8598         },
8599
8600         /**
8601          * Returns an object with properties matching the styles requested.
8602          * For example, el.getStyles('color', 'font-size', 'width') might return
8603          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8604          * @param {String} style1 A style name
8605          * @param {String} style2 A style name
8606          * @param {String} etc.
8607          * @return {Object} The style object
8608          */
8609         getStyles : function(){
8610             var a = arguments, len = a.length, r = {};
8611             for(var i = 0; i < len; i++){
8612                 r[a[i]] = this.getStyle(a[i]);
8613             }
8614             return r;
8615         },
8616
8617         /**
8618          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8619          * @param {String} property The style property whose value is returned.
8620          * @return {String} The current value of the style property for this element.
8621          */
8622         getStyle : function(){
8623             return view && view.getComputedStyle ?
8624                 function(prop){
8625                     var el = this.dom, v, cs, camel;
8626                     if(prop == 'float'){
8627                         prop = "cssFloat";
8628                     }
8629                     if(el.style && (v = el.style[prop])){
8630                         return v;
8631                     }
8632                     if(cs = view.getComputedStyle(el, "")){
8633                         if(!(camel = propCache[prop])){
8634                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8635                         }
8636                         return cs[camel];
8637                     }
8638                     return null;
8639                 } :
8640                 function(prop){
8641                     var el = this.dom, v, cs, camel;
8642                     if(prop == 'opacity'){
8643                         if(typeof el.style.filter == 'string'){
8644                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8645                             if(m){
8646                                 var fv = parseFloat(m[1]);
8647                                 if(!isNaN(fv)){
8648                                     return fv ? fv / 100 : 0;
8649                                 }
8650                             }
8651                         }
8652                         return 1;
8653                     }else if(prop == 'float'){
8654                         prop = "styleFloat";
8655                     }
8656                     if(!(camel = propCache[prop])){
8657                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8658                     }
8659                     if(v = el.style[camel]){
8660                         return v;
8661                     }
8662                     if(cs = el.currentStyle){
8663                         return cs[camel];
8664                     }
8665                     return null;
8666                 };
8667         }(),
8668
8669         /**
8670          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8671          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8672          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8673          * @return {Roo.Element} this
8674          */
8675         setStyle : function(prop, value){
8676             if(typeof prop == "string"){
8677                 
8678                 if (prop == 'float') {
8679                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8680                     return this;
8681                 }
8682                 
8683                 var camel;
8684                 if(!(camel = propCache[prop])){
8685                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8686                 }
8687                 
8688                 if(camel == 'opacity') {
8689                     this.setOpacity(value);
8690                 }else{
8691                     this.dom.style[camel] = value;
8692                 }
8693             }else{
8694                 for(var style in prop){
8695                     if(typeof prop[style] != "function"){
8696                        this.setStyle(style, prop[style]);
8697                     }
8698                 }
8699             }
8700             return this;
8701         },
8702
8703         /**
8704          * More flexible version of {@link #setStyle} for setting style properties.
8705          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8706          * a function which returns such a specification.
8707          * @return {Roo.Element} this
8708          */
8709         applyStyles : function(style){
8710             Roo.DomHelper.applyStyles(this.dom, style);
8711             return this;
8712         },
8713
8714         /**
8715           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8716           * @return {Number} The X position of the element
8717           */
8718         getX : function(){
8719             return D.getX(this.dom);
8720         },
8721
8722         /**
8723           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8724           * @return {Number} The Y position of the element
8725           */
8726         getY : function(){
8727             return D.getY(this.dom);
8728         },
8729
8730         /**
8731           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8732           * @return {Array} The XY position of the element
8733           */
8734         getXY : function(){
8735             return D.getXY(this.dom);
8736         },
8737
8738         /**
8739          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8740          * @param {Number} The X position of the element
8741          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8742          * @return {Roo.Element} this
8743          */
8744         setX : function(x, animate){
8745             if(!animate || !A){
8746                 D.setX(this.dom, x);
8747             }else{
8748                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8749             }
8750             return this;
8751         },
8752
8753         /**
8754          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8755          * @param {Number} The Y position of the element
8756          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8757          * @return {Roo.Element} this
8758          */
8759         setY : function(y, animate){
8760             if(!animate || !A){
8761                 D.setY(this.dom, y);
8762             }else{
8763                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8764             }
8765             return this;
8766         },
8767
8768         /**
8769          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8770          * @param {String} left The left CSS property value
8771          * @return {Roo.Element} this
8772          */
8773         setLeft : function(left){
8774             this.setStyle("left", this.addUnits(left));
8775             return this;
8776         },
8777
8778         /**
8779          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8780          * @param {String} top The top CSS property value
8781          * @return {Roo.Element} this
8782          */
8783         setTop : function(top){
8784             this.setStyle("top", this.addUnits(top));
8785             return this;
8786         },
8787
8788         /**
8789          * Sets the element's CSS right style.
8790          * @param {String} right The right CSS property value
8791          * @return {Roo.Element} this
8792          */
8793         setRight : function(right){
8794             this.setStyle("right", this.addUnits(right));
8795             return this;
8796         },
8797
8798         /**
8799          * Sets the element's CSS bottom style.
8800          * @param {String} bottom The bottom CSS property value
8801          * @return {Roo.Element} this
8802          */
8803         setBottom : function(bottom){
8804             this.setStyle("bottom", this.addUnits(bottom));
8805             return this;
8806         },
8807
8808         /**
8809          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8810          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8811          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8812          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8813          * @return {Roo.Element} this
8814          */
8815         setXY : function(pos, animate){
8816             if(!animate || !A){
8817                 D.setXY(this.dom, pos);
8818             }else{
8819                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8820             }
8821             return this;
8822         },
8823
8824         /**
8825          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8826          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8827          * @param {Number} x X value for new position (coordinates are page-based)
8828          * @param {Number} y Y value for new position (coordinates are page-based)
8829          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8830          * @return {Roo.Element} this
8831          */
8832         setLocation : function(x, y, animate){
8833             this.setXY([x, y], this.preanim(arguments, 2));
8834             return this;
8835         },
8836
8837         /**
8838          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8839          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8840          * @param {Number} x X value for new position (coordinates are page-based)
8841          * @param {Number} y Y value for new position (coordinates are page-based)
8842          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8843          * @return {Roo.Element} this
8844          */
8845         moveTo : function(x, y, animate){
8846             this.setXY([x, y], this.preanim(arguments, 2));
8847             return this;
8848         },
8849
8850         /**
8851          * Returns the region of the given element.
8852          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8853          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8854          */
8855         getRegion : function(){
8856             return D.getRegion(this.dom);
8857         },
8858
8859         /**
8860          * Returns the offset height of the element
8861          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8862          * @return {Number} The element's height
8863          */
8864         getHeight : function(contentHeight){
8865             var h = this.dom.offsetHeight || 0;
8866             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8867         },
8868
8869         /**
8870          * Returns the offset width of the element
8871          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8872          * @return {Number} The element's width
8873          */
8874         getWidth : function(contentWidth){
8875             var w = this.dom.offsetWidth || 0;
8876             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8877         },
8878
8879         /**
8880          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8881          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8882          * if a height has not been set using CSS.
8883          * @return {Number}
8884          */
8885         getComputedHeight : function(){
8886             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8887             if(!h){
8888                 h = parseInt(this.getStyle('height'), 10) || 0;
8889                 if(!this.isBorderBox()){
8890                     h += this.getFrameWidth('tb');
8891                 }
8892             }
8893             return h;
8894         },
8895
8896         /**
8897          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8898          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8899          * if a width has not been set using CSS.
8900          * @return {Number}
8901          */
8902         getComputedWidth : function(){
8903             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8904             if(!w){
8905                 w = parseInt(this.getStyle('width'), 10) || 0;
8906                 if(!this.isBorderBox()){
8907                     w += this.getFrameWidth('lr');
8908                 }
8909             }
8910             return w;
8911         },
8912
8913         /**
8914          * Returns the size of the element.
8915          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8916          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8917          */
8918         getSize : function(contentSize){
8919             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8920         },
8921
8922         /**
8923          * Returns the width and height of the viewport.
8924          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8925          */
8926         getViewSize : function(){
8927             var d = this.dom, doc = document, aw = 0, ah = 0;
8928             if(d == doc || d == doc.body){
8929                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8930             }else{
8931                 return {
8932                     width : d.clientWidth,
8933                     height: d.clientHeight
8934                 };
8935             }
8936         },
8937
8938         /**
8939          * Returns the value of the "value" attribute
8940          * @param {Boolean} asNumber true to parse the value as a number
8941          * @return {String/Number}
8942          */
8943         getValue : function(asNumber){
8944             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8945         },
8946
8947         // private
8948         adjustWidth : function(width){
8949             if(typeof width == "number"){
8950                 if(this.autoBoxAdjust && !this.isBorderBox()){
8951                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8952                 }
8953                 if(width < 0){
8954                     width = 0;
8955                 }
8956             }
8957             return width;
8958         },
8959
8960         // private
8961         adjustHeight : function(height){
8962             if(typeof height == "number"){
8963                if(this.autoBoxAdjust && !this.isBorderBox()){
8964                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8965                }
8966                if(height < 0){
8967                    height = 0;
8968                }
8969             }
8970             return height;
8971         },
8972
8973         /**
8974          * Set the width of the element
8975          * @param {Number} width The new width
8976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8977          * @return {Roo.Element} this
8978          */
8979         setWidth : function(width, animate){
8980             width = this.adjustWidth(width);
8981             if(!animate || !A){
8982                 this.dom.style.width = this.addUnits(width);
8983             }else{
8984                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8985             }
8986             return this;
8987         },
8988
8989         /**
8990          * Set the height of the element
8991          * @param {Number} height The new height
8992          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8993          * @return {Roo.Element} this
8994          */
8995          setHeight : function(height, animate){
8996             height = this.adjustHeight(height);
8997             if(!animate || !A){
8998                 this.dom.style.height = this.addUnits(height);
8999             }else{
9000                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9001             }
9002             return this;
9003         },
9004
9005         /**
9006          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9007          * @param {Number} width The new width
9008          * @param {Number} height The new height
9009          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9010          * @return {Roo.Element} this
9011          */
9012          setSize : function(width, height, animate){
9013             if(typeof width == "object"){ // in case of object from getSize()
9014                 height = width.height; width = width.width;
9015             }
9016             width = this.adjustWidth(width); height = this.adjustHeight(height);
9017             if(!animate || !A){
9018                 this.dom.style.width = this.addUnits(width);
9019                 this.dom.style.height = this.addUnits(height);
9020             }else{
9021                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9022             }
9023             return this;
9024         },
9025
9026         /**
9027          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9028          * @param {Number} x X value for new position (coordinates are page-based)
9029          * @param {Number} y Y value for new position (coordinates are page-based)
9030          * @param {Number} width The new width
9031          * @param {Number} height The new height
9032          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9033          * @return {Roo.Element} this
9034          */
9035         setBounds : function(x, y, width, height, animate){
9036             if(!animate || !A){
9037                 this.setSize(width, height);
9038                 this.setLocation(x, y);
9039             }else{
9040                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9041                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9042                               this.preanim(arguments, 4), 'motion');
9043             }
9044             return this;
9045         },
9046
9047         /**
9048          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
9049          * @param {Roo.lib.Region} region The region to fill
9050          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9051          * @return {Roo.Element} this
9052          */
9053         setRegion : function(region, animate){
9054             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9055             return this;
9056         },
9057
9058         /**
9059          * Appends an event handler
9060          *
9061          * @param {String}   eventName     The type of event to append
9062          * @param {Function} fn        The method the event invokes
9063          * @param {Object} scope       (optional) The scope (this object) of the fn
9064          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9065          */
9066         addListener : function(eventName, fn, scope, options)
9067         {
9068             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9069                 this.addListener('touchstart', this.onTapHandler, this);
9070             }
9071             
9072             // we need to handle a special case where dom element is a svg element.
9073             // in this case we do not actua
9074             if (!this.dom) {
9075                 return;
9076             }
9077             
9078             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9079                 if (typeof(this.listeners[eventName]) == 'undefined') {
9080                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9081                 }
9082                 this.listeners[eventName].addListener(fn, scope, options);
9083                 return;
9084             }
9085             
9086                 
9087             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9088             
9089             
9090         },
9091         tapedTwice : false,
9092         onTapHandler : function(event)
9093         {
9094             if(!this.tapedTwice) {
9095                 this.tapedTwice = true;
9096                 var s = this;
9097                 setTimeout( function() {
9098                     s.tapedTwice = false;
9099                 }, 300 );
9100                 return;
9101             }
9102             event.preventDefault();
9103             var revent = new MouseEvent('dblclick',  {
9104                 view: window,
9105                 bubbles: true,
9106                 cancelable: true
9107             });
9108              
9109             this.dom.dispatchEvent(revent);
9110             //action on double tap goes below
9111              
9112         }, 
9113  
9114         /**
9115          * Removes an event handler from this element
9116          * @param {String} eventName the type of event to remove
9117          * @param {Function} fn the method the event invokes
9118          * @param {Function} scope (needed for svg fake listeners)
9119          * @return {Roo.Element} this
9120          */
9121         removeListener : function(eventName, fn, scope){
9122             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9123             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9124                 return this;
9125             }
9126             this.listeners[eventName].removeListener(fn, scope);
9127             return this;
9128         },
9129
9130         /**
9131          * Removes all previous added listeners from this element
9132          * @return {Roo.Element} this
9133          */
9134         removeAllListeners : function(){
9135             E.purgeElement(this.dom);
9136             this.listeners = {};
9137             return this;
9138         },
9139
9140         relayEvent : function(eventName, observable){
9141             this.on(eventName, function(e){
9142                 observable.fireEvent(eventName, e);
9143             });
9144         },
9145
9146         
9147         /**
9148          * Set the opacity of the element
9149          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9150          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9151          * @return {Roo.Element} this
9152          */
9153          setOpacity : function(opacity, animate){
9154             if(!animate || !A){
9155                 var s = this.dom.style;
9156                 if(Roo.isIE){
9157                     s.zoom = 1;
9158                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9159                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9160                 }else{
9161                     s.opacity = opacity;
9162                 }
9163             }else{
9164                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9165             }
9166             return this;
9167         },
9168
9169         /**
9170          * Gets the left X coordinate
9171          * @param {Boolean} local True to get the local css position instead of page coordinate
9172          * @return {Number}
9173          */
9174         getLeft : function(local){
9175             if(!local){
9176                 return this.getX();
9177             }else{
9178                 return parseInt(this.getStyle("left"), 10) || 0;
9179             }
9180         },
9181
9182         /**
9183          * Gets the right X coordinate of the element (element X position + element width)
9184          * @param {Boolean} local True to get the local css position instead of page coordinate
9185          * @return {Number}
9186          */
9187         getRight : function(local){
9188             if(!local){
9189                 return this.getX() + this.getWidth();
9190             }else{
9191                 return (this.getLeft(true) + this.getWidth()) || 0;
9192             }
9193         },
9194
9195         /**
9196          * Gets the top Y coordinate
9197          * @param {Boolean} local True to get the local css position instead of page coordinate
9198          * @return {Number}
9199          */
9200         getTop : function(local) {
9201             if(!local){
9202                 return this.getY();
9203             }else{
9204                 return parseInt(this.getStyle("top"), 10) || 0;
9205             }
9206         },
9207
9208         /**
9209          * Gets the bottom Y coordinate of the element (element Y position + element height)
9210          * @param {Boolean} local True to get the local css position instead of page coordinate
9211          * @return {Number}
9212          */
9213         getBottom : function(local){
9214             if(!local){
9215                 return this.getY() + this.getHeight();
9216             }else{
9217                 return (this.getTop(true) + this.getHeight()) || 0;
9218             }
9219         },
9220
9221         /**
9222         * Initializes positioning on this element. If a desired position is not passed, it will make the
9223         * the element positioned relative IF it is not already positioned.
9224         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9225         * @param {Number} zIndex (optional) The zIndex to apply
9226         * @param {Number} x (optional) Set the page X position
9227         * @param {Number} y (optional) Set the page Y position
9228         */
9229         position : function(pos, zIndex, x, y){
9230             if(!pos){
9231                if(this.getStyle('position') == 'static'){
9232                    this.setStyle('position', 'relative');
9233                }
9234             }else{
9235                 this.setStyle("position", pos);
9236             }
9237             if(zIndex){
9238                 this.setStyle("z-index", zIndex);
9239             }
9240             if(x !== undefined && y !== undefined){
9241                 this.setXY([x, y]);
9242             }else if(x !== undefined){
9243                 this.setX(x);
9244             }else if(y !== undefined){
9245                 this.setY(y);
9246             }
9247         },
9248
9249         /**
9250         * Clear positioning back to the default when the document was loaded
9251         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9252         * @return {Roo.Element} this
9253          */
9254         clearPositioning : function(value){
9255             value = value ||'';
9256             this.setStyle({
9257                 "left": value,
9258                 "right": value,
9259                 "top": value,
9260                 "bottom": value,
9261                 "z-index": "",
9262                 "position" : "static"
9263             });
9264             return this;
9265         },
9266
9267         /**
9268         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9269         * snapshot before performing an update and then restoring the element.
9270         * @return {Object}
9271         */
9272         getPositioning : function(){
9273             var l = this.getStyle("left");
9274             var t = this.getStyle("top");
9275             return {
9276                 "position" : this.getStyle("position"),
9277                 "left" : l,
9278                 "right" : l ? "" : this.getStyle("right"),
9279                 "top" : t,
9280                 "bottom" : t ? "" : this.getStyle("bottom"),
9281                 "z-index" : this.getStyle("z-index")
9282             };
9283         },
9284
9285         /**
9286          * Gets the width of the border(s) for the specified side(s)
9287          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9288          * passing lr would get the border (l)eft width + the border (r)ight width.
9289          * @return {Number} The width of the sides passed added together
9290          */
9291         getBorderWidth : function(side){
9292             return this.addStyles(side, El.borders);
9293         },
9294
9295         /**
9296          * Gets the width of the padding(s) for the specified side(s)
9297          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9298          * passing lr would get the padding (l)eft + the padding (r)ight.
9299          * @return {Number} The padding of the sides passed added together
9300          */
9301         getPadding : function(side){
9302             return this.addStyles(side, El.paddings);
9303         },
9304
9305         /**
9306         * Set positioning with an object returned by getPositioning().
9307         * @param {Object} posCfg
9308         * @return {Roo.Element} this
9309          */
9310         setPositioning : function(pc){
9311             this.applyStyles(pc);
9312             if(pc.right == "auto"){
9313                 this.dom.style.right = "";
9314             }
9315             if(pc.bottom == "auto"){
9316                 this.dom.style.bottom = "";
9317             }
9318             return this;
9319         },
9320
9321         // private
9322         fixDisplay : function(){
9323             if(this.getStyle("display") == "none"){
9324                 this.setStyle("visibility", "hidden");
9325                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9326                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9327                     this.setStyle("display", "block");
9328                 }
9329             }
9330         },
9331
9332         /**
9333          * Quick set left and top adding default units
9334          * @param {String} left The left CSS property value
9335          * @param {String} top The top CSS property value
9336          * @return {Roo.Element} this
9337          */
9338          setLeftTop : function(left, top){
9339             this.dom.style.left = this.addUnits(left);
9340             this.dom.style.top = this.addUnits(top);
9341             return this;
9342         },
9343
9344         /**
9345          * Move this element relative to its current position.
9346          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9347          * @param {Number} distance How far to move the element in pixels
9348          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9349          * @return {Roo.Element} this
9350          */
9351          move : function(direction, distance, animate){
9352             var xy = this.getXY();
9353             direction = direction.toLowerCase();
9354             switch(direction){
9355                 case "l":
9356                 case "left":
9357                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9358                     break;
9359                case "r":
9360                case "right":
9361                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9362                     break;
9363                case "t":
9364                case "top":
9365                case "up":
9366                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9367                     break;
9368                case "b":
9369                case "bottom":
9370                case "down":
9371                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9372                     break;
9373             }
9374             return this;
9375         },
9376
9377         /**
9378          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9379          * @return {Roo.Element} this
9380          */
9381         clip : function(){
9382             if(!this.isClipped){
9383                this.isClipped = true;
9384                this.originalClip = {
9385                    "o": this.getStyle("overflow"),
9386                    "x": this.getStyle("overflow-x"),
9387                    "y": this.getStyle("overflow-y")
9388                };
9389                this.setStyle("overflow", "hidden");
9390                this.setStyle("overflow-x", "hidden");
9391                this.setStyle("overflow-y", "hidden");
9392             }
9393             return this;
9394         },
9395
9396         /**
9397          *  Return clipping (overflow) to original clipping before clip() was called
9398          * @return {Roo.Element} this
9399          */
9400         unclip : function(){
9401             if(this.isClipped){
9402                 this.isClipped = false;
9403                 var o = this.originalClip;
9404                 if(o.o){this.setStyle("overflow", o.o);}
9405                 if(o.x){this.setStyle("overflow-x", o.x);}
9406                 if(o.y){this.setStyle("overflow-y", o.y);}
9407             }
9408             return this;
9409         },
9410
9411
9412         /**
9413          * Gets the x,y coordinates specified by the anchor position on the element.
9414          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9415          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9416          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9417          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9418          * @return {Array} [x, y] An array containing the element's x and y coordinates
9419          */
9420         getAnchorXY : function(anchor, local, s){
9421             //Passing a different size is useful for pre-calculating anchors,
9422             //especially for anchored animations that change the el size.
9423
9424             var w, h, vp = false;
9425             if(!s){
9426                 var d = this.dom;
9427                 if(d == document.body || d == document){
9428                     vp = true;
9429                     w = D.getViewWidth(); h = D.getViewHeight();
9430                 }else{
9431                     w = this.getWidth(); h = this.getHeight();
9432                 }
9433             }else{
9434                 w = s.width;  h = s.height;
9435             }
9436             var x = 0, y = 0, r = Math.round;
9437             switch((anchor || "tl").toLowerCase()){
9438                 case "c":
9439                     x = r(w*.5);
9440                     y = r(h*.5);
9441                 break;
9442                 case "t":
9443                     x = r(w*.5);
9444                     y = 0;
9445                 break;
9446                 case "l":
9447                     x = 0;
9448                     y = r(h*.5);
9449                 break;
9450                 case "r":
9451                     x = w;
9452                     y = r(h*.5);
9453                 break;
9454                 case "b":
9455                     x = r(w*.5);
9456                     y = h;
9457                 break;
9458                 case "tl":
9459                     x = 0;
9460                     y = 0;
9461                 break;
9462                 case "bl":
9463                     x = 0;
9464                     y = h;
9465                 break;
9466                 case "br":
9467                     x = w;
9468                     y = h;
9469                 break;
9470                 case "tr":
9471                     x = w;
9472                     y = 0;
9473                 break;
9474             }
9475             if(local === true){
9476                 return [x, y];
9477             }
9478             if(vp){
9479                 var sc = this.getScroll();
9480                 return [x + sc.left, y + sc.top];
9481             }
9482             //Add the element's offset xy
9483             var o = this.getXY();
9484             return [x+o[0], y+o[1]];
9485         },
9486
9487         /**
9488          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9489          * supported position values.
9490          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9491          * @param {String} position The position to align to.
9492          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9493          * @return {Array} [x, y]
9494          */
9495         getAlignToXY : function(el, p, o)
9496         {
9497             el = Roo.get(el);
9498             var d = this.dom;
9499             if(!el.dom){
9500                 throw "Element.alignTo with an element that doesn't exist";
9501             }
9502             var c = false; //constrain to viewport
9503             var p1 = "", p2 = "";
9504             o = o || [0,0];
9505
9506             if(!p){
9507                 p = "tl-bl";
9508             }else if(p == "?"){
9509                 p = "tl-bl?";
9510             }else if(p.indexOf("-") == -1){
9511                 p = "tl-" + p;
9512             }
9513             p = p.toLowerCase();
9514             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9515             if(!m){
9516                throw "Element.alignTo with an invalid alignment " + p;
9517             }
9518             p1 = m[1]; p2 = m[2]; c = !!m[3];
9519
9520             //Subtract the aligned el's internal xy from the target's offset xy
9521             //plus custom offset to get the aligned el's new offset xy
9522             var a1 = this.getAnchorXY(p1, true);
9523             var a2 = el.getAnchorXY(p2, false);
9524             var x = a2[0] - a1[0] + o[0];
9525             var y = a2[1] - a1[1] + o[1];
9526             if(c){
9527                 //constrain the aligned el to viewport if necessary
9528                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9529                 // 5px of margin for ie
9530                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9531
9532                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9533                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9534                 //otherwise swap the aligned el to the opposite border of the target.
9535                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9536                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9537                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9538                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9539
9540                var doc = document;
9541                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9542                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9543
9544                if((x+w) > dw + scrollX){
9545                     x = swapX ? r.left-w : dw+scrollX-w;
9546                 }
9547                if(x < scrollX){
9548                    x = swapX ? r.right : scrollX;
9549                }
9550                if((y+h) > dh + scrollY){
9551                     y = swapY ? r.top-h : dh+scrollY-h;
9552                 }
9553                if (y < scrollY){
9554                    y = swapY ? r.bottom : scrollY;
9555                }
9556             }
9557             return [x,y];
9558         },
9559
9560         // private
9561         getConstrainToXY : function(){
9562             var os = {top:0, left:0, bottom:0, right: 0};
9563
9564             return function(el, local, offsets, proposedXY){
9565                 el = Roo.get(el);
9566                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9567
9568                 var vw, vh, vx = 0, vy = 0;
9569                 if(el.dom == document.body || el.dom == document){
9570                     vw = Roo.lib.Dom.getViewWidth();
9571                     vh = Roo.lib.Dom.getViewHeight();
9572                 }else{
9573                     vw = el.dom.clientWidth;
9574                     vh = el.dom.clientHeight;
9575                     if(!local){
9576                         var vxy = el.getXY();
9577                         vx = vxy[0];
9578                         vy = vxy[1];
9579                     }
9580                 }
9581
9582                 var s = el.getScroll();
9583
9584                 vx += offsets.left + s.left;
9585                 vy += offsets.top + s.top;
9586
9587                 vw -= offsets.right;
9588                 vh -= offsets.bottom;
9589
9590                 var vr = vx+vw;
9591                 var vb = vy+vh;
9592
9593                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9594                 var x = xy[0], y = xy[1];
9595                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9596
9597                 // only move it if it needs it
9598                 var moved = false;
9599
9600                 // first validate right/bottom
9601                 if((x + w) > vr){
9602                     x = vr - w;
9603                     moved = true;
9604                 }
9605                 if((y + h) > vb){
9606                     y = vb - h;
9607                     moved = true;
9608                 }
9609                 // then make sure top/left isn't negative
9610                 if(x < vx){
9611                     x = vx;
9612                     moved = true;
9613                 }
9614                 if(y < vy){
9615                     y = vy;
9616                     moved = true;
9617                 }
9618                 return moved ? [x, y] : false;
9619             };
9620         }(),
9621
9622         // private
9623         adjustForConstraints : function(xy, parent, offsets){
9624             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9625         },
9626
9627         /**
9628          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9629          * document it aligns it to the viewport.
9630          * The position parameter is optional, and can be specified in any one of the following formats:
9631          * <ul>
9632          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9633          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9634          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9635          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9636          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
9637          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9638          * </ul>
9639          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9640          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9641          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9642          * that specified in order to enforce the viewport constraints.
9643          * Following are all of the supported anchor positions:
9644     <pre>
9645     Value  Description
9646     -----  -----------------------------
9647     tl     The top left corner (default)
9648     t      The center of the top edge
9649     tr     The top right corner
9650     l      The center of the left edge
9651     c      In the center of the element
9652     r      The center of the right edge
9653     bl     The bottom left corner
9654     b      The center of the bottom edge
9655     br     The bottom right corner
9656     </pre>
9657     Example Usage:
9658     <pre><code>
9659     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9660     el.alignTo("other-el");
9661
9662     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9663     el.alignTo("other-el", "tr?");
9664
9665     // align the bottom right corner of el with the center left edge of other-el
9666     el.alignTo("other-el", "br-l?");
9667
9668     // align the center of el with the bottom left corner of other-el and
9669     // adjust the x position by -6 pixels (and the y position by 0)
9670     el.alignTo("other-el", "c-bl", [-6, 0]);
9671     </code></pre>
9672          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9673          * @param {String} position The position to align to.
9674          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9675          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9676          * @return {Roo.Element} this
9677          */
9678         alignTo : function(element, position, offsets, animate){
9679             var xy = this.getAlignToXY(element, position, offsets);
9680             this.setXY(xy, this.preanim(arguments, 3));
9681             return this;
9682         },
9683
9684         /**
9685          * Anchors an element to another element and realigns it when the window is resized.
9686          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9687          * @param {String} position The position to align to.
9688          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9689          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9690          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9691          * is a number, it is used as the buffer delay (defaults to 50ms).
9692          * @param {Function} callback The function to call after the animation finishes
9693          * @return {Roo.Element} this
9694          */
9695         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9696             var action = function(){
9697                 this.alignTo(el, alignment, offsets, animate);
9698                 Roo.callback(callback, this);
9699             };
9700             Roo.EventManager.onWindowResize(action, this);
9701             var tm = typeof monitorScroll;
9702             if(tm != 'undefined'){
9703                 Roo.EventManager.on(window, 'scroll', action, this,
9704                     {buffer: tm == 'number' ? monitorScroll : 50});
9705             }
9706             action.call(this); // align immediately
9707             return this;
9708         },
9709         /**
9710          * Clears any opacity settings from this element. Required in some cases for IE.
9711          * @return {Roo.Element} this
9712          */
9713         clearOpacity : function(){
9714             if (window.ActiveXObject) {
9715                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9716                     this.dom.style.filter = "";
9717                 }
9718             } else {
9719                 this.dom.style.opacity = "";
9720                 this.dom.style["-moz-opacity"] = "";
9721                 this.dom.style["-khtml-opacity"] = "";
9722             }
9723             return this;
9724         },
9725
9726         /**
9727          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9728          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9729          * @return {Roo.Element} this
9730          */
9731         hide : function(animate){
9732             this.setVisible(false, this.preanim(arguments, 0));
9733             return this;
9734         },
9735
9736         /**
9737         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9738         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9739          * @return {Roo.Element} this
9740          */
9741         show : function(animate){
9742             this.setVisible(true, this.preanim(arguments, 0));
9743             return this;
9744         },
9745
9746         /**
9747          * @private Test if size has a unit, otherwise appends the default
9748          */
9749         addUnits : function(size){
9750             return Roo.Element.addUnits(size, this.defaultUnit);
9751         },
9752
9753         /**
9754          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9755          * @return {Roo.Element} this
9756          */
9757         beginMeasure : function(){
9758             var el = this.dom;
9759             if(el.offsetWidth || el.offsetHeight){
9760                 return this; // offsets work already
9761             }
9762             var changed = [];
9763             var p = this.dom, b = document.body; // start with this element
9764             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9765                 var pe = Roo.get(p);
9766                 if(pe.getStyle('display') == 'none'){
9767                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9768                     p.style.visibility = "hidden";
9769                     p.style.display = "block";
9770                 }
9771                 p = p.parentNode;
9772             }
9773             this._measureChanged = changed;
9774             return this;
9775
9776         },
9777
9778         /**
9779          * Restores displays to before beginMeasure was called
9780          * @return {Roo.Element} this
9781          */
9782         endMeasure : function(){
9783             var changed = this._measureChanged;
9784             if(changed){
9785                 for(var i = 0, len = changed.length; i < len; i++) {
9786                     var r = changed[i];
9787                     r.el.style.visibility = r.visibility;
9788                     r.el.style.display = "none";
9789                 }
9790                 this._measureChanged = null;
9791             }
9792             return this;
9793         },
9794
9795         /**
9796         * Update the innerHTML of this element, optionally searching for and processing scripts
9797         * @param {String} html The new HTML
9798         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9799         * @param {Function} callback For async script loading you can be noticed when the update completes
9800         * @return {Roo.Element} this
9801          */
9802         update : function(html, loadScripts, callback){
9803             if(typeof html == "undefined"){
9804                 html = "";
9805             }
9806             if(loadScripts !== true){
9807                 this.dom.innerHTML = html;
9808                 if(typeof callback == "function"){
9809                     callback();
9810                 }
9811                 return this;
9812             }
9813             var id = Roo.id();
9814             var dom = this.dom;
9815
9816             html += '<span id="' + id + '"></span>';
9817
9818             E.onAvailable(id, function(){
9819                 var hd = document.getElementsByTagName("head")[0];
9820                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9821                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9822                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9823
9824                 var match;
9825                 while(match = re.exec(html)){
9826                     var attrs = match[1];
9827                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9828                     if(srcMatch && srcMatch[2]){
9829                        var s = document.createElement("script");
9830                        s.src = srcMatch[2];
9831                        var typeMatch = attrs.match(typeRe);
9832                        if(typeMatch && typeMatch[2]){
9833                            s.type = typeMatch[2];
9834                        }
9835                        hd.appendChild(s);
9836                     }else if(match[2] && match[2].length > 0){
9837                         if(window.execScript) {
9838                            window.execScript(match[2]);
9839                         } else {
9840                             /**
9841                              * eval:var:id
9842                              * eval:var:dom
9843                              * eval:var:html
9844                              * 
9845                              */
9846                            window.eval(match[2]);
9847                         }
9848                     }
9849                 }
9850                 var el = document.getElementById(id);
9851                 if(el){el.parentNode.removeChild(el);}
9852                 if(typeof callback == "function"){
9853                     callback();
9854                 }
9855             });
9856             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9857             return this;
9858         },
9859
9860         /**
9861          * Direct access to the UpdateManager update() method (takes the same parameters).
9862          * @param {String/Function} url The url for this request or a function to call to get the url
9863          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9864          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9865          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
9866          * @return {Roo.Element} this
9867          */
9868         load : function(){
9869             var um = this.getUpdateManager();
9870             um.update.apply(um, arguments);
9871             return this;
9872         },
9873
9874         /**
9875         * Gets this element's UpdateManager
9876         * @return {Roo.UpdateManager} The UpdateManager
9877         */
9878         getUpdateManager : function(){
9879             if(!this.updateManager){
9880                 this.updateManager = new Roo.UpdateManager(this);
9881             }
9882             return this.updateManager;
9883         },
9884
9885         /**
9886          * Disables text selection for this element (normalized across browsers)
9887          * @return {Roo.Element} this
9888          */
9889         unselectable : function(){
9890             this.dom.unselectable = "on";
9891             this.swallowEvent("selectstart", true);
9892             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9893             this.addClass("x-unselectable");
9894             return this;
9895         },
9896
9897         /**
9898         * Calculates the x, y to center this element on the screen
9899         * @return {Array} The x, y values [x, y]
9900         */
9901         getCenterXY : function(){
9902             return this.getAlignToXY(document, 'c-c');
9903         },
9904
9905         /**
9906         * Centers the Element in either the viewport, or another Element.
9907         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9908         */
9909         center : function(centerIn){
9910             this.alignTo(centerIn || document, 'c-c');
9911             return this;
9912         },
9913
9914         /**
9915          * Tests various css rules/browsers to determine if this element uses a border box
9916          * @return {Boolean}
9917          */
9918         isBorderBox : function(){
9919             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9920         },
9921
9922         /**
9923          * Return a box {x, y, width, height} that can be used to set another elements
9924          * size/location to match this element.
9925          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9926          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9927          * @return {Object} box An object in the format {x, y, width, height}
9928          */
9929         getBox : function(contentBox, local){
9930             var xy;
9931             if(!local){
9932                 xy = this.getXY();
9933             }else{
9934                 var left = parseInt(this.getStyle("left"), 10) || 0;
9935                 var top = parseInt(this.getStyle("top"), 10) || 0;
9936                 xy = [left, top];
9937             }
9938             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9939             if(!contentBox){
9940                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9941             }else{
9942                 var l = this.getBorderWidth("l")+this.getPadding("l");
9943                 var r = this.getBorderWidth("r")+this.getPadding("r");
9944                 var t = this.getBorderWidth("t")+this.getPadding("t");
9945                 var b = this.getBorderWidth("b")+this.getPadding("b");
9946                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
9947             }
9948             bx.right = bx.x + bx.width;
9949             bx.bottom = bx.y + bx.height;
9950             return bx;
9951         },
9952
9953         /**
9954          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9955          for more information about the sides.
9956          * @param {String} sides
9957          * @return {Number}
9958          */
9959         getFrameWidth : function(sides, onlyContentBox){
9960             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9961         },
9962
9963         /**
9964          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
9965          * @param {Object} box The box to fill {x, y, width, height}
9966          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9967          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9968          * @return {Roo.Element} this
9969          */
9970         setBox : function(box, adjust, animate){
9971             var w = box.width, h = box.height;
9972             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9973                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9974                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9975             }
9976             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9977             return this;
9978         },
9979
9980         /**
9981          * Forces the browser to repaint this element
9982          * @return {Roo.Element} this
9983          */
9984          repaint : function(){
9985             var dom = this.dom;
9986             this.addClass("x-repaint");
9987             setTimeout(function(){
9988                 Roo.get(dom).removeClass("x-repaint");
9989             }, 1);
9990             return this;
9991         },
9992
9993         /**
9994          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9995          * then it returns the calculated width of the sides (see getPadding)
9996          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9997          * @return {Object/Number}
9998          */
9999         getMargins : function(side){
10000             if(!side){
10001                 return {
10002                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10003                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10004                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10005                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10006                 };
10007             }else{
10008                 return this.addStyles(side, El.margins);
10009              }
10010         },
10011
10012         // private
10013         addStyles : function(sides, styles){
10014             var val = 0, v, w;
10015             for(var i = 0, len = sides.length; i < len; i++){
10016                 v = this.getStyle(styles[sides.charAt(i)]);
10017                 if(v){
10018                      w = parseInt(v, 10);
10019                      if(w){ val += w; }
10020                 }
10021             }
10022             return val;
10023         },
10024
10025         /**
10026          * Creates a proxy element of this element
10027          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10028          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10029          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10030          * @return {Roo.Element} The new proxy element
10031          */
10032         createProxy : function(config, renderTo, matchBox){
10033             if(renderTo){
10034                 renderTo = Roo.getDom(renderTo);
10035             }else{
10036                 renderTo = document.body;
10037             }
10038             config = typeof config == "object" ?
10039                 config : {tag : "div", cls: config};
10040             var proxy = Roo.DomHelper.append(renderTo, config, true);
10041             if(matchBox){
10042                proxy.setBox(this.getBox());
10043             }
10044             return proxy;
10045         },
10046
10047         /**
10048          * Puts a mask over this element to disable user interaction. Requires core.css.
10049          * This method can only be applied to elements which accept child nodes.
10050          * @param {String} msg (optional) A message to display in the mask
10051          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10052          * @return {Element} The mask  element
10053          */
10054         mask : function(msg, msgCls)
10055         {
10056             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10057                 this.setStyle("position", "relative");
10058             }
10059             if(!this._mask){
10060                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10061             }
10062             
10063             this.addClass("x-masked");
10064             this._mask.setDisplayed(true);
10065             
10066             // we wander
10067             var z = 0;
10068             var dom = this.dom;
10069             while (dom && dom.style) {
10070                 if (!isNaN(parseInt(dom.style.zIndex))) {
10071                     z = Math.max(z, parseInt(dom.style.zIndex));
10072                 }
10073                 dom = dom.parentNode;
10074             }
10075             // if we are masking the body - then it hides everything..
10076             if (this.dom == document.body) {
10077                 z = 1000000;
10078                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10079                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10080             }
10081            
10082             if(typeof msg == 'string'){
10083                 if(!this._maskMsg){
10084                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10085                         cls: "roo-el-mask-msg", 
10086                         cn: [
10087                             {
10088                                 tag: 'i',
10089                                 cls: 'fa fa-spinner fa-spin'
10090                             },
10091                             {
10092                                 tag: 'div'
10093                             }   
10094                         ]
10095                     }, true);
10096                 }
10097                 var mm = this._maskMsg;
10098                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10099                 if (mm.dom.lastChild) { // weird IE issue?
10100                     mm.dom.lastChild.innerHTML = msg;
10101                 }
10102                 mm.setDisplayed(true);
10103                 mm.center(this);
10104                 mm.setStyle('z-index', z + 102);
10105             }
10106             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10107                 this._mask.setHeight(this.getHeight());
10108             }
10109             this._mask.setStyle('z-index', z + 100);
10110             
10111             return this._mask;
10112         },
10113
10114         /**
10115          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10116          * it is cached for reuse.
10117          */
10118         unmask : function(removeEl){
10119             if(this._mask){
10120                 if(removeEl === true){
10121                     this._mask.remove();
10122                     delete this._mask;
10123                     if(this._maskMsg){
10124                         this._maskMsg.remove();
10125                         delete this._maskMsg;
10126                     }
10127                 }else{
10128                     this._mask.setDisplayed(false);
10129                     if(this._maskMsg){
10130                         this._maskMsg.setDisplayed(false);
10131                     }
10132                 }
10133             }
10134             this.removeClass("x-masked");
10135         },
10136
10137         /**
10138          * Returns true if this element is masked
10139          * @return {Boolean}
10140          */
10141         isMasked : function(){
10142             return this._mask && this._mask.isVisible();
10143         },
10144
10145         /**
10146          * Creates an iframe shim for this element to keep selects and other windowed objects from
10147          * showing through.
10148          * @return {Roo.Element} The new shim element
10149          */
10150         createShim : function(){
10151             var el = document.createElement('iframe');
10152             el.frameBorder = 'no';
10153             el.className = 'roo-shim';
10154             if(Roo.isIE && Roo.isSecure){
10155                 el.src = Roo.SSL_SECURE_URL;
10156             }
10157             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10158             shim.autoBoxAdjust = false;
10159             return shim;
10160         },
10161
10162         /**
10163          * Removes this element from the DOM and deletes it from the cache
10164          */
10165         remove : function(){
10166             if(this.dom.parentNode){
10167                 this.dom.parentNode.removeChild(this.dom);
10168             }
10169             delete El.cache[this.dom.id];
10170         },
10171
10172         /**
10173          * Sets up event handlers to add and remove a css class when the mouse is over this element
10174          * @param {String} className
10175          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10176          * mouseout events for children elements
10177          * @return {Roo.Element} this
10178          */
10179         addClassOnOver : function(className, preventFlicker){
10180             this.on("mouseover", function(){
10181                 Roo.fly(this, '_internal').addClass(className);
10182             }, this.dom);
10183             var removeFn = function(e){
10184                 if(preventFlicker !== true || !e.within(this, true)){
10185                     Roo.fly(this, '_internal').removeClass(className);
10186                 }
10187             };
10188             this.on("mouseout", removeFn, this.dom);
10189             return this;
10190         },
10191
10192         /**
10193          * Sets up event handlers to add and remove a css class when this element has the focus
10194          * @param {String} className
10195          * @return {Roo.Element} this
10196          */
10197         addClassOnFocus : function(className){
10198             this.on("focus", function(){
10199                 Roo.fly(this, '_internal').addClass(className);
10200             }, this.dom);
10201             this.on("blur", function(){
10202                 Roo.fly(this, '_internal').removeClass(className);
10203             }, this.dom);
10204             return this;
10205         },
10206         /**
10207          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
10208          * @param {String} className
10209          * @return {Roo.Element} this
10210          */
10211         addClassOnClick : function(className){
10212             var dom = this.dom;
10213             this.on("mousedown", function(){
10214                 Roo.fly(dom, '_internal').addClass(className);
10215                 var d = Roo.get(document);
10216                 var fn = function(){
10217                     Roo.fly(dom, '_internal').removeClass(className);
10218                     d.removeListener("mouseup", fn);
10219                 };
10220                 d.on("mouseup", fn);
10221             });
10222             return this;
10223         },
10224
10225         /**
10226          * Stops the specified event from bubbling and optionally prevents the default action
10227          * @param {String} eventName
10228          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10229          * @return {Roo.Element} this
10230          */
10231         swallowEvent : function(eventName, preventDefault){
10232             var fn = function(e){
10233                 e.stopPropagation();
10234                 if(preventDefault){
10235                     e.preventDefault();
10236                 }
10237             };
10238             if(eventName instanceof Array){
10239                 for(var i = 0, len = eventName.length; i < len; i++){
10240                      this.on(eventName[i], fn);
10241                 }
10242                 return this;
10243             }
10244             this.on(eventName, fn);
10245             return this;
10246         },
10247
10248         /**
10249          * @private
10250          */
10251         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10252
10253         /**
10254          * Sizes this element to its parent element's dimensions performing
10255          * neccessary box adjustments.
10256          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10257          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10258          * @return {Roo.Element} this
10259          */
10260         fitToParent : function(monitorResize, targetParent) {
10261           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10262           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10263           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10264             return this;
10265           }
10266           var p = Roo.get(targetParent || this.dom.parentNode);
10267           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10268           if (monitorResize === true) {
10269             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10270             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10271           }
10272           return this;
10273         },
10274
10275         /**
10276          * Gets the next sibling, skipping text nodes
10277          * @return {HTMLElement} The next sibling or null
10278          */
10279         getNextSibling : function(){
10280             var n = this.dom.nextSibling;
10281             while(n && n.nodeType != 1){
10282                 n = n.nextSibling;
10283             }
10284             return n;
10285         },
10286
10287         /**
10288          * Gets the previous sibling, skipping text nodes
10289          * @return {HTMLElement} The previous sibling or null
10290          */
10291         getPrevSibling : function(){
10292             var n = this.dom.previousSibling;
10293             while(n && n.nodeType != 1){
10294                 n = n.previousSibling;
10295             }
10296             return n;
10297         },
10298
10299
10300         /**
10301          * Appends the passed element(s) to this element
10302          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10303          * @return {Roo.Element} this
10304          */
10305         appendChild: function(el){
10306             el = Roo.get(el);
10307             el.appendTo(this);
10308             return this;
10309         },
10310
10311         /**
10312          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10313          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10314          * automatically generated with the specified attributes.
10315          * @param {HTMLElement} insertBefore (optional) a child element of this element
10316          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10317          * @return {Roo.Element} The new child element
10318          */
10319         createChild: function(config, insertBefore, returnDom){
10320             config = config || {tag:'div'};
10321             if(insertBefore){
10322                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10323             }
10324             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10325         },
10326
10327         /**
10328          * Appends this element to the passed element
10329          * @param {String/HTMLElement/Element} el The new parent element
10330          * @return {Roo.Element} this
10331          */
10332         appendTo: function(el){
10333             el = Roo.getDom(el);
10334             el.appendChild(this.dom);
10335             return this;
10336         },
10337
10338         /**
10339          * Inserts this element before the passed element in the DOM
10340          * @param {String/HTMLElement/Element} el The element to insert before
10341          * @return {Roo.Element} this
10342          */
10343         insertBefore: function(el){
10344             el = Roo.getDom(el);
10345             el.parentNode.insertBefore(this.dom, el);
10346             return this;
10347         },
10348
10349         /**
10350          * Inserts this element after the passed element in the DOM
10351          * @param {String/HTMLElement/Element} el The element to insert after
10352          * @return {Roo.Element} this
10353          */
10354         insertAfter: function(el){
10355             el = Roo.getDom(el);
10356             el.parentNode.insertBefore(this.dom, el.nextSibling);
10357             return this;
10358         },
10359
10360         /**
10361          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10362          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10363          * @return {Roo.Element} The new child
10364          */
10365         insertFirst: function(el, returnDom){
10366             el = el || {};
10367             if(typeof el == 'object' && !el.nodeType){ // dh config
10368                 return this.createChild(el, this.dom.firstChild, returnDom);
10369             }else{
10370                 el = Roo.getDom(el);
10371                 this.dom.insertBefore(el, this.dom.firstChild);
10372                 return !returnDom ? Roo.get(el) : el;
10373             }
10374         },
10375
10376         /**
10377          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10378          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10379          * @param {String} where (optional) 'before' or 'after' defaults to before
10380          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10381          * @return {Roo.Element} the inserted Element
10382          */
10383         insertSibling: function(el, where, returnDom){
10384             where = where ? where.toLowerCase() : 'before';
10385             el = el || {};
10386             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10387
10388             if(typeof el == 'object' && !el.nodeType){ // dh config
10389                 if(where == 'after' && !this.dom.nextSibling){
10390                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10391                 }else{
10392                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10393                 }
10394
10395             }else{
10396                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10397                             where == 'before' ? this.dom : this.dom.nextSibling);
10398                 if(!returnDom){
10399                     rt = Roo.get(rt);
10400                 }
10401             }
10402             return rt;
10403         },
10404
10405         /**
10406          * Creates and wraps this element with another element
10407          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10408          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10409          * @return {HTMLElement/Element} The newly created wrapper element
10410          */
10411         wrap: function(config, returnDom){
10412             if(!config){
10413                 config = {tag: "div"};
10414             }
10415             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10416             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10417             return newEl;
10418         },
10419
10420         /**
10421          * Replaces the passed element with this element
10422          * @param {String/HTMLElement/Element} el The element to replace
10423          * @return {Roo.Element} this
10424          */
10425         replace: function(el){
10426             el = Roo.get(el);
10427             this.insertBefore(el);
10428             el.remove();
10429             return this;
10430         },
10431
10432         /**
10433          * Inserts an html fragment into this element
10434          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10435          * @param {String} html The HTML fragment
10436          * @param {Boolean} returnEl True to return an Roo.Element
10437          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10438          */
10439         insertHtml : function(where, html, returnEl){
10440             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10441             return returnEl ? Roo.get(el) : el;
10442         },
10443
10444         /**
10445          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10446          * @param {Object} o The object with the attributes
10447          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10448          * @return {Roo.Element} this
10449          */
10450         set : function(o, useSet){
10451             var el = this.dom;
10452             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10453             for(var attr in o){
10454                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10455                 if(attr=="cls"){
10456                     el.className = o["cls"];
10457                 }else{
10458                     if(useSet) {
10459                         el.setAttribute(attr, o[attr]);
10460                     } else {
10461                         el[attr] = o[attr];
10462                     }
10463                 }
10464             }
10465             if(o.style){
10466                 Roo.DomHelper.applyStyles(el, o.style);
10467             }
10468             return this;
10469         },
10470
10471         /**
10472          * Convenience method for constructing a KeyMap
10473          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
10474          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10475          * @param {Function} fn The function to call
10476          * @param {Object} scope (optional) The scope of the function
10477          * @return {Roo.KeyMap} The KeyMap created
10478          */
10479         addKeyListener : function(key, fn, scope){
10480             var config;
10481             if(typeof key != "object" || key instanceof Array){
10482                 config = {
10483                     key: key,
10484                     fn: fn,
10485                     scope: scope
10486                 };
10487             }else{
10488                 config = {
10489                     key : key.key,
10490                     shift : key.shift,
10491                     ctrl : key.ctrl,
10492                     alt : key.alt,
10493                     fn: fn,
10494                     scope: scope
10495                 };
10496             }
10497             return new Roo.KeyMap(this, config);
10498         },
10499
10500         /**
10501          * Creates a KeyMap for this element
10502          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10503          * @return {Roo.KeyMap} The KeyMap created
10504          */
10505         addKeyMap : function(config){
10506             return new Roo.KeyMap(this, config);
10507         },
10508
10509         /**
10510          * Returns true if this element is scrollable.
10511          * @return {Boolean}
10512          */
10513          isScrollable : function(){
10514             var dom = this.dom;
10515             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10516         },
10517
10518         /**
10519          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
10520          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10521          * @param {Number} value The new scroll value
10522          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10523          * @return {Element} this
10524          */
10525
10526         scrollTo : function(side, value, animate){
10527             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10528             if(!animate || !A){
10529                 this.dom[prop] = value;
10530             }else{
10531                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10532                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10533             }
10534             return this;
10535         },
10536
10537         /**
10538          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10539          * within this element's scrollable range.
10540          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10541          * @param {Number} distance How far to scroll the element in pixels
10542          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10543          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10544          * was scrolled as far as it could go.
10545          */
10546          scroll : function(direction, distance, animate){
10547              if(!this.isScrollable()){
10548                  return;
10549              }
10550              var el = this.dom;
10551              var l = el.scrollLeft, t = el.scrollTop;
10552              var w = el.scrollWidth, h = el.scrollHeight;
10553              var cw = el.clientWidth, ch = el.clientHeight;
10554              direction = direction.toLowerCase();
10555              var scrolled = false;
10556              var a = this.preanim(arguments, 2);
10557              switch(direction){
10558                  case "l":
10559                  case "left":
10560                      if(w - l > cw){
10561                          var v = Math.min(l + distance, w-cw);
10562                          this.scrollTo("left", v, a);
10563                          scrolled = true;
10564                      }
10565                      break;
10566                 case "r":
10567                 case "right":
10568                      if(l > 0){
10569                          var v = Math.max(l - distance, 0);
10570                          this.scrollTo("left", v, a);
10571                          scrolled = true;
10572                      }
10573                      break;
10574                 case "t":
10575                 case "top":
10576                 case "up":
10577                      if(t > 0){
10578                          var v = Math.max(t - distance, 0);
10579                          this.scrollTo("top", v, a);
10580                          scrolled = true;
10581                      }
10582                      break;
10583                 case "b":
10584                 case "bottom":
10585                 case "down":
10586                      if(h - t > ch){
10587                          var v = Math.min(t + distance, h-ch);
10588                          this.scrollTo("top", v, a);
10589                          scrolled = true;
10590                      }
10591                      break;
10592              }
10593              return scrolled;
10594         },
10595
10596         /**
10597          * Translates the passed page coordinates into left/top css values for this element
10598          * @param {Number/Array} x The page x or an array containing [x, y]
10599          * @param {Number} y The page y
10600          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10601          */
10602         translatePoints : function(x, y){
10603             if(typeof x == 'object' || x instanceof Array){
10604                 y = x[1]; x = x[0];
10605             }
10606             var p = this.getStyle('position');
10607             var o = this.getXY();
10608
10609             var l = parseInt(this.getStyle('left'), 10);
10610             var t = parseInt(this.getStyle('top'), 10);
10611
10612             if(isNaN(l)){
10613                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10614             }
10615             if(isNaN(t)){
10616                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10617             }
10618
10619             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10620         },
10621
10622         /**
10623          * Returns the current scroll position of the element.
10624          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10625          */
10626         getScroll : function(){
10627             var d = this.dom, doc = document;
10628             if(d == doc || d == doc.body){
10629                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10630                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10631                 return {left: l, top: t};
10632             }else{
10633                 return {left: d.scrollLeft, top: d.scrollTop};
10634             }
10635         },
10636
10637         /**
10638          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10639          * are convert to standard 6 digit hex color.
10640          * @param {String} attr The css attribute
10641          * @param {String} defaultValue The default value to use when a valid color isn't found
10642          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10643          * YUI color anims.
10644          */
10645         getColor : function(attr, defaultValue, prefix){
10646             var v = this.getStyle(attr);
10647             if(!v || v == "transparent" || v == "inherit") {
10648                 return defaultValue;
10649             }
10650             var color = typeof prefix == "undefined" ? "#" : prefix;
10651             if(v.substr(0, 4) == "rgb("){
10652                 var rvs = v.slice(4, v.length -1).split(",");
10653                 for(var i = 0; i < 3; i++){
10654                     var h = parseInt(rvs[i]).toString(16);
10655                     if(h < 16){
10656                         h = "0" + h;
10657                     }
10658                     color += h;
10659                 }
10660             } else {
10661                 if(v.substr(0, 1) == "#"){
10662                     if(v.length == 4) {
10663                         for(var i = 1; i < 4; i++){
10664                             var c = v.charAt(i);
10665                             color +=  c + c;
10666                         }
10667                     }else if(v.length == 7){
10668                         color += v.substr(1);
10669                     }
10670                 }
10671             }
10672             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10673         },
10674
10675         /**
10676          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10677          * gradient background, rounded corners and a 4-way shadow.
10678          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10679          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10680          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10681          * @return {Roo.Element} this
10682          */
10683         boxWrap : function(cls){
10684             cls = cls || 'x-box';
10685             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10686             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10687             return el;
10688         },
10689
10690         /**
10691          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10692          * @param {String} namespace The namespace in which to look for the attribute
10693          * @param {String} name The attribute name
10694          * @return {String} The attribute value
10695          */
10696         getAttributeNS : Roo.isIE ? function(ns, name){
10697             var d = this.dom;
10698             var type = typeof d[ns+":"+name];
10699             if(type != 'undefined' && type != 'unknown'){
10700                 return d[ns+":"+name];
10701             }
10702             return d[name];
10703         } : function(ns, name){
10704             var d = this.dom;
10705             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10706         },
10707         
10708         
10709         /**
10710          * Sets or Returns the value the dom attribute value
10711          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10712          * @param {String} value (optional) The value to set the attribute to
10713          * @return {String} The attribute value
10714          */
10715         attr : function(name){
10716             if (arguments.length > 1) {
10717                 this.dom.setAttribute(name, arguments[1]);
10718                 return arguments[1];
10719             }
10720             if (typeof(name) == 'object') {
10721                 for(var i in name) {
10722                     this.attr(i, name[i]);
10723                 }
10724                 return name;
10725             }
10726             
10727             
10728             if (!this.dom.hasAttribute(name)) {
10729                 return undefined;
10730             }
10731             return this.dom.getAttribute(name);
10732         }
10733         
10734         
10735         
10736     };
10737
10738     var ep = El.prototype;
10739
10740     /**
10741      * Appends an event handler (Shorthand for addListener)
10742      * @param {String}   eventName     The type of event to append
10743      * @param {Function} fn        The method the event invokes
10744      * @param {Object} scope       (optional) The scope (this object) of the fn
10745      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10746      * @method
10747      */
10748     ep.on = ep.addListener;
10749         // backwards compat
10750     ep.mon = ep.addListener;
10751
10752     /**
10753      * Removes an event handler from this element (shorthand for removeListener)
10754      * @param {String} eventName the type of event to remove
10755      * @param {Function} fn the method the event invokes
10756      * @return {Roo.Element} this
10757      * @method
10758      */
10759     ep.un = ep.removeListener;
10760
10761     /**
10762      * true to automatically adjust width and height settings for box-model issues (default to true)
10763      */
10764     ep.autoBoxAdjust = true;
10765
10766     // private
10767     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10768
10769     // private
10770     El.addUnits = function(v, defaultUnit){
10771         if(v === "" || v == "auto"){
10772             return v;
10773         }
10774         if(v === undefined){
10775             return '';
10776         }
10777         if(typeof v == "number" || !El.unitPattern.test(v)){
10778             return v + (defaultUnit || 'px');
10779         }
10780         return v;
10781     };
10782
10783     // special markup used throughout Roo when box wrapping elements
10784     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
10785     /**
10786      * Visibility mode constant - Use visibility to hide element
10787      * @static
10788      * @type Number
10789      */
10790     El.VISIBILITY = 1;
10791     /**
10792      * Visibility mode constant - Use display to hide element
10793      * @static
10794      * @type Number
10795      */
10796     El.DISPLAY = 2;
10797
10798     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10799     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10800     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10801
10802
10803
10804     /**
10805      * @private
10806      */
10807     El.cache = {};
10808
10809     var docEl;
10810
10811     /**
10812      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10813      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10814      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10815      * @return {Element} The Element object
10816      * @static
10817      */
10818     El.get = function(el){
10819         var ex, elm, id;
10820         if(!el){ return null; }
10821         if(typeof el == "string"){ // element id
10822             if(!(elm = document.getElementById(el))){
10823                 return null;
10824             }
10825             if(ex = El.cache[el]){
10826                 ex.dom = elm;
10827             }else{
10828                 ex = El.cache[el] = new El(elm);
10829             }
10830             return ex;
10831         }else if(el.tagName){ // dom element
10832             if(!(id = el.id)){
10833                 id = Roo.id(el);
10834             }
10835             if(ex = El.cache[id]){
10836                 ex.dom = el;
10837             }else{
10838                 ex = El.cache[id] = new El(el);
10839             }
10840             return ex;
10841         }else if(el instanceof El){
10842             if(el != docEl){
10843                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10844                                                               // catch case where it hasn't been appended
10845                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10846             }
10847             return el;
10848         }else if(el.isComposite){
10849             return el;
10850         }else if(el instanceof Array){
10851             return El.select(el);
10852         }else if(el == document){
10853             // create a bogus element object representing the document object
10854             if(!docEl){
10855                 var f = function(){};
10856                 f.prototype = El.prototype;
10857                 docEl = new f();
10858                 docEl.dom = document;
10859             }
10860             return docEl;
10861         }
10862         return null;
10863     };
10864
10865     // private
10866     El.uncache = function(el){
10867         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10868             if(a[i]){
10869                 delete El.cache[a[i].id || a[i]];
10870             }
10871         }
10872     };
10873
10874     // private
10875     // Garbage collection - uncache elements/purge listeners on orphaned elements
10876     // so we don't hold a reference and cause the browser to retain them
10877     El.garbageCollect = function(){
10878         if(!Roo.enableGarbageCollector){
10879             clearInterval(El.collectorThread);
10880             return;
10881         }
10882         for(var eid in El.cache){
10883             var el = El.cache[eid], d = el.dom;
10884             // -------------------------------------------------------
10885             // Determining what is garbage:
10886             // -------------------------------------------------------
10887             // !d
10888             // dom node is null, definitely garbage
10889             // -------------------------------------------------------
10890             // !d.parentNode
10891             // no parentNode == direct orphan, definitely garbage
10892             // -------------------------------------------------------
10893             // !d.offsetParent && !document.getElementById(eid)
10894             // display none elements have no offsetParent so we will
10895             // also try to look it up by it's id. However, check
10896             // offsetParent first so we don't do unneeded lookups.
10897             // This enables collection of elements that are not orphans
10898             // directly, but somewhere up the line they have an orphan
10899             // parent.
10900             // -------------------------------------------------------
10901             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10902                 delete El.cache[eid];
10903                 if(d && Roo.enableListenerCollection){
10904                     E.purgeElement(d);
10905                 }
10906             }
10907         }
10908     }
10909     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10910
10911
10912     // dom is optional
10913     El.Flyweight = function(dom){
10914         this.dom = dom;
10915     };
10916     El.Flyweight.prototype = El.prototype;
10917
10918     El._flyweights = {};
10919     /**
10920      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10921      * the dom node can be overwritten by other code.
10922      * @param {String/HTMLElement} el The dom node or id
10923      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10924      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10925      * @static
10926      * @return {Element} The shared Element object
10927      */
10928     El.fly = function(el, named){
10929         named = named || '_global';
10930         el = Roo.getDom(el);
10931         if(!el){
10932             return null;
10933         }
10934         if(!El._flyweights[named]){
10935             El._flyweights[named] = new El.Flyweight();
10936         }
10937         El._flyweights[named].dom = el;
10938         return El._flyweights[named];
10939     };
10940
10941     /**
10942      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10943      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10944      * Shorthand of {@link Roo.Element#get}
10945      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10946      * @return {Element} The Element object
10947      * @member Roo
10948      * @method get
10949      */
10950     Roo.get = El.get;
10951     /**
10952      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10953      * the dom node can be overwritten by other code.
10954      * Shorthand of {@link Roo.Element#fly}
10955      * @param {String/HTMLElement} el The dom node or id
10956      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10957      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10958      * @static
10959      * @return {Element} The shared Element object
10960      * @member Roo
10961      * @method fly
10962      */
10963     Roo.fly = El.fly;
10964
10965     // speedy lookup for elements never to box adjust
10966     var noBoxAdjust = Roo.isStrict ? {
10967         select:1
10968     } : {
10969         input:1, select:1, textarea:1
10970     };
10971     if(Roo.isIE || Roo.isGecko){
10972         noBoxAdjust['button'] = 1;
10973     }
10974
10975
10976     Roo.EventManager.on(window, 'unload', function(){
10977         delete El.cache;
10978         delete El._flyweights;
10979     });
10980 })();
10981
10982
10983
10984
10985 if(Roo.DomQuery){
10986     Roo.Element.selectorFunction = Roo.DomQuery.select;
10987 }
10988
10989 Roo.Element.select = function(selector, unique, root){
10990     var els;
10991     if(typeof selector == "string"){
10992         els = Roo.Element.selectorFunction(selector, root);
10993     }else if(selector.length !== undefined){
10994         els = selector;
10995     }else{
10996         throw "Invalid selector";
10997     }
10998     if(unique === true){
10999         return new Roo.CompositeElement(els);
11000     }else{
11001         return new Roo.CompositeElementLite(els);
11002     }
11003 };
11004 /**
11005  * Selects elements based on the passed CSS selector to enable working on them as 1.
11006  * @param {String/Array} selector The CSS selector or an array of elements
11007  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11008  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11009  * @return {CompositeElementLite/CompositeElement}
11010  * @member Roo
11011  * @method select
11012  */
11013 Roo.select = Roo.Element.select;
11014
11015
11016
11017
11018
11019
11020
11021
11022
11023
11024
11025
11026
11027
11028 /*
11029  * Based on:
11030  * Ext JS Library 1.1.1
11031  * Copyright(c) 2006-2007, Ext JS, LLC.
11032  *
11033  * Originally Released Under LGPL - original licence link has changed is not relivant.
11034  *
11035  * Fork - LGPL
11036  * <script type="text/javascript">
11037  */
11038
11039
11040
11041 //Notifies Element that fx methods are available
11042 Roo.enableFx = true;
11043
11044 /**
11045  * @class Roo.Fx
11046  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11047  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11048  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11049  * Element effects to work.</p><br/>
11050  *
11051  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11052  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11053  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11054  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11055  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11056  * expected results and should be done with care.</p><br/>
11057  *
11058  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11059  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11060 <pre>
11061 Value  Description
11062 -----  -----------------------------
11063 tl     The top left corner
11064 t      The center of the top edge
11065 tr     The top right corner
11066 l      The center of the left edge
11067 r      The center of the right edge
11068 bl     The bottom left corner
11069 b      The center of the bottom edge
11070 br     The bottom right corner
11071 </pre>
11072  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11073  * below are common options that can be passed to any Fx method.</b>
11074  * @cfg {Function} callback A function called when the effect is finished
11075  * @cfg {Object} scope The scope of the effect function
11076  * @cfg {String} easing A valid Easing value for the effect
11077  * @cfg {String} afterCls A css class to apply after the effect
11078  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11079  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11080  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11081  * effects that end with the element being visually hidden, ignored otherwise)
11082  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11083  * a function which returns such a specification that will be applied to the Element after the effect finishes
11084  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11085  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
11086  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11087  */
11088 Roo.Fx = {
11089         /**
11090          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11091          * origin for the slide effect.  This function automatically handles wrapping the element with
11092          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11093          * Usage:
11094          *<pre><code>
11095 // default: slide the element in from the top
11096 el.slideIn();
11097
11098 // custom: slide the element in from the right with a 2-second duration
11099 el.slideIn('r', { duration: 2 });
11100
11101 // common config options shown with default values
11102 el.slideIn('t', {
11103     easing: 'easeOut',
11104     duration: .5
11105 });
11106 </code></pre>
11107          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11108          * @param {Object} options (optional) Object literal with any of the Fx config options
11109          * @return {Roo.Element} The Element
11110          */
11111     slideIn : function(anchor, o){
11112         var el = this.getFxEl();
11113         o = o || {};
11114
11115         el.queueFx(o, function(){
11116
11117             anchor = anchor || "t";
11118
11119             // fix display to visibility
11120             this.fixDisplay();
11121
11122             // restore values after effect
11123             var r = this.getFxRestore();
11124             var b = this.getBox();
11125             // fixed size for slide
11126             this.setSize(b);
11127
11128             // wrap if needed
11129             var wrap = this.fxWrap(r.pos, o, "hidden");
11130
11131             var st = this.dom.style;
11132             st.visibility = "visible";
11133             st.position = "absolute";
11134
11135             // clear out temp styles after slide and unwrap
11136             var after = function(){
11137                 el.fxUnwrap(wrap, r.pos, o);
11138                 st.width = r.width;
11139                 st.height = r.height;
11140                 el.afterFx(o);
11141             };
11142             // time to calc the positions
11143             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11144
11145             switch(anchor.toLowerCase()){
11146                 case "t":
11147                     wrap.setSize(b.width, 0);
11148                     st.left = st.bottom = "0";
11149                     a = {height: bh};
11150                 break;
11151                 case "l":
11152                     wrap.setSize(0, b.height);
11153                     st.right = st.top = "0";
11154                     a = {width: bw};
11155                 break;
11156                 case "r":
11157                     wrap.setSize(0, b.height);
11158                     wrap.setX(b.right);
11159                     st.left = st.top = "0";
11160                     a = {width: bw, points: pt};
11161                 break;
11162                 case "b":
11163                     wrap.setSize(b.width, 0);
11164                     wrap.setY(b.bottom);
11165                     st.left = st.top = "0";
11166                     a = {height: bh, points: pt};
11167                 break;
11168                 case "tl":
11169                     wrap.setSize(0, 0);
11170                     st.right = st.bottom = "0";
11171                     a = {width: bw, height: bh};
11172                 break;
11173                 case "bl":
11174                     wrap.setSize(0, 0);
11175                     wrap.setY(b.y+b.height);
11176                     st.right = st.top = "0";
11177                     a = {width: bw, height: bh, points: pt};
11178                 break;
11179                 case "br":
11180                     wrap.setSize(0, 0);
11181                     wrap.setXY([b.right, b.bottom]);
11182                     st.left = st.top = "0";
11183                     a = {width: bw, height: bh, points: pt};
11184                 break;
11185                 case "tr":
11186                     wrap.setSize(0, 0);
11187                     wrap.setX(b.x+b.width);
11188                     st.left = st.bottom = "0";
11189                     a = {width: bw, height: bh, points: pt};
11190                 break;
11191             }
11192             this.dom.style.visibility = "visible";
11193             wrap.show();
11194
11195             arguments.callee.anim = wrap.fxanim(a,
11196                 o,
11197                 'motion',
11198                 .5,
11199                 'easeOut', after);
11200         });
11201         return this;
11202     },
11203     
11204         /**
11205          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11206          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11207          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11208          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11209          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11210          * Usage:
11211          *<pre><code>
11212 // default: slide the element out to the top
11213 el.slideOut();
11214
11215 // custom: slide the element out to the right with a 2-second duration
11216 el.slideOut('r', { duration: 2 });
11217
11218 // common config options shown with default values
11219 el.slideOut('t', {
11220     easing: 'easeOut',
11221     duration: .5,
11222     remove: false,
11223     useDisplay: false
11224 });
11225 </code></pre>
11226          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11227          * @param {Object} options (optional) Object literal with any of the Fx config options
11228          * @return {Roo.Element} The Element
11229          */
11230     slideOut : function(anchor, o){
11231         var el = this.getFxEl();
11232         o = o || {};
11233
11234         el.queueFx(o, function(){
11235
11236             anchor = anchor || "t";
11237
11238             // restore values after effect
11239             var r = this.getFxRestore();
11240             
11241             var b = this.getBox();
11242             // fixed size for slide
11243             this.setSize(b);
11244
11245             // wrap if needed
11246             var wrap = this.fxWrap(r.pos, o, "visible");
11247
11248             var st = this.dom.style;
11249             st.visibility = "visible";
11250             st.position = "absolute";
11251
11252             wrap.setSize(b);
11253
11254             var after = function(){
11255                 if(o.useDisplay){
11256                     el.setDisplayed(false);
11257                 }else{
11258                     el.hide();
11259                 }
11260
11261                 el.fxUnwrap(wrap, r.pos, o);
11262
11263                 st.width = r.width;
11264                 st.height = r.height;
11265
11266                 el.afterFx(o);
11267             };
11268
11269             var a, zero = {to: 0};
11270             switch(anchor.toLowerCase()){
11271                 case "t":
11272                     st.left = st.bottom = "0";
11273                     a = {height: zero};
11274                 break;
11275                 case "l":
11276                     st.right = st.top = "0";
11277                     a = {width: zero};
11278                 break;
11279                 case "r":
11280                     st.left = st.top = "0";
11281                     a = {width: zero, points: {to:[b.right, b.y]}};
11282                 break;
11283                 case "b":
11284                     st.left = st.top = "0";
11285                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11286                 break;
11287                 case "tl":
11288                     st.right = st.bottom = "0";
11289                     a = {width: zero, height: zero};
11290                 break;
11291                 case "bl":
11292                     st.right = st.top = "0";
11293                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11294                 break;
11295                 case "br":
11296                     st.left = st.top = "0";
11297                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11298                 break;
11299                 case "tr":
11300                     st.left = st.bottom = "0";
11301                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11302                 break;
11303             }
11304
11305             arguments.callee.anim = wrap.fxanim(a,
11306                 o,
11307                 'motion',
11308                 .5,
11309                 "easeOut", after);
11310         });
11311         return this;
11312     },
11313
11314         /**
11315          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11316          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11317          * The element must be removed from the DOM using the 'remove' config option if desired.
11318          * Usage:
11319          *<pre><code>
11320 // default
11321 el.puff();
11322
11323 // common config options shown with default values
11324 el.puff({
11325     easing: 'easeOut',
11326     duration: .5,
11327     remove: false,
11328     useDisplay: false
11329 });
11330 </code></pre>
11331          * @param {Object} options (optional) Object literal with any of the Fx config options
11332          * @return {Roo.Element} The Element
11333          */
11334     puff : function(o){
11335         var el = this.getFxEl();
11336         o = o || {};
11337
11338         el.queueFx(o, function(){
11339             this.clearOpacity();
11340             this.show();
11341
11342             // restore values after effect
11343             var r = this.getFxRestore();
11344             var st = this.dom.style;
11345
11346             var after = function(){
11347                 if(o.useDisplay){
11348                     el.setDisplayed(false);
11349                 }else{
11350                     el.hide();
11351                 }
11352
11353                 el.clearOpacity();
11354
11355                 el.setPositioning(r.pos);
11356                 st.width = r.width;
11357                 st.height = r.height;
11358                 st.fontSize = '';
11359                 el.afterFx(o);
11360             };
11361
11362             var width = this.getWidth();
11363             var height = this.getHeight();
11364
11365             arguments.callee.anim = this.fxanim({
11366                     width : {to: this.adjustWidth(width * 2)},
11367                     height : {to: this.adjustHeight(height * 2)},
11368                     points : {by: [-(width * .5), -(height * .5)]},
11369                     opacity : {to: 0},
11370                     fontSize: {to:200, unit: "%"}
11371                 },
11372                 o,
11373                 'motion',
11374                 .5,
11375                 "easeOut", after);
11376         });
11377         return this;
11378     },
11379
11380         /**
11381          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11382          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11383          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11384          * Usage:
11385          *<pre><code>
11386 // default
11387 el.switchOff();
11388
11389 // all config options shown with default values
11390 el.switchOff({
11391     easing: 'easeIn',
11392     duration: .3,
11393     remove: false,
11394     useDisplay: false
11395 });
11396 </code></pre>
11397          * @param {Object} options (optional) Object literal with any of the Fx config options
11398          * @return {Roo.Element} The Element
11399          */
11400     switchOff : function(o){
11401         var el = this.getFxEl();
11402         o = o || {};
11403
11404         el.queueFx(o, function(){
11405             this.clearOpacity();
11406             this.clip();
11407
11408             // restore values after effect
11409             var r = this.getFxRestore();
11410             var st = this.dom.style;
11411
11412             var after = function(){
11413                 if(o.useDisplay){
11414                     el.setDisplayed(false);
11415                 }else{
11416                     el.hide();
11417                 }
11418
11419                 el.clearOpacity();
11420                 el.setPositioning(r.pos);
11421                 st.width = r.width;
11422                 st.height = r.height;
11423
11424                 el.afterFx(o);
11425             };
11426
11427             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11428                 this.clearOpacity();
11429                 (function(){
11430                     this.fxanim({
11431                         height:{to:1},
11432                         points:{by:[0, this.getHeight() * .5]}
11433                     }, o, 'motion', 0.3, 'easeIn', after);
11434                 }).defer(100, this);
11435             });
11436         });
11437         return this;
11438     },
11439
11440     /**
11441      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11442      * changed using the "attr" config option) and then fading back to the original color. If no original
11443      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11444      * Usage:
11445 <pre><code>
11446 // default: highlight background to yellow
11447 el.highlight();
11448
11449 // custom: highlight foreground text to blue for 2 seconds
11450 el.highlight("0000ff", { attr: 'color', duration: 2 });
11451
11452 // common config options shown with default values
11453 el.highlight("ffff9c", {
11454     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11455     endColor: (current color) or "ffffff",
11456     easing: 'easeIn',
11457     duration: 1
11458 });
11459 </code></pre>
11460      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11461      * @param {Object} options (optional) Object literal with any of the Fx config options
11462      * @return {Roo.Element} The Element
11463      */ 
11464     highlight : function(color, o){
11465         var el = this.getFxEl();
11466         o = o || {};
11467
11468         el.queueFx(o, function(){
11469             color = color || "ffff9c";
11470             attr = o.attr || "backgroundColor";
11471
11472             this.clearOpacity();
11473             this.show();
11474
11475             var origColor = this.getColor(attr);
11476             var restoreColor = this.dom.style[attr];
11477             endColor = (o.endColor || origColor) || "ffffff";
11478
11479             var after = function(){
11480                 el.dom.style[attr] = restoreColor;
11481                 el.afterFx(o);
11482             };
11483
11484             var a = {};
11485             a[attr] = {from: color, to: endColor};
11486             arguments.callee.anim = this.fxanim(a,
11487                 o,
11488                 'color',
11489                 1,
11490                 'easeIn', after);
11491         });
11492         return this;
11493     },
11494
11495    /**
11496     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11497     * Usage:
11498 <pre><code>
11499 // default: a single light blue ripple
11500 el.frame();
11501
11502 // custom: 3 red ripples lasting 3 seconds total
11503 el.frame("ff0000", 3, { duration: 3 });
11504
11505 // common config options shown with default values
11506 el.frame("C3DAF9", 1, {
11507     duration: 1 //duration of entire animation (not each individual ripple)
11508     // Note: Easing is not configurable and will be ignored if included
11509 });
11510 </code></pre>
11511     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11512     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11513     * @param {Object} options (optional) Object literal with any of the Fx config options
11514     * @return {Roo.Element} The Element
11515     */
11516     frame : function(color, count, o){
11517         var el = this.getFxEl();
11518         o = o || {};
11519
11520         el.queueFx(o, function(){
11521             color = color || "#C3DAF9";
11522             if(color.length == 6){
11523                 color = "#" + color;
11524             }
11525             count = count || 1;
11526             duration = o.duration || 1;
11527             this.show();
11528
11529             var b = this.getBox();
11530             var animFn = function(){
11531                 var proxy = this.createProxy({
11532
11533                      style:{
11534                         visbility:"hidden",
11535                         position:"absolute",
11536                         "z-index":"35000", // yee haw
11537                         border:"0px solid " + color
11538                      }
11539                   });
11540                 var scale = Roo.isBorderBox ? 2 : 1;
11541                 proxy.animate({
11542                     top:{from:b.y, to:b.y - 20},
11543                     left:{from:b.x, to:b.x - 20},
11544                     borderWidth:{from:0, to:10},
11545                     opacity:{from:1, to:0},
11546                     height:{from:b.height, to:(b.height + (20*scale))},
11547                     width:{from:b.width, to:(b.width + (20*scale))}
11548                 }, duration, function(){
11549                     proxy.remove();
11550                 });
11551                 if(--count > 0){
11552                      animFn.defer((duration/2)*1000, this);
11553                 }else{
11554                     el.afterFx(o);
11555                 }
11556             };
11557             animFn.call(this);
11558         });
11559         return this;
11560     },
11561
11562    /**
11563     * Creates a pause before any subsequent queued effects begin.  If there are
11564     * no effects queued after the pause it will have no effect.
11565     * Usage:
11566 <pre><code>
11567 el.pause(1);
11568 </code></pre>
11569     * @param {Number} seconds The length of time to pause (in seconds)
11570     * @return {Roo.Element} The Element
11571     */
11572     pause : function(seconds){
11573         var el = this.getFxEl();
11574         var o = {};
11575
11576         el.queueFx(o, function(){
11577             setTimeout(function(){
11578                 el.afterFx(o);
11579             }, seconds * 1000);
11580         });
11581         return this;
11582     },
11583
11584    /**
11585     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11586     * using the "endOpacity" config option.
11587     * Usage:
11588 <pre><code>
11589 // default: fade in from opacity 0 to 100%
11590 el.fadeIn();
11591
11592 // custom: fade in from opacity 0 to 75% over 2 seconds
11593 el.fadeIn({ endOpacity: .75, duration: 2});
11594
11595 // common config options shown with default values
11596 el.fadeIn({
11597     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11598     easing: 'easeOut',
11599     duration: .5
11600 });
11601 </code></pre>
11602     * @param {Object} options (optional) Object literal with any of the Fx config options
11603     * @return {Roo.Element} The Element
11604     */
11605     fadeIn : function(o){
11606         var el = this.getFxEl();
11607         o = o || {};
11608         el.queueFx(o, function(){
11609             this.setOpacity(0);
11610             this.fixDisplay();
11611             this.dom.style.visibility = 'visible';
11612             var to = o.endOpacity || 1;
11613             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11614                 o, null, .5, "easeOut", function(){
11615                 if(to == 1){
11616                     this.clearOpacity();
11617                 }
11618                 el.afterFx(o);
11619             });
11620         });
11621         return this;
11622     },
11623
11624    /**
11625     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11626     * using the "endOpacity" config option.
11627     * Usage:
11628 <pre><code>
11629 // default: fade out from the element's current opacity to 0
11630 el.fadeOut();
11631
11632 // custom: fade out from the element's current opacity to 25% over 2 seconds
11633 el.fadeOut({ endOpacity: .25, duration: 2});
11634
11635 // common config options shown with default values
11636 el.fadeOut({
11637     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11638     easing: 'easeOut',
11639     duration: .5
11640     remove: false,
11641     useDisplay: false
11642 });
11643 </code></pre>
11644     * @param {Object} options (optional) Object literal with any of the Fx config options
11645     * @return {Roo.Element} The Element
11646     */
11647     fadeOut : function(o){
11648         var el = this.getFxEl();
11649         o = o || {};
11650         el.queueFx(o, function(){
11651             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11652                 o, null, .5, "easeOut", function(){
11653                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11654                      this.dom.style.display = "none";
11655                 }else{
11656                      this.dom.style.visibility = "hidden";
11657                 }
11658                 this.clearOpacity();
11659                 el.afterFx(o);
11660             });
11661         });
11662         return this;
11663     },
11664
11665    /**
11666     * Animates the transition of an element's dimensions from a starting height/width
11667     * to an ending height/width.
11668     * Usage:
11669 <pre><code>
11670 // change height and width to 100x100 pixels
11671 el.scale(100, 100);
11672
11673 // common config options shown with default values.  The height and width will default to
11674 // the element's existing values if passed as null.
11675 el.scale(
11676     [element's width],
11677     [element's height], {
11678     easing: 'easeOut',
11679     duration: .35
11680 });
11681 </code></pre>
11682     * @param {Number} width  The new width (pass undefined to keep the original width)
11683     * @param {Number} height  The new height (pass undefined to keep the original height)
11684     * @param {Object} options (optional) Object literal with any of the Fx config options
11685     * @return {Roo.Element} The Element
11686     */
11687     scale : function(w, h, o){
11688         this.shift(Roo.apply({}, o, {
11689             width: w,
11690             height: h
11691         }));
11692         return this;
11693     },
11694
11695    /**
11696     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11697     * Any of these properties not specified in the config object will not be changed.  This effect 
11698     * requires that at least one new dimension, position or opacity setting must be passed in on
11699     * the config object in order for the function to have any effect.
11700     * Usage:
11701 <pre><code>
11702 // slide the element horizontally to x position 200 while changing the height and opacity
11703 el.shift({ x: 200, height: 50, opacity: .8 });
11704
11705 // common config options shown with default values.
11706 el.shift({
11707     width: [element's width],
11708     height: [element's height],
11709     x: [element's x position],
11710     y: [element's y position],
11711     opacity: [element's opacity],
11712     easing: 'easeOut',
11713     duration: .35
11714 });
11715 </code></pre>
11716     * @param {Object} options  Object literal with any of the Fx config options
11717     * @return {Roo.Element} The Element
11718     */
11719     shift : function(o){
11720         var el = this.getFxEl();
11721         o = o || {};
11722         el.queueFx(o, function(){
11723             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11724             if(w !== undefined){
11725                 a.width = {to: this.adjustWidth(w)};
11726             }
11727             if(h !== undefined){
11728                 a.height = {to: this.adjustHeight(h)};
11729             }
11730             if(x !== undefined || y !== undefined){
11731                 a.points = {to: [
11732                     x !== undefined ? x : this.getX(),
11733                     y !== undefined ? y : this.getY()
11734                 ]};
11735             }
11736             if(op !== undefined){
11737                 a.opacity = {to: op};
11738             }
11739             if(o.xy !== undefined){
11740                 a.points = {to: o.xy};
11741             }
11742             arguments.callee.anim = this.fxanim(a,
11743                 o, 'motion', .35, "easeOut", function(){
11744                 el.afterFx(o);
11745             });
11746         });
11747         return this;
11748     },
11749
11750         /**
11751          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11752          * ending point of the effect.
11753          * Usage:
11754          *<pre><code>
11755 // default: slide the element downward while fading out
11756 el.ghost();
11757
11758 // custom: slide the element out to the right with a 2-second duration
11759 el.ghost('r', { duration: 2 });
11760
11761 // common config options shown with default values
11762 el.ghost('b', {
11763     easing: 'easeOut',
11764     duration: .5
11765     remove: false,
11766     useDisplay: false
11767 });
11768 </code></pre>
11769          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11770          * @param {Object} options (optional) Object literal with any of the Fx config options
11771          * @return {Roo.Element} The Element
11772          */
11773     ghost : function(anchor, o){
11774         var el = this.getFxEl();
11775         o = o || {};
11776
11777         el.queueFx(o, function(){
11778             anchor = anchor || "b";
11779
11780             // restore values after effect
11781             var r = this.getFxRestore();
11782             var w = this.getWidth(),
11783                 h = this.getHeight();
11784
11785             var st = this.dom.style;
11786
11787             var after = function(){
11788                 if(o.useDisplay){
11789                     el.setDisplayed(false);
11790                 }else{
11791                     el.hide();
11792                 }
11793
11794                 el.clearOpacity();
11795                 el.setPositioning(r.pos);
11796                 st.width = r.width;
11797                 st.height = r.height;
11798
11799                 el.afterFx(o);
11800             };
11801
11802             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11803             switch(anchor.toLowerCase()){
11804                 case "t":
11805                     pt.by = [0, -h];
11806                 break;
11807                 case "l":
11808                     pt.by = [-w, 0];
11809                 break;
11810                 case "r":
11811                     pt.by = [w, 0];
11812                 break;
11813                 case "b":
11814                     pt.by = [0, h];
11815                 break;
11816                 case "tl":
11817                     pt.by = [-w, -h];
11818                 break;
11819                 case "bl":
11820                     pt.by = [-w, h];
11821                 break;
11822                 case "br":
11823                     pt.by = [w, h];
11824                 break;
11825                 case "tr":
11826                     pt.by = [w, -h];
11827                 break;
11828             }
11829
11830             arguments.callee.anim = this.fxanim(a,
11831                 o,
11832                 'motion',
11833                 .5,
11834                 "easeOut", after);
11835         });
11836         return this;
11837     },
11838
11839         /**
11840          * Ensures that all effects queued after syncFx is called on the element are
11841          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11842          * @return {Roo.Element} The Element
11843          */
11844     syncFx : function(){
11845         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11846             block : false,
11847             concurrent : true,
11848             stopFx : false
11849         });
11850         return this;
11851     },
11852
11853         /**
11854          * Ensures that all effects queued after sequenceFx is called on the element are
11855          * run in sequence.  This is the opposite of {@link #syncFx}.
11856          * @return {Roo.Element} The Element
11857          */
11858     sequenceFx : function(){
11859         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11860             block : false,
11861             concurrent : false,
11862             stopFx : false
11863         });
11864         return this;
11865     },
11866
11867         /* @private */
11868     nextFx : function(){
11869         var ef = this.fxQueue[0];
11870         if(ef){
11871             ef.call(this);
11872         }
11873     },
11874
11875         /**
11876          * Returns true if the element has any effects actively running or queued, else returns false.
11877          * @return {Boolean} True if element has active effects, else false
11878          */
11879     hasActiveFx : function(){
11880         return this.fxQueue && this.fxQueue[0];
11881     },
11882
11883         /**
11884          * Stops any running effects and clears the element's internal effects queue if it contains
11885          * any additional effects that haven't started yet.
11886          * @return {Roo.Element} The Element
11887          */
11888     stopFx : function(){
11889         if(this.hasActiveFx()){
11890             var cur = this.fxQueue[0];
11891             if(cur && cur.anim && cur.anim.isAnimated()){
11892                 this.fxQueue = [cur]; // clear out others
11893                 cur.anim.stop(true);
11894             }
11895         }
11896         return this;
11897     },
11898
11899         /* @private */
11900     beforeFx : function(o){
11901         if(this.hasActiveFx() && !o.concurrent){
11902            if(o.stopFx){
11903                this.stopFx();
11904                return true;
11905            }
11906            return false;
11907         }
11908         return true;
11909     },
11910
11911         /**
11912          * Returns true if the element is currently blocking so that no other effect can be queued
11913          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11914          * used to ensure that an effect initiated by a user action runs to completion prior to the
11915          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11916          * @return {Boolean} True if blocking, else false
11917          */
11918     hasFxBlock : function(){
11919         var q = this.fxQueue;
11920         return q && q[0] && q[0].block;
11921     },
11922
11923         /* @private */
11924     queueFx : function(o, fn){
11925         if(!this.fxQueue){
11926             this.fxQueue = [];
11927         }
11928         if(!this.hasFxBlock()){
11929             Roo.applyIf(o, this.fxDefaults);
11930             if(!o.concurrent){
11931                 var run = this.beforeFx(o);
11932                 fn.block = o.block;
11933                 this.fxQueue.push(fn);
11934                 if(run){
11935                     this.nextFx();
11936                 }
11937             }else{
11938                 fn.call(this);
11939             }
11940         }
11941         return this;
11942     },
11943
11944         /* @private */
11945     fxWrap : function(pos, o, vis){
11946         var wrap;
11947         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11948             var wrapXY;
11949             if(o.fixPosition){
11950                 wrapXY = this.getXY();
11951             }
11952             var div = document.createElement("div");
11953             div.style.visibility = vis;
11954             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11955             wrap.setPositioning(pos);
11956             if(wrap.getStyle("position") == "static"){
11957                 wrap.position("relative");
11958             }
11959             this.clearPositioning('auto');
11960             wrap.clip();
11961             wrap.dom.appendChild(this.dom);
11962             if(wrapXY){
11963                 wrap.setXY(wrapXY);
11964             }
11965         }
11966         return wrap;
11967     },
11968
11969         /* @private */
11970     fxUnwrap : function(wrap, pos, o){
11971         this.clearPositioning();
11972         this.setPositioning(pos);
11973         if(!o.wrap){
11974             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11975             wrap.remove();
11976         }
11977     },
11978
11979         /* @private */
11980     getFxRestore : function(){
11981         var st = this.dom.style;
11982         return {pos: this.getPositioning(), width: st.width, height : st.height};
11983     },
11984
11985         /* @private */
11986     afterFx : function(o){
11987         if(o.afterStyle){
11988             this.applyStyles(o.afterStyle);
11989         }
11990         if(o.afterCls){
11991             this.addClass(o.afterCls);
11992         }
11993         if(o.remove === true){
11994             this.remove();
11995         }
11996         Roo.callback(o.callback, o.scope, [this]);
11997         if(!o.concurrent){
11998             this.fxQueue.shift();
11999             this.nextFx();
12000         }
12001     },
12002
12003         /* @private */
12004     getFxEl : function(){ // support for composite element fx
12005         return Roo.get(this.dom);
12006     },
12007
12008         /* @private */
12009     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12010         animType = animType || 'run';
12011         opt = opt || {};
12012         var anim = Roo.lib.Anim[animType](
12013             this.dom, args,
12014             (opt.duration || defaultDur) || .35,
12015             (opt.easing || defaultEase) || 'easeOut',
12016             function(){
12017                 Roo.callback(cb, this);
12018             },
12019             this
12020         );
12021         opt.anim = anim;
12022         return anim;
12023     }
12024 };
12025
12026 // backwords compat
12027 Roo.Fx.resize = Roo.Fx.scale;
12028
12029 //When included, Roo.Fx is automatically applied to Element so that all basic
12030 //effects are available directly via the Element API
12031 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12032  * Based on:
12033  * Ext JS Library 1.1.1
12034  * Copyright(c) 2006-2007, Ext JS, LLC.
12035  *
12036  * Originally Released Under LGPL - original licence link has changed is not relivant.
12037  *
12038  * Fork - LGPL
12039  * <script type="text/javascript">
12040  */
12041
12042
12043 /**
12044  * @class Roo.CompositeElement
12045  * Standard composite class. Creates a Roo.Element for every element in the collection.
12046  * <br><br>
12047  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12048  * actions will be performed on all the elements in this collection.</b>
12049  * <br><br>
12050  * All methods return <i>this</i> and can be chained.
12051  <pre><code>
12052  var els = Roo.select("#some-el div.some-class", true);
12053  // or select directly from an existing element
12054  var el = Roo.get('some-el');
12055  el.select('div.some-class', true);
12056
12057  els.setWidth(100); // all elements become 100 width
12058  els.hide(true); // all elements fade out and hide
12059  // or
12060  els.setWidth(100).hide(true);
12061  </code></pre>
12062  */
12063 Roo.CompositeElement = function(els){
12064     this.elements = [];
12065     this.addElements(els);
12066 };
12067 Roo.CompositeElement.prototype = {
12068     isComposite: true,
12069     addElements : function(els){
12070         if(!els) {
12071             return this;
12072         }
12073         if(typeof els == "string"){
12074             els = Roo.Element.selectorFunction(els);
12075         }
12076         var yels = this.elements;
12077         var index = yels.length-1;
12078         for(var i = 0, len = els.length; i < len; i++) {
12079                 yels[++index] = Roo.get(els[i]);
12080         }
12081         return this;
12082     },
12083
12084     /**
12085     * Clears this composite and adds the elements returned by the passed selector.
12086     * @param {String/Array} els A string CSS selector, an array of elements or an element
12087     * @return {CompositeElement} this
12088     */
12089     fill : function(els){
12090         this.elements = [];
12091         this.add(els);
12092         return this;
12093     },
12094
12095     /**
12096     * Filters this composite to only elements that match the passed selector.
12097     * @param {String} selector A string CSS selector
12098     * @param {Boolean} inverse return inverse filter (not matches)
12099     * @return {CompositeElement} this
12100     */
12101     filter : function(selector, inverse){
12102         var els = [];
12103         inverse = inverse || false;
12104         this.each(function(el){
12105             var match = inverse ? !el.is(selector) : el.is(selector);
12106             if(match){
12107                 els[els.length] = el.dom;
12108             }
12109         });
12110         this.fill(els);
12111         return this;
12112     },
12113
12114     invoke : function(fn, args){
12115         var els = this.elements;
12116         for(var i = 0, len = els.length; i < len; i++) {
12117                 Roo.Element.prototype[fn].apply(els[i], args);
12118         }
12119         return this;
12120     },
12121     /**
12122     * Adds elements to this composite.
12123     * @param {String/Array} els A string CSS selector, an array of elements or an element
12124     * @return {CompositeElement} this
12125     */
12126     add : function(els){
12127         if(typeof els == "string"){
12128             this.addElements(Roo.Element.selectorFunction(els));
12129         }else if(els.length !== undefined){
12130             this.addElements(els);
12131         }else{
12132             this.addElements([els]);
12133         }
12134         return this;
12135     },
12136     /**
12137     * Calls the passed function passing (el, this, index) for each element in this composite.
12138     * @param {Function} fn The function to call
12139     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12140     * @return {CompositeElement} this
12141     */
12142     each : function(fn, scope){
12143         var els = this.elements;
12144         for(var i = 0, len = els.length; i < len; i++){
12145             if(fn.call(scope || els[i], els[i], this, i) === false) {
12146                 break;
12147             }
12148         }
12149         return this;
12150     },
12151
12152     /**
12153      * Returns the Element object at the specified index
12154      * @param {Number} index
12155      * @return {Roo.Element}
12156      */
12157     item : function(index){
12158         return this.elements[index] || null;
12159     },
12160
12161     /**
12162      * Returns the first Element
12163      * @return {Roo.Element}
12164      */
12165     first : function(){
12166         return this.item(0);
12167     },
12168
12169     /**
12170      * Returns the last Element
12171      * @return {Roo.Element}
12172      */
12173     last : function(){
12174         return this.item(this.elements.length-1);
12175     },
12176
12177     /**
12178      * Returns the number of elements in this composite
12179      * @return Number
12180      */
12181     getCount : function(){
12182         return this.elements.length;
12183     },
12184
12185     /**
12186      * Returns true if this composite contains the passed element
12187      * @return Boolean
12188      */
12189     contains : function(el){
12190         return this.indexOf(el) !== -1;
12191     },
12192
12193     /**
12194      * Returns true if this composite contains the passed element
12195      * @return Boolean
12196      */
12197     indexOf : function(el){
12198         return this.elements.indexOf(Roo.get(el));
12199     },
12200
12201
12202     /**
12203     * Removes the specified element(s).
12204     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12205     * or an array of any of those.
12206     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12207     * @return {CompositeElement} this
12208     */
12209     removeElement : function(el, removeDom){
12210         if(el instanceof Array){
12211             for(var i = 0, len = el.length; i < len; i++){
12212                 this.removeElement(el[i]);
12213             }
12214             return this;
12215         }
12216         var index = typeof el == 'number' ? el : this.indexOf(el);
12217         if(index !== -1){
12218             if(removeDom){
12219                 var d = this.elements[index];
12220                 if(d.dom){
12221                     d.remove();
12222                 }else{
12223                     d.parentNode.removeChild(d);
12224                 }
12225             }
12226             this.elements.splice(index, 1);
12227         }
12228         return this;
12229     },
12230
12231     /**
12232     * Replaces the specified element with the passed element.
12233     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12234     * to replace.
12235     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12236     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12237     * @return {CompositeElement} this
12238     */
12239     replaceElement : function(el, replacement, domReplace){
12240         var index = typeof el == 'number' ? el : this.indexOf(el);
12241         if(index !== -1){
12242             if(domReplace){
12243                 this.elements[index].replaceWith(replacement);
12244             }else{
12245                 this.elements.splice(index, 1, Roo.get(replacement))
12246             }
12247         }
12248         return this;
12249     },
12250
12251     /**
12252      * Removes all elements.
12253      */
12254     clear : function(){
12255         this.elements = [];
12256     }
12257 };
12258 (function(){
12259     Roo.CompositeElement.createCall = function(proto, fnName){
12260         if(!proto[fnName]){
12261             proto[fnName] = function(){
12262                 return this.invoke(fnName, arguments);
12263             };
12264         }
12265     };
12266     for(var fnName in Roo.Element.prototype){
12267         if(typeof Roo.Element.prototype[fnName] == "function"){
12268             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12269         }
12270     };
12271 })();
12272 /*
12273  * Based on:
12274  * Ext JS Library 1.1.1
12275  * Copyright(c) 2006-2007, Ext JS, LLC.
12276  *
12277  * Originally Released Under LGPL - original licence link has changed is not relivant.
12278  *
12279  * Fork - LGPL
12280  * <script type="text/javascript">
12281  */
12282
12283 /**
12284  * @class Roo.CompositeElementLite
12285  * @extends Roo.CompositeElement
12286  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12287  <pre><code>
12288  var els = Roo.select("#some-el div.some-class");
12289  // or select directly from an existing element
12290  var el = Roo.get('some-el');
12291  el.select('div.some-class');
12292
12293  els.setWidth(100); // all elements become 100 width
12294  els.hide(true); // all elements fade out and hide
12295  // or
12296  els.setWidth(100).hide(true);
12297  </code></pre><br><br>
12298  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12299  * actions will be performed on all the elements in this collection.</b>
12300  */
12301 Roo.CompositeElementLite = function(els){
12302     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12303     this.el = new Roo.Element.Flyweight();
12304 };
12305 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12306     addElements : function(els){
12307         if(els){
12308             if(els instanceof Array){
12309                 this.elements = this.elements.concat(els);
12310             }else{
12311                 var yels = this.elements;
12312                 var index = yels.length-1;
12313                 for(var i = 0, len = els.length; i < len; i++) {
12314                     yels[++index] = els[i];
12315                 }
12316             }
12317         }
12318         return this;
12319     },
12320     invoke : function(fn, args){
12321         var els = this.elements;
12322         var el = this.el;
12323         for(var i = 0, len = els.length; i < len; i++) {
12324             el.dom = els[i];
12325                 Roo.Element.prototype[fn].apply(el, args);
12326         }
12327         return this;
12328     },
12329     /**
12330      * Returns a flyweight Element of the dom element object at the specified index
12331      * @param {Number} index
12332      * @return {Roo.Element}
12333      */
12334     item : function(index){
12335         if(!this.elements[index]){
12336             return null;
12337         }
12338         this.el.dom = this.elements[index];
12339         return this.el;
12340     },
12341
12342     // fixes scope with flyweight
12343     addListener : function(eventName, handler, scope, opt){
12344         var els = this.elements;
12345         for(var i = 0, len = els.length; i < len; i++) {
12346             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12347         }
12348         return this;
12349     },
12350
12351     /**
12352     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12353     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12354     * a reference to the dom node, use el.dom.</b>
12355     * @param {Function} fn The function to call
12356     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12357     * @return {CompositeElement} this
12358     */
12359     each : function(fn, scope){
12360         var els = this.elements;
12361         var el = this.el;
12362         for(var i = 0, len = els.length; i < len; i++){
12363             el.dom = els[i];
12364                 if(fn.call(scope || el, el, this, i) === false){
12365                 break;
12366             }
12367         }
12368         return this;
12369     },
12370
12371     indexOf : function(el){
12372         return this.elements.indexOf(Roo.getDom(el));
12373     },
12374
12375     replaceElement : function(el, replacement, domReplace){
12376         var index = typeof el == 'number' ? el : this.indexOf(el);
12377         if(index !== -1){
12378             replacement = Roo.getDom(replacement);
12379             if(domReplace){
12380                 var d = this.elements[index];
12381                 d.parentNode.insertBefore(replacement, d);
12382                 d.parentNode.removeChild(d);
12383             }
12384             this.elements.splice(index, 1, replacement);
12385         }
12386         return this;
12387     }
12388 });
12389 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12390
12391 /*
12392  * Based on:
12393  * Ext JS Library 1.1.1
12394  * Copyright(c) 2006-2007, Ext JS, LLC.
12395  *
12396  * Originally Released Under LGPL - original licence link has changed is not relivant.
12397  *
12398  * Fork - LGPL
12399  * <script type="text/javascript">
12400  */
12401
12402  
12403
12404 /**
12405  * @class Roo.data.Connection
12406  * @extends Roo.util.Observable
12407  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12408  * either to a configured URL, or to a URL specified at request time. 
12409  * 
12410  * Requests made by this class are asynchronous, and will return immediately. No data from
12411  * the server will be available to the statement immediately following the {@link #request} call.
12412  * To process returned data, use a callback in the request options object, or an event listener.
12413  * 
12414  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12415  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12416  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12417  * property and, if present, the IFRAME's XML document as the responseXML property.
12418  * 
12419  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12420  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12421  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12422  * standard DOM methods.
12423  * @constructor
12424  * @param {Object} config a configuration object.
12425  */
12426 Roo.data.Connection = function(config){
12427     Roo.apply(this, config);
12428     this.addEvents({
12429         /**
12430          * @event beforerequest
12431          * Fires before a network request is made to retrieve a data object.
12432          * @param {Connection} conn This Connection object.
12433          * @param {Object} options The options config object passed to the {@link #request} method.
12434          */
12435         "beforerequest" : true,
12436         /**
12437          * @event requestcomplete
12438          * Fires if the request was successfully completed.
12439          * @param {Connection} conn This Connection object.
12440          * @param {Object} response The XHR object containing the response data.
12441          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12442          * @param {Object} options The options config object passed to the {@link #request} method.
12443          */
12444         "requestcomplete" : true,
12445         /**
12446          * @event requestexception
12447          * Fires if an error HTTP status was returned from the server.
12448          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12449          * @param {Connection} conn This Connection object.
12450          * @param {Object} response The XHR object containing the response data.
12451          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12452          * @param {Object} options The options config object passed to the {@link #request} method.
12453          */
12454         "requestexception" : true
12455     });
12456     Roo.data.Connection.superclass.constructor.call(this);
12457 };
12458
12459 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12460     /**
12461      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12462      */
12463     /**
12464      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12465      * extra parameters to each request made by this object. (defaults to undefined)
12466      */
12467     /**
12468      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12469      *  to each request made by this object. (defaults to undefined)
12470      */
12471     /**
12472      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12473      */
12474     /**
12475      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12476      */
12477     timeout : 30000,
12478     /**
12479      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12480      * @type Boolean
12481      */
12482     autoAbort:false,
12483
12484     /**
12485      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12486      * @type Boolean
12487      */
12488     disableCaching: true,
12489
12490     /**
12491      * Sends an HTTP request to a remote server.
12492      * @param {Object} options An object which may contain the following properties:<ul>
12493      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12494      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12495      * request, a url encoded string or a function to call to get either.</li>
12496      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12497      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12498      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12499      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12500      * <li>options {Object} The parameter to the request call.</li>
12501      * <li>success {Boolean} True if the request succeeded.</li>
12502      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12503      * </ul></li>
12504      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12505      * The callback is passed the following parameters:<ul>
12506      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12507      * <li>options {Object} The parameter to the request call.</li>
12508      * </ul></li>
12509      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12510      * The callback is passed the following parameters:<ul>
12511      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12512      * <li>options {Object} The parameter to the request call.</li>
12513      * </ul></li>
12514      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12515      * for the callback function. Defaults to the browser window.</li>
12516      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12517      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12518      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12519      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12520      * params for the post data. Any params will be appended to the URL.</li>
12521      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12522      * </ul>
12523      * @return {Number} transactionId
12524      */
12525     request : function(o){
12526         if(this.fireEvent("beforerequest", this, o) !== false){
12527             var p = o.params;
12528
12529             if(typeof p == "function"){
12530                 p = p.call(o.scope||window, o);
12531             }
12532             if(typeof p == "object"){
12533                 p = Roo.urlEncode(o.params);
12534             }
12535             if(this.extraParams){
12536                 var extras = Roo.urlEncode(this.extraParams);
12537                 p = p ? (p + '&' + extras) : extras;
12538             }
12539
12540             var url = o.url || this.url;
12541             if(typeof url == 'function'){
12542                 url = url.call(o.scope||window, o);
12543             }
12544
12545             if(o.form){
12546                 var form = Roo.getDom(o.form);
12547                 url = url || form.action;
12548
12549                 var enctype = form.getAttribute("enctype");
12550                 
12551                 if (o.formData) {
12552                     return this.doFormDataUpload(o, url);
12553                 }
12554                 
12555                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12556                     return this.doFormUpload(o, p, url);
12557                 }
12558                 var f = Roo.lib.Ajax.serializeForm(form);
12559                 p = p ? (p + '&' + f) : f;
12560             }
12561             
12562             if (!o.form && o.formData) {
12563                 o.formData = o.formData === true ? new FormData() : o.formData;
12564                 for (var k in o.params) {
12565                     o.formData.append(k,o.params[k]);
12566                 }
12567                     
12568                 return this.doFormDataUpload(o, url);
12569             }
12570             
12571
12572             var hs = o.headers;
12573             if(this.defaultHeaders){
12574                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12575                 if(!o.headers){
12576                     o.headers = hs;
12577                 }
12578             }
12579
12580             var cb = {
12581                 success: this.handleResponse,
12582                 failure: this.handleFailure,
12583                 scope: this,
12584                 argument: {options: o},
12585                 timeout : o.timeout || this.timeout
12586             };
12587
12588             var method = o.method||this.method||(p ? "POST" : "GET");
12589
12590             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12591                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12592             }
12593
12594             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12595                 if(o.autoAbort){
12596                     this.abort();
12597                 }
12598             }else if(this.autoAbort !== false){
12599                 this.abort();
12600             }
12601
12602             if((method == 'GET' && p) || o.xmlData){
12603                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12604                 p = '';
12605             }
12606             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12607             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12608             Roo.lib.Ajax.useDefaultHeader == true;
12609             return this.transId;
12610         }else{
12611             Roo.callback(o.callback, o.scope, [o, null, null]);
12612             return null;
12613         }
12614     },
12615
12616     /**
12617      * Determine whether this object has a request outstanding.
12618      * @param {Number} transactionId (Optional) defaults to the last transaction
12619      * @return {Boolean} True if there is an outstanding request.
12620      */
12621     isLoading : function(transId){
12622         if(transId){
12623             return Roo.lib.Ajax.isCallInProgress(transId);
12624         }else{
12625             return this.transId ? true : false;
12626         }
12627     },
12628
12629     /**
12630      * Aborts any outstanding request.
12631      * @param {Number} transactionId (Optional) defaults to the last transaction
12632      */
12633     abort : function(transId){
12634         if(transId || this.isLoading()){
12635             Roo.lib.Ajax.abort(transId || this.transId);
12636         }
12637     },
12638
12639     // private
12640     handleResponse : function(response){
12641         this.transId = false;
12642         var options = response.argument.options;
12643         response.argument = options ? options.argument : null;
12644         this.fireEvent("requestcomplete", this, response, options);
12645         Roo.callback(options.success, options.scope, [response, options]);
12646         Roo.callback(options.callback, options.scope, [options, true, response]);
12647     },
12648
12649     // private
12650     handleFailure : function(response, e){
12651         this.transId = false;
12652         var options = response.argument.options;
12653         response.argument = options ? options.argument : null;
12654         this.fireEvent("requestexception", this, response, options, e);
12655         Roo.callback(options.failure, options.scope, [response, options]);
12656         Roo.callback(options.callback, options.scope, [options, false, response]);
12657     },
12658
12659     // private
12660     doFormUpload : function(o, ps, url){
12661         var id = Roo.id();
12662         var frame = document.createElement('iframe');
12663         frame.id = id;
12664         frame.name = id;
12665         frame.className = 'x-hidden';
12666         if(Roo.isIE){
12667             frame.src = Roo.SSL_SECURE_URL;
12668         }
12669         document.body.appendChild(frame);
12670
12671         if(Roo.isIE){
12672            document.frames[id].name = id;
12673         }
12674
12675         var form = Roo.getDom(o.form);
12676         form.target = id;
12677         form.method = 'POST';
12678         form.enctype = form.encoding = 'multipart/form-data';
12679         if(url){
12680             form.action = url;
12681         }
12682
12683         var hiddens, hd;
12684         if(ps){ // add dynamic params
12685             hiddens = [];
12686             ps = Roo.urlDecode(ps, false);
12687             for(var k in ps){
12688                 if(ps.hasOwnProperty(k)){
12689                     hd = document.createElement('input');
12690                     hd.type = 'hidden';
12691                     hd.name = k;
12692                     hd.value = ps[k];
12693                     form.appendChild(hd);
12694                     hiddens.push(hd);
12695                 }
12696             }
12697         }
12698
12699         function cb(){
12700             var r = {  // bogus response object
12701                 responseText : '',
12702                 responseXML : null
12703             };
12704
12705             r.argument = o ? o.argument : null;
12706
12707             try { //
12708                 var doc;
12709                 if(Roo.isIE){
12710                     doc = frame.contentWindow.document;
12711                 }else {
12712                     doc = (frame.contentDocument || window.frames[id].document);
12713                 }
12714                 if(doc && doc.body){
12715                     r.responseText = doc.body.innerHTML;
12716                 }
12717                 if(doc && doc.XMLDocument){
12718                     r.responseXML = doc.XMLDocument;
12719                 }else {
12720                     r.responseXML = doc;
12721                 }
12722             }
12723             catch(e) {
12724                 // ignore
12725             }
12726
12727             Roo.EventManager.removeListener(frame, 'load', cb, this);
12728
12729             this.fireEvent("requestcomplete", this, r, o);
12730             Roo.callback(o.success, o.scope, [r, o]);
12731             Roo.callback(o.callback, o.scope, [o, true, r]);
12732
12733             setTimeout(function(){document.body.removeChild(frame);}, 100);
12734         }
12735
12736         Roo.EventManager.on(frame, 'load', cb, this);
12737         form.submit();
12738
12739         if(hiddens){ // remove dynamic params
12740             for(var i = 0, len = hiddens.length; i < len; i++){
12741                 form.removeChild(hiddens[i]);
12742             }
12743         }
12744     },
12745     // this is a 'formdata version???'
12746     
12747     
12748     doFormDataUpload : function(o,  url)
12749     {
12750         var formData;
12751         if (o.form) {
12752             var form =  Roo.getDom(o.form);
12753             form.enctype = form.encoding = 'multipart/form-data';
12754             formData = o.formData === true ? new FormData(form) : o.formData;
12755         } else {
12756             formData = o.formData === true ? new FormData() : o.formData;
12757         }
12758         
12759       
12760         var cb = {
12761             success: this.handleResponse,
12762             failure: this.handleFailure,
12763             scope: this,
12764             argument: {options: o},
12765             timeout : o.timeout || this.timeout
12766         };
12767  
12768         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12769             if(o.autoAbort){
12770                 this.abort();
12771             }
12772         }else if(this.autoAbort !== false){
12773             this.abort();
12774         }
12775
12776         //Roo.lib.Ajax.defaultPostHeader = null;
12777         Roo.lib.Ajax.useDefaultHeader = false;
12778         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12779         Roo.lib.Ajax.useDefaultHeader = true;
12780  
12781          
12782     }
12783     
12784 });
12785 /*
12786  * Based on:
12787  * Ext JS Library 1.1.1
12788  * Copyright(c) 2006-2007, Ext JS, LLC.
12789  *
12790  * Originally Released Under LGPL - original licence link has changed is not relivant.
12791  *
12792  * Fork - LGPL
12793  * <script type="text/javascript">
12794  */
12795  
12796 /**
12797  * Global Ajax request class.
12798  * 
12799  * @class Roo.Ajax
12800  * @extends Roo.data.Connection
12801  * @static
12802  * 
12803  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12804  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12805  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12806  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12807  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12808  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12809  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12810  */
12811 Roo.Ajax = new Roo.data.Connection({
12812     // fix up the docs
12813     /**
12814      * @scope Roo.Ajax
12815      * @type {Boolear} 
12816      */
12817     autoAbort : false,
12818
12819     /**
12820      * Serialize the passed form into a url encoded string
12821      * @scope Roo.Ajax
12822      * @param {String/HTMLElement} form
12823      * @return {String}
12824      */
12825     serializeForm : function(form){
12826         return Roo.lib.Ajax.serializeForm(form);
12827     }
12828 });/*
12829  * Based on:
12830  * Ext JS Library 1.1.1
12831  * Copyright(c) 2006-2007, Ext JS, LLC.
12832  *
12833  * Originally Released Under LGPL - original licence link has changed is not relivant.
12834  *
12835  * Fork - LGPL
12836  * <script type="text/javascript">
12837  */
12838
12839  
12840 /**
12841  * @class Roo.UpdateManager
12842  * @extends Roo.util.Observable
12843  * Provides AJAX-style update for Element object.<br><br>
12844  * Usage:<br>
12845  * <pre><code>
12846  * // Get it from a Roo.Element object
12847  * var el = Roo.get("foo");
12848  * var mgr = el.getUpdateManager();
12849  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12850  * ...
12851  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12852  * <br>
12853  * // or directly (returns the same UpdateManager instance)
12854  * var mgr = new Roo.UpdateManager("myElementId");
12855  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12856  * mgr.on("update", myFcnNeedsToKnow);
12857  * <br>
12858    // short handed call directly from the element object
12859    Roo.get("foo").load({
12860         url: "bar.php",
12861         scripts:true,
12862         params: "for=bar",
12863         text: "Loading Foo..."
12864    });
12865  * </code></pre>
12866  * @constructor
12867  * Create new UpdateManager directly.
12868  * @param {String/HTMLElement/Roo.Element} el The element to update
12869  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
12870  */
12871 Roo.UpdateManager = function(el, forceNew){
12872     el = Roo.get(el);
12873     if(!forceNew && el.updateManager){
12874         return el.updateManager;
12875     }
12876     /**
12877      * The Element object
12878      * @type Roo.Element
12879      */
12880     this.el = el;
12881     /**
12882      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12883      * @type String
12884      */
12885     this.defaultUrl = null;
12886
12887     this.addEvents({
12888         /**
12889          * @event beforeupdate
12890          * Fired before an update is made, return false from your handler and the update is cancelled.
12891          * @param {Roo.Element} el
12892          * @param {String/Object/Function} url
12893          * @param {String/Object} params
12894          */
12895         "beforeupdate": true,
12896         /**
12897          * @event update
12898          * Fired after successful update is made.
12899          * @param {Roo.Element} el
12900          * @param {Object} oResponseObject The response Object
12901          */
12902         "update": true,
12903         /**
12904          * @event failure
12905          * Fired on update failure.
12906          * @param {Roo.Element} el
12907          * @param {Object} oResponseObject The response Object
12908          */
12909         "failure": true
12910     });
12911     var d = Roo.UpdateManager.defaults;
12912     /**
12913      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12914      * @type String
12915      */
12916     this.sslBlankUrl = d.sslBlankUrl;
12917     /**
12918      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12919      * @type Boolean
12920      */
12921     this.disableCaching = d.disableCaching;
12922     /**
12923      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12924      * @type String
12925      */
12926     this.indicatorText = d.indicatorText;
12927     /**
12928      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12929      * @type String
12930      */
12931     this.showLoadIndicator = d.showLoadIndicator;
12932     /**
12933      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12934      * @type Number
12935      */
12936     this.timeout = d.timeout;
12937
12938     /**
12939      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12940      * @type Boolean
12941      */
12942     this.loadScripts = d.loadScripts;
12943
12944     /**
12945      * Transaction object of current executing transaction
12946      */
12947     this.transaction = null;
12948
12949     /**
12950      * @private
12951      */
12952     this.autoRefreshProcId = null;
12953     /**
12954      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12955      * @type Function
12956      */
12957     this.refreshDelegate = this.refresh.createDelegate(this);
12958     /**
12959      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12960      * @type Function
12961      */
12962     this.updateDelegate = this.update.createDelegate(this);
12963     /**
12964      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12965      * @type Function
12966      */
12967     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12968     /**
12969      * @private
12970      */
12971     this.successDelegate = this.processSuccess.createDelegate(this);
12972     /**
12973      * @private
12974      */
12975     this.failureDelegate = this.processFailure.createDelegate(this);
12976
12977     if(!this.renderer){
12978      /**
12979       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12980       */
12981     this.renderer = new Roo.UpdateManager.BasicRenderer();
12982     }
12983     
12984     Roo.UpdateManager.superclass.constructor.call(this);
12985 };
12986
12987 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12988     /**
12989      * Get the Element this UpdateManager is bound to
12990      * @return {Roo.Element} The element
12991      */
12992     getEl : function(){
12993         return this.el;
12994     },
12995     /**
12996      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12997      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
12998 <pre><code>
12999 um.update({<br/>
13000     url: "your-url.php",<br/>
13001     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13002     callback: yourFunction,<br/>
13003     scope: yourObject, //(optional scope)  <br/>
13004     discardUrl: false, <br/>
13005     nocache: false,<br/>
13006     text: "Loading...",<br/>
13007     timeout: 30,<br/>
13008     scripts: false<br/>
13009 });
13010 </code></pre>
13011      * The only required property is url. The optional properties nocache, text and scripts
13012      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13013      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
13014      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13015      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
13016      */
13017     update : function(url, params, callback, discardUrl){
13018         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13019             var method = this.method,
13020                 cfg;
13021             if(typeof url == "object"){ // must be config object
13022                 cfg = url;
13023                 url = cfg.url;
13024                 params = params || cfg.params;
13025                 callback = callback || cfg.callback;
13026                 discardUrl = discardUrl || cfg.discardUrl;
13027                 if(callback && cfg.scope){
13028                     callback = callback.createDelegate(cfg.scope);
13029                 }
13030                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13031                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13032                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13033                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13034                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13035             }
13036             this.showLoading();
13037             if(!discardUrl){
13038                 this.defaultUrl = url;
13039             }
13040             if(typeof url == "function"){
13041                 url = url.call(this);
13042             }
13043
13044             method = method || (params ? "POST" : "GET");
13045             if(method == "GET"){
13046                 url = this.prepareUrl(url);
13047             }
13048
13049             var o = Roo.apply(cfg ||{}, {
13050                 url : url,
13051                 params: params,
13052                 success: this.successDelegate,
13053                 failure: this.failureDelegate,
13054                 callback: undefined,
13055                 timeout: (this.timeout*1000),
13056                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13057             });
13058             Roo.log("updated manager called with timeout of " + o.timeout);
13059             this.transaction = Roo.Ajax.request(o);
13060         }
13061     },
13062
13063     /**
13064      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
13065      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13066      * @param {String/HTMLElement} form The form Id or form element
13067      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13068      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13069      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13070      */
13071     formUpdate : function(form, url, reset, callback){
13072         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13073             if(typeof url == "function"){
13074                 url = url.call(this);
13075             }
13076             form = Roo.getDom(form);
13077             this.transaction = Roo.Ajax.request({
13078                 form: form,
13079                 url:url,
13080                 success: this.successDelegate,
13081                 failure: this.failureDelegate,
13082                 timeout: (this.timeout*1000),
13083                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13084             });
13085             this.showLoading.defer(1, this);
13086         }
13087     },
13088
13089     /**
13090      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13091      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13092      */
13093     refresh : function(callback){
13094         if(this.defaultUrl == null){
13095             return;
13096         }
13097         this.update(this.defaultUrl, null, callback, true);
13098     },
13099
13100     /**
13101      * Set this element to auto refresh.
13102      * @param {Number} interval How often to update (in seconds).
13103      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
13104      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
13105      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13106      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13107      */
13108     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13109         if(refreshNow){
13110             this.update(url || this.defaultUrl, params, callback, true);
13111         }
13112         if(this.autoRefreshProcId){
13113             clearInterval(this.autoRefreshProcId);
13114         }
13115         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13116     },
13117
13118     /**
13119      * Stop auto refresh on this element.
13120      */
13121      stopAutoRefresh : function(){
13122         if(this.autoRefreshProcId){
13123             clearInterval(this.autoRefreshProcId);
13124             delete this.autoRefreshProcId;
13125         }
13126     },
13127
13128     isAutoRefreshing : function(){
13129        return this.autoRefreshProcId ? true : false;
13130     },
13131     /**
13132      * Called to update the element to "Loading" state. Override to perform custom action.
13133      */
13134     showLoading : function(){
13135         if(this.showLoadIndicator){
13136             this.el.update(this.indicatorText);
13137         }
13138     },
13139
13140     /**
13141      * Adds unique parameter to query string if disableCaching = true
13142      * @private
13143      */
13144     prepareUrl : function(url){
13145         if(this.disableCaching){
13146             var append = "_dc=" + (new Date().getTime());
13147             if(url.indexOf("?") !== -1){
13148                 url += "&" + append;
13149             }else{
13150                 url += "?" + append;
13151             }
13152         }
13153         return url;
13154     },
13155
13156     /**
13157      * @private
13158      */
13159     processSuccess : function(response){
13160         this.transaction = null;
13161         if(response.argument.form && response.argument.reset){
13162             try{ // put in try/catch since some older FF releases had problems with this
13163                 response.argument.form.reset();
13164             }catch(e){}
13165         }
13166         if(this.loadScripts){
13167             this.renderer.render(this.el, response, this,
13168                 this.updateComplete.createDelegate(this, [response]));
13169         }else{
13170             this.renderer.render(this.el, response, this);
13171             this.updateComplete(response);
13172         }
13173     },
13174
13175     updateComplete : function(response){
13176         this.fireEvent("update", this.el, response);
13177         if(typeof response.argument.callback == "function"){
13178             response.argument.callback(this.el, true, response);
13179         }
13180     },
13181
13182     /**
13183      * @private
13184      */
13185     processFailure : function(response){
13186         this.transaction = null;
13187         this.fireEvent("failure", this.el, response);
13188         if(typeof response.argument.callback == "function"){
13189             response.argument.callback(this.el, false, response);
13190         }
13191     },
13192
13193     /**
13194      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13195      * @param {Object} renderer The object implementing the render() method
13196      */
13197     setRenderer : function(renderer){
13198         this.renderer = renderer;
13199     },
13200
13201     getRenderer : function(){
13202        return this.renderer;
13203     },
13204
13205     /**
13206      * Set the defaultUrl used for updates
13207      * @param {String/Function} defaultUrl The url or a function to call to get the url
13208      */
13209     setDefaultUrl : function(defaultUrl){
13210         this.defaultUrl = defaultUrl;
13211     },
13212
13213     /**
13214      * Aborts the executing transaction
13215      */
13216     abort : function(){
13217         if(this.transaction){
13218             Roo.Ajax.abort(this.transaction);
13219         }
13220     },
13221
13222     /**
13223      * Returns true if an update is in progress
13224      * @return {Boolean}
13225      */
13226     isUpdating : function(){
13227         if(this.transaction){
13228             return Roo.Ajax.isLoading(this.transaction);
13229         }
13230         return false;
13231     }
13232 });
13233
13234 /**
13235  * @class Roo.UpdateManager.defaults
13236  * @static (not really - but it helps the doc tool)
13237  * The defaults collection enables customizing the default properties of UpdateManager
13238  */
13239    Roo.UpdateManager.defaults = {
13240        /**
13241          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13242          * @type Number
13243          */
13244          timeout : 30,
13245
13246          /**
13247          * True to process scripts by default (Defaults to false).
13248          * @type Boolean
13249          */
13250         loadScripts : false,
13251
13252         /**
13253         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13254         * @type String
13255         */
13256         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13257         /**
13258          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13259          * @type Boolean
13260          */
13261         disableCaching : false,
13262         /**
13263          * Whether to show indicatorText when loading (Defaults to true).
13264          * @type Boolean
13265          */
13266         showLoadIndicator : true,
13267         /**
13268          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13269          * @type String
13270          */
13271         indicatorText : '<div class="loading-indicator">Loading...</div>'
13272    };
13273
13274 /**
13275  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13276  *Usage:
13277  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13278  * @param {String/HTMLElement/Roo.Element} el The element to update
13279  * @param {String} url The url
13280  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13281  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13282  * @static
13283  * @deprecated
13284  * @member Roo.UpdateManager
13285  */
13286 Roo.UpdateManager.updateElement = function(el, url, params, options){
13287     var um = Roo.get(el, true).getUpdateManager();
13288     Roo.apply(um, options);
13289     um.update(url, params, options ? options.callback : null);
13290 };
13291 // alias for backwards compat
13292 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13293 /**
13294  * @class Roo.UpdateManager.BasicRenderer
13295  * Default Content renderer. Updates the elements innerHTML with the responseText.
13296  */
13297 Roo.UpdateManager.BasicRenderer = function(){};
13298
13299 Roo.UpdateManager.BasicRenderer.prototype = {
13300     /**
13301      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13302      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13303      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13304      * @param {Roo.Element} el The element being rendered
13305      * @param {Object} response The YUI Connect response object
13306      * @param {UpdateManager} updateManager The calling update manager
13307      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13308      */
13309      render : function(el, response, updateManager, callback){
13310         el.update(response.responseText, updateManager.loadScripts, callback);
13311     }
13312 };
13313 /*
13314  * Based on:
13315  * Roo JS
13316  * (c)) Alan Knowles
13317  * Licence : LGPL
13318  */
13319
13320
13321 /**
13322  * @class Roo.DomTemplate
13323  * @extends Roo.Template
13324  * An effort at a dom based template engine..
13325  *
13326  * Similar to XTemplate, except it uses dom parsing to create the template..
13327  *
13328  * Supported features:
13329  *
13330  *  Tags:
13331
13332 <pre><code>
13333       {a_variable} - output encoded.
13334       {a_variable.format:("Y-m-d")} - call a method on the variable
13335       {a_variable:raw} - unencoded output
13336       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13337       {a_variable:this.method_on_template(...)} - call a method on the template object.
13338  
13339 </code></pre>
13340  *  The tpl tag:
13341 <pre><code>
13342         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13343         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13344         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13345         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13346   
13347 </code></pre>
13348  *      
13349  */
13350 Roo.DomTemplate = function()
13351 {
13352      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13353      if (this.html) {
13354         this.compile();
13355      }
13356 };
13357
13358
13359 Roo.extend(Roo.DomTemplate, Roo.Template, {
13360     /**
13361      * id counter for sub templates.
13362      */
13363     id : 0,
13364     /**
13365      * flag to indicate if dom parser is inside a pre,
13366      * it will strip whitespace if not.
13367      */
13368     inPre : false,
13369     
13370     /**
13371      * The various sub templates
13372      */
13373     tpls : false,
13374     
13375     
13376     
13377     /**
13378      *
13379      * basic tag replacing syntax
13380      * WORD:WORD()
13381      *
13382      * // you can fake an object call by doing this
13383      *  x.t:(test,tesT) 
13384      * 
13385      */
13386     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13387     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13388     
13389     iterChild : function (node, method) {
13390         
13391         var oldPre = this.inPre;
13392         if (node.tagName == 'PRE') {
13393             this.inPre = true;
13394         }
13395         for( var i = 0; i < node.childNodes.length; i++) {
13396             method.call(this, node.childNodes[i]);
13397         }
13398         this.inPre = oldPre;
13399     },
13400     
13401     
13402     
13403     /**
13404      * compile the template
13405      *
13406      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13407      *
13408      */
13409     compile: function()
13410     {
13411         var s = this.html;
13412         
13413         // covert the html into DOM...
13414         var doc = false;
13415         var div =false;
13416         try {
13417             doc = document.implementation.createHTMLDocument("");
13418             doc.documentElement.innerHTML =   this.html  ;
13419             div = doc.documentElement;
13420         } catch (e) {
13421             // old IE... - nasty -- it causes all sorts of issues.. with
13422             // images getting pulled from server..
13423             div = document.createElement('div');
13424             div.innerHTML = this.html;
13425         }
13426         //doc.documentElement.innerHTML = htmlBody
13427          
13428         
13429         
13430         this.tpls = [];
13431         var _t = this;
13432         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13433         
13434         var tpls = this.tpls;
13435         
13436         // create a top level template from the snippet..
13437         
13438         //Roo.log(div.innerHTML);
13439         
13440         var tpl = {
13441             uid : 'master',
13442             id : this.id++,
13443             attr : false,
13444             value : false,
13445             body : div.innerHTML,
13446             
13447             forCall : false,
13448             execCall : false,
13449             dom : div,
13450             isTop : true
13451             
13452         };
13453         tpls.unshift(tpl);
13454         
13455         
13456         // compile them...
13457         this.tpls = [];
13458         Roo.each(tpls, function(tp){
13459             this.compileTpl(tp);
13460             this.tpls[tp.id] = tp;
13461         }, this);
13462         
13463         this.master = tpls[0];
13464         return this;
13465         
13466         
13467     },
13468     
13469     compileNode : function(node, istop) {
13470         // test for
13471         //Roo.log(node);
13472         
13473         
13474         // skip anything not a tag..
13475         if (node.nodeType != 1) {
13476             if (node.nodeType == 3 && !this.inPre) {
13477                 // reduce white space..
13478                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13479                 
13480             }
13481             return;
13482         }
13483         
13484         var tpl = {
13485             uid : false,
13486             id : false,
13487             attr : false,
13488             value : false,
13489             body : '',
13490             
13491             forCall : false,
13492             execCall : false,
13493             dom : false,
13494             isTop : istop
13495             
13496             
13497         };
13498         
13499         
13500         switch(true) {
13501             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13502             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13503             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13504             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13505             // no default..
13506         }
13507         
13508         
13509         if (!tpl.attr) {
13510             // just itterate children..
13511             this.iterChild(node,this.compileNode);
13512             return;
13513         }
13514         tpl.uid = this.id++;
13515         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13516         node.removeAttribute('roo-'+ tpl.attr);
13517         if (tpl.attr != 'name') {
13518             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13519             node.parentNode.replaceChild(placeholder,  node);
13520         } else {
13521             
13522             var placeholder =  document.createElement('span');
13523             placeholder.className = 'roo-tpl-' + tpl.value;
13524             node.parentNode.replaceChild(placeholder,  node);
13525         }
13526         
13527         // parent now sees '{domtplXXXX}
13528         this.iterChild(node,this.compileNode);
13529         
13530         // we should now have node body...
13531         var div = document.createElement('div');
13532         div.appendChild(node);
13533         tpl.dom = node;
13534         // this has the unfortunate side effect of converting tagged attributes
13535         // eg. href="{...}" into %7C...%7D
13536         // this has been fixed by searching for those combo's although it's a bit hacky..
13537         
13538         
13539         tpl.body = div.innerHTML;
13540         
13541         
13542          
13543         tpl.id = tpl.uid;
13544         switch(tpl.attr) {
13545             case 'for' :
13546                 switch (tpl.value) {
13547                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13548                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13549                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13550                 }
13551                 break;
13552             
13553             case 'exec':
13554                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13555                 break;
13556             
13557             case 'if':     
13558                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13559                 break;
13560             
13561             case 'name':
13562                 tpl.id  = tpl.value; // replace non characters???
13563                 break;
13564             
13565         }
13566         
13567         
13568         this.tpls.push(tpl);
13569         
13570         
13571         
13572     },
13573     
13574     
13575     
13576     
13577     /**
13578      * Compile a segment of the template into a 'sub-template'
13579      *
13580      * 
13581      * 
13582      *
13583      */
13584     compileTpl : function(tpl)
13585     {
13586         var fm = Roo.util.Format;
13587         var useF = this.disableFormats !== true;
13588         
13589         var sep = Roo.isGecko ? "+\n" : ",\n";
13590         
13591         var undef = function(str) {
13592             Roo.debug && Roo.log("Property not found :"  + str);
13593             return '';
13594         };
13595           
13596         //Roo.log(tpl.body);
13597         
13598         
13599         
13600         var fn = function(m, lbrace, name, format, args)
13601         {
13602             //Roo.log("ARGS");
13603             //Roo.log(arguments);
13604             args = args ? args.replace(/\\'/g,"'") : args;
13605             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13606             if (typeof(format) == 'undefined') {
13607                 format =  'htmlEncode'; 
13608             }
13609             if (format == 'raw' ) {
13610                 format = false;
13611             }
13612             
13613             if(name.substr(0, 6) == 'domtpl'){
13614                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13615             }
13616             
13617             // build an array of options to determine if value is undefined..
13618             
13619             // basically get 'xxxx.yyyy' then do
13620             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13621             //    (function () { Roo.log("Property not found"); return ''; })() :
13622             //    ......
13623             
13624             var udef_ar = [];
13625             var lookfor = '';
13626             Roo.each(name.split('.'), function(st) {
13627                 lookfor += (lookfor.length ? '.': '') + st;
13628                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13629             });
13630             
13631             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13632             
13633             
13634             if(format && useF){
13635                 
13636                 args = args ? ',' + args : "";
13637                  
13638                 if(format.substr(0, 5) != "this."){
13639                     format = "fm." + format + '(';
13640                 }else{
13641                     format = 'this.call("'+ format.substr(5) + '", ';
13642                     args = ", values";
13643                 }
13644                 
13645                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13646             }
13647              
13648             if (args && args.length) {
13649                 // called with xxyx.yuu:(test,test)
13650                 // change to ()
13651                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13652             }
13653             // raw.. - :raw modifier..
13654             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13655             
13656         };
13657         var body;
13658         // branched to use + in gecko and [].join() in others
13659         if(Roo.isGecko){
13660             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13661                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13662                     "';};};";
13663         }else{
13664             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13665             body.push(tpl.body.replace(/(\r\n|\n)/g,
13666                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13667             body.push("'].join('');};};");
13668             body = body.join('');
13669         }
13670         
13671         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13672        
13673         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13674         eval(body);
13675         
13676         return this;
13677     },
13678      
13679     /**
13680      * same as applyTemplate, except it's done to one of the subTemplates
13681      * when using named templates, you can do:
13682      *
13683      * var str = pl.applySubTemplate('your-name', values);
13684      *
13685      * 
13686      * @param {Number} id of the template
13687      * @param {Object} values to apply to template
13688      * @param {Object} parent (normaly the instance of this object)
13689      */
13690     applySubTemplate : function(id, values, parent)
13691     {
13692         
13693         
13694         var t = this.tpls[id];
13695         
13696         
13697         try { 
13698             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13699                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13700                 return '';
13701             }
13702         } catch(e) {
13703             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13704             Roo.log(values);
13705           
13706             return '';
13707         }
13708         try { 
13709             
13710             if(t.execCall && t.execCall.call(this, values, parent)){
13711                 return '';
13712             }
13713         } catch(e) {
13714             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13715             Roo.log(values);
13716             return '';
13717         }
13718         
13719         try {
13720             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13721             parent = t.target ? values : parent;
13722             if(t.forCall && vs instanceof Array){
13723                 var buf = [];
13724                 for(var i = 0, len = vs.length; i < len; i++){
13725                     try {
13726                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13727                     } catch (e) {
13728                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13729                         Roo.log(e.body);
13730                         //Roo.log(t.compiled);
13731                         Roo.log(vs[i]);
13732                     }   
13733                 }
13734                 return buf.join('');
13735             }
13736         } catch (e) {
13737             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13738             Roo.log(values);
13739             return '';
13740         }
13741         try {
13742             return t.compiled.call(this, vs, parent);
13743         } catch (e) {
13744             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13745             Roo.log(e.body);
13746             //Roo.log(t.compiled);
13747             Roo.log(values);
13748             return '';
13749         }
13750     },
13751
13752    
13753
13754     applyTemplate : function(values){
13755         return this.master.compiled.call(this, values, {});
13756         //var s = this.subs;
13757     },
13758
13759     apply : function(){
13760         return this.applyTemplate.apply(this, arguments);
13761     }
13762
13763  });
13764
13765 Roo.DomTemplate.from = function(el){
13766     el = Roo.getDom(el);
13767     return new Roo.Domtemplate(el.value || el.innerHTML);
13768 };/*
13769  * Based on:
13770  * Ext JS Library 1.1.1
13771  * Copyright(c) 2006-2007, Ext JS, LLC.
13772  *
13773  * Originally Released Under LGPL - original licence link has changed is not relivant.
13774  *
13775  * Fork - LGPL
13776  * <script type="text/javascript">
13777  */
13778
13779 /**
13780  * @class Roo.util.DelayedTask
13781  * Provides a convenient method of performing setTimeout where a new
13782  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13783  * You can use this class to buffer
13784  * the keypress events for a certain number of milliseconds, and perform only if they stop
13785  * for that amount of time.
13786  * @constructor The parameters to this constructor serve as defaults and are not required.
13787  * @param {Function} fn (optional) The default function to timeout
13788  * @param {Object} scope (optional) The default scope of that timeout
13789  * @param {Array} args (optional) The default Array of arguments
13790  */
13791 Roo.util.DelayedTask = function(fn, scope, args){
13792     var id = null, d, t;
13793
13794     var call = function(){
13795         var now = new Date().getTime();
13796         if(now - t >= d){
13797             clearInterval(id);
13798             id = null;
13799             fn.apply(scope, args || []);
13800         }
13801     };
13802     /**
13803      * Cancels any pending timeout and queues a new one
13804      * @param {Number} delay The milliseconds to delay
13805      * @param {Function} newFn (optional) Overrides function passed to constructor
13806      * @param {Object} newScope (optional) Overrides scope passed to constructor
13807      * @param {Array} newArgs (optional) Overrides args passed to constructor
13808      */
13809     this.delay = function(delay, newFn, newScope, newArgs){
13810         if(id && delay != d){
13811             this.cancel();
13812         }
13813         d = delay;
13814         t = new Date().getTime();
13815         fn = newFn || fn;
13816         scope = newScope || scope;
13817         args = newArgs || args;
13818         if(!id){
13819             id = setInterval(call, d);
13820         }
13821     };
13822
13823     /**
13824      * Cancel the last queued timeout
13825      */
13826     this.cancel = function(){
13827         if(id){
13828             clearInterval(id);
13829             id = null;
13830         }
13831     };
13832 };/*
13833  * Based on:
13834  * Ext JS Library 1.1.1
13835  * Copyright(c) 2006-2007, Ext JS, LLC.
13836  *
13837  * Originally Released Under LGPL - original licence link has changed is not relivant.
13838  *
13839  * Fork - LGPL
13840  * <script type="text/javascript">
13841  */
13842 /**
13843  * @class Roo.util.TaskRunner
13844  * Manage background tasks - not sure why this is better that setInterval?
13845  * @static
13846  *
13847  */
13848  
13849 Roo.util.TaskRunner = function(interval){
13850     interval = interval || 10;
13851     var tasks = [], removeQueue = [];
13852     var id = 0;
13853     var running = false;
13854
13855     var stopThread = function(){
13856         running = false;
13857         clearInterval(id);
13858         id = 0;
13859     };
13860
13861     var startThread = function(){
13862         if(!running){
13863             running = true;
13864             id = setInterval(runTasks, interval);
13865         }
13866     };
13867
13868     var removeTask = function(task){
13869         removeQueue.push(task);
13870         if(task.onStop){
13871             task.onStop();
13872         }
13873     };
13874
13875     var runTasks = function(){
13876         if(removeQueue.length > 0){
13877             for(var i = 0, len = removeQueue.length; i < len; i++){
13878                 tasks.remove(removeQueue[i]);
13879             }
13880             removeQueue = [];
13881             if(tasks.length < 1){
13882                 stopThread();
13883                 return;
13884             }
13885         }
13886         var now = new Date().getTime();
13887         for(var i = 0, len = tasks.length; i < len; ++i){
13888             var t = tasks[i];
13889             var itime = now - t.taskRunTime;
13890             if(t.interval <= itime){
13891                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13892                 t.taskRunTime = now;
13893                 if(rt === false || t.taskRunCount === t.repeat){
13894                     removeTask(t);
13895                     return;
13896                 }
13897             }
13898             if(t.duration && t.duration <= (now - t.taskStartTime)){
13899                 removeTask(t);
13900             }
13901         }
13902     };
13903
13904     /**
13905      * Queues a new task.
13906      * @param {Object} task
13907      *
13908      * Task property : interval = how frequent to run.
13909      * Task object should implement
13910      * function run()
13911      * Task object may implement
13912      * function onStop()
13913      */
13914     this.start = function(task){
13915         tasks.push(task);
13916         task.taskStartTime = new Date().getTime();
13917         task.taskRunTime = 0;
13918         task.taskRunCount = 0;
13919         startThread();
13920         return task;
13921     };
13922     /**
13923      * Stop  new task.
13924      * @param {Object} task
13925      */
13926     this.stop = function(task){
13927         removeTask(task);
13928         return task;
13929     };
13930     /**
13931      * Stop all Tasks
13932      */
13933     this.stopAll = function(){
13934         stopThread();
13935         for(var i = 0, len = tasks.length; i < len; i++){
13936             if(tasks[i].onStop){
13937                 tasks[i].onStop();
13938             }
13939         }
13940         tasks = [];
13941         removeQueue = [];
13942     };
13943 };
13944
13945 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13946  * Based on:
13947  * Ext JS Library 1.1.1
13948  * Copyright(c) 2006-2007, Ext JS, LLC.
13949  *
13950  * Originally Released Under LGPL - original licence link has changed is not relivant.
13951  *
13952  * Fork - LGPL
13953  * <script type="text/javascript">
13954  */
13955
13956  
13957 /**
13958  * @class Roo.util.MixedCollection
13959  * @extends Roo.util.Observable
13960  * A Collection class that maintains both numeric indexes and keys and exposes events.
13961  * @constructor
13962  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13963  * collection (defaults to false)
13964  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13965  * and return the key value for that item.  This is used when available to look up the key on items that
13966  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13967  * equivalent to providing an implementation for the {@link #getKey} method.
13968  */
13969 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13970     this.items = [];
13971     this.map = {};
13972     this.keys = [];
13973     this.length = 0;
13974     this.addEvents({
13975         /**
13976          * @event clear
13977          * Fires when the collection is cleared.
13978          */
13979         "clear" : true,
13980         /**
13981          * @event add
13982          * Fires when an item is added to the collection.
13983          * @param {Number} index The index at which the item was added.
13984          * @param {Object} o The item added.
13985          * @param {String} key The key associated with the added item.
13986          */
13987         "add" : true,
13988         /**
13989          * @event replace
13990          * Fires when an item is replaced in the collection.
13991          * @param {String} key he key associated with the new added.
13992          * @param {Object} old The item being replaced.
13993          * @param {Object} new The new item.
13994          */
13995         "replace" : true,
13996         /**
13997          * @event remove
13998          * Fires when an item is removed from the collection.
13999          * @param {Object} o The item being removed.
14000          * @param {String} key (optional) The key associated with the removed item.
14001          */
14002         "remove" : true,
14003         "sort" : true
14004     });
14005     this.allowFunctions = allowFunctions === true;
14006     if(keyFn){
14007         this.getKey = keyFn;
14008     }
14009     Roo.util.MixedCollection.superclass.constructor.call(this);
14010 };
14011
14012 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14013     allowFunctions : false,
14014     
14015 /**
14016  * Adds an item to the collection.
14017  * @param {String} key The key to associate with the item
14018  * @param {Object} o The item to add.
14019  * @return {Object} The item added.
14020  */
14021     add : function(key, o){
14022         if(arguments.length == 1){
14023             o = arguments[0];
14024             key = this.getKey(o);
14025         }
14026         if(typeof key == "undefined" || key === null){
14027             this.length++;
14028             this.items.push(o);
14029             this.keys.push(null);
14030         }else{
14031             var old = this.map[key];
14032             if(old){
14033                 return this.replace(key, o);
14034             }
14035             this.length++;
14036             this.items.push(o);
14037             this.map[key] = o;
14038             this.keys.push(key);
14039         }
14040         this.fireEvent("add", this.length-1, o, key);
14041         return o;
14042     },
14043        
14044 /**
14045   * MixedCollection has a generic way to fetch keys if you implement getKey.
14046 <pre><code>
14047 // normal way
14048 var mc = new Roo.util.MixedCollection();
14049 mc.add(someEl.dom.id, someEl);
14050 mc.add(otherEl.dom.id, otherEl);
14051 //and so on
14052
14053 // using getKey
14054 var mc = new Roo.util.MixedCollection();
14055 mc.getKey = function(el){
14056    return el.dom.id;
14057 };
14058 mc.add(someEl);
14059 mc.add(otherEl);
14060
14061 // or via the constructor
14062 var mc = new Roo.util.MixedCollection(false, function(el){
14063    return el.dom.id;
14064 });
14065 mc.add(someEl);
14066 mc.add(otherEl);
14067 </code></pre>
14068  * @param o {Object} The item for which to find the key.
14069  * @return {Object} The key for the passed item.
14070  */
14071     getKey : function(o){
14072          return o.id; 
14073     },
14074    
14075 /**
14076  * Replaces an item in the collection.
14077  * @param {String} key The key associated with the item to replace, or the item to replace.
14078  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14079  * @return {Object}  The new item.
14080  */
14081     replace : function(key, o){
14082         if(arguments.length == 1){
14083             o = arguments[0];
14084             key = this.getKey(o);
14085         }
14086         var old = this.item(key);
14087         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14088              return this.add(key, o);
14089         }
14090         var index = this.indexOfKey(key);
14091         this.items[index] = o;
14092         this.map[key] = o;
14093         this.fireEvent("replace", key, old, o);
14094         return o;
14095     },
14096    
14097 /**
14098  * Adds all elements of an Array or an Object to the collection.
14099  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14100  * an Array of values, each of which are added to the collection.
14101  */
14102     addAll : function(objs){
14103         if(arguments.length > 1 || objs instanceof Array){
14104             var args = arguments.length > 1 ? arguments : objs;
14105             for(var i = 0, len = args.length; i < len; i++){
14106                 this.add(args[i]);
14107             }
14108         }else{
14109             for(var key in objs){
14110                 if(this.allowFunctions || typeof objs[key] != "function"){
14111                     this.add(key, objs[key]);
14112                 }
14113             }
14114         }
14115     },
14116    
14117 /**
14118  * Executes the specified function once for every item in the collection, passing each
14119  * item as the first and only parameter. returning false from the function will stop the iteration.
14120  * @param {Function} fn The function to execute for each item.
14121  * @param {Object} scope (optional) The scope in which to execute the function.
14122  */
14123     each : function(fn, scope){
14124         var items = [].concat(this.items); // each safe for removal
14125         for(var i = 0, len = items.length; i < len; i++){
14126             if(fn.call(scope || items[i], items[i], i, len) === false){
14127                 break;
14128             }
14129         }
14130     },
14131    
14132 /**
14133  * Executes the specified function once for every key in the collection, passing each
14134  * key, and its associated item as the first two parameters.
14135  * @param {Function} fn The function to execute for each item.
14136  * @param {Object} scope (optional) The scope in which to execute the function.
14137  */
14138     eachKey : function(fn, scope){
14139         for(var i = 0, len = this.keys.length; i < len; i++){
14140             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14141         }
14142     },
14143    
14144 /**
14145  * Returns the first item in the collection which elicits a true return value from the
14146  * passed selection function.
14147  * @param {Function} fn The selection function to execute for each item.
14148  * @param {Object} scope (optional) The scope in which to execute the function.
14149  * @return {Object} The first item in the collection which returned true from the selection function.
14150  */
14151     find : function(fn, scope){
14152         for(var i = 0, len = this.items.length; i < len; i++){
14153             if(fn.call(scope || window, this.items[i], this.keys[i])){
14154                 return this.items[i];
14155             }
14156         }
14157         return null;
14158     },
14159    
14160 /**
14161  * Inserts an item at the specified index in the collection.
14162  * @param {Number} index The index to insert the item at.
14163  * @param {String} key The key to associate with the new item, or the item itself.
14164  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14165  * @return {Object} The item inserted.
14166  */
14167     insert : function(index, key, o){
14168         if(arguments.length == 2){
14169             o = arguments[1];
14170             key = this.getKey(o);
14171         }
14172         if(index >= this.length){
14173             return this.add(key, o);
14174         }
14175         this.length++;
14176         this.items.splice(index, 0, o);
14177         if(typeof key != "undefined" && key != null){
14178             this.map[key] = o;
14179         }
14180         this.keys.splice(index, 0, key);
14181         this.fireEvent("add", index, o, key);
14182         return o;
14183     },
14184    
14185 /**
14186  * Removed an item from the collection.
14187  * @param {Object} o The item to remove.
14188  * @return {Object} The item removed.
14189  */
14190     remove : function(o){
14191         return this.removeAt(this.indexOf(o));
14192     },
14193    
14194 /**
14195  * Remove an item from a specified index in the collection.
14196  * @param {Number} index The index within the collection of the item to remove.
14197  */
14198     removeAt : function(index){
14199         if(index < this.length && index >= 0){
14200             this.length--;
14201             var o = this.items[index];
14202             this.items.splice(index, 1);
14203             var key = this.keys[index];
14204             if(typeof key != "undefined"){
14205                 delete this.map[key];
14206             }
14207             this.keys.splice(index, 1);
14208             this.fireEvent("remove", o, key);
14209         }
14210     },
14211    
14212 /**
14213  * Removed an item associated with the passed key fom the collection.
14214  * @param {String} key The key of the item to remove.
14215  */
14216     removeKey : function(key){
14217         return this.removeAt(this.indexOfKey(key));
14218     },
14219    
14220 /**
14221  * Returns the number of items in the collection.
14222  * @return {Number} the number of items in the collection.
14223  */
14224     getCount : function(){
14225         return this.length; 
14226     },
14227    
14228 /**
14229  * Returns index within the collection of the passed Object.
14230  * @param {Object} o The item to find the index of.
14231  * @return {Number} index of the item.
14232  */
14233     indexOf : function(o){
14234         if(!this.items.indexOf){
14235             for(var i = 0, len = this.items.length; i < len; i++){
14236                 if(this.items[i] == o) {
14237                     return i;
14238                 }
14239             }
14240             return -1;
14241         }else{
14242             return this.items.indexOf(o);
14243         }
14244     },
14245    
14246 /**
14247  * Returns index within the collection of the passed key.
14248  * @param {String} key The key to find the index of.
14249  * @return {Number} index of the key.
14250  */
14251     indexOfKey : function(key){
14252         if(!this.keys.indexOf){
14253             for(var i = 0, len = this.keys.length; i < len; i++){
14254                 if(this.keys[i] == key) {
14255                     return i;
14256                 }
14257             }
14258             return -1;
14259         }else{
14260             return this.keys.indexOf(key);
14261         }
14262     },
14263    
14264 /**
14265  * Returns the item associated with the passed key OR index. Key has priority over index.
14266  * @param {String/Number} key The key or index of the item.
14267  * @return {Object} The item associated with the passed key.
14268  */
14269     item : function(key){
14270         if (key === 'length') {
14271             return null;
14272         }
14273         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14274         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14275     },
14276     
14277 /**
14278  * Returns the item at the specified index.
14279  * @param {Number} index The index of the item.
14280  * @return {Object}
14281  */
14282     itemAt : function(index){
14283         return this.items[index];
14284     },
14285     
14286 /**
14287  * Returns the item associated with the passed key.
14288  * @param {String/Number} key The key of the item.
14289  * @return {Object} The item associated with the passed key.
14290  */
14291     key : function(key){
14292         return this.map[key];
14293     },
14294    
14295 /**
14296  * Returns true if the collection contains the passed Object as an item.
14297  * @param {Object} o  The Object to look for in the collection.
14298  * @return {Boolean} True if the collection contains the Object as an item.
14299  */
14300     contains : function(o){
14301         return this.indexOf(o) != -1;
14302     },
14303    
14304 /**
14305  * Returns true if the collection contains the passed Object as a key.
14306  * @param {String} key The key to look for in the collection.
14307  * @return {Boolean} True if the collection contains the Object as a key.
14308  */
14309     containsKey : function(key){
14310         return typeof this.map[key] != "undefined";
14311     },
14312    
14313 /**
14314  * Removes all items from the collection.
14315  */
14316     clear : function(){
14317         this.length = 0;
14318         this.items = [];
14319         this.keys = [];
14320         this.map = {};
14321         this.fireEvent("clear");
14322     },
14323    
14324 /**
14325  * Returns the first item in the collection.
14326  * @return {Object} the first item in the collection..
14327  */
14328     first : function(){
14329         return this.items[0]; 
14330     },
14331    
14332 /**
14333  * Returns the last item in the collection.
14334  * @return {Object} the last item in the collection..
14335  */
14336     last : function(){
14337         return this.items[this.length-1];   
14338     },
14339     
14340     _sort : function(property, dir, fn){
14341         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14342         fn = fn || function(a, b){
14343             return a-b;
14344         };
14345         var c = [], k = this.keys, items = this.items;
14346         for(var i = 0, len = items.length; i < len; i++){
14347             c[c.length] = {key: k[i], value: items[i], index: i};
14348         }
14349         c.sort(function(a, b){
14350             var v = fn(a[property], b[property]) * dsc;
14351             if(v == 0){
14352                 v = (a.index < b.index ? -1 : 1);
14353             }
14354             return v;
14355         });
14356         for(var i = 0, len = c.length; i < len; i++){
14357             items[i] = c[i].value;
14358             k[i] = c[i].key;
14359         }
14360         this.fireEvent("sort", this);
14361     },
14362     
14363     /**
14364      * Sorts this collection with the passed comparison function
14365      * @param {String} direction (optional) "ASC" or "DESC"
14366      * @param {Function} fn (optional) comparison function
14367      */
14368     sort : function(dir, fn){
14369         this._sort("value", dir, fn);
14370     },
14371     
14372     /**
14373      * Sorts this collection by keys
14374      * @param {String} direction (optional) "ASC" or "DESC"
14375      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14376      */
14377     keySort : function(dir, fn){
14378         this._sort("key", dir, fn || function(a, b){
14379             return String(a).toUpperCase()-String(b).toUpperCase();
14380         });
14381     },
14382     
14383     /**
14384      * Returns a range of items in this collection
14385      * @param {Number} startIndex (optional) defaults to 0
14386      * @param {Number} endIndex (optional) default to the last item
14387      * @return {Array} An array of items
14388      */
14389     getRange : function(start, end){
14390         var items = this.items;
14391         if(items.length < 1){
14392             return [];
14393         }
14394         start = start || 0;
14395         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14396         var r = [];
14397         if(start <= end){
14398             for(var i = start; i <= end; i++) {
14399                     r[r.length] = items[i];
14400             }
14401         }else{
14402             for(var i = start; i >= end; i--) {
14403                     r[r.length] = items[i];
14404             }
14405         }
14406         return r;
14407     },
14408         
14409     /**
14410      * Filter the <i>objects</i> in this collection by a specific property. 
14411      * Returns a new collection that has been filtered.
14412      * @param {String} property A property on your objects
14413      * @param {String/RegExp} value Either string that the property values 
14414      * should start with or a RegExp to test against the property
14415      * @return {MixedCollection} The new filtered collection
14416      */
14417     filter : function(property, value){
14418         if(!value.exec){ // not a regex
14419             value = String(value);
14420             if(value.length == 0){
14421                 return this.clone();
14422             }
14423             value = new RegExp("^" + Roo.escapeRe(value), "i");
14424         }
14425         return this.filterBy(function(o){
14426             return o && value.test(o[property]);
14427         });
14428         },
14429     
14430     /**
14431      * Filter by a function. * Returns a new collection that has been filtered.
14432      * The passed function will be called with each 
14433      * object in the collection. If the function returns true, the value is included 
14434      * otherwise it is filtered.
14435      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14436      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14437      * @return {MixedCollection} The new filtered collection
14438      */
14439     filterBy : function(fn, scope){
14440         var r = new Roo.util.MixedCollection();
14441         r.getKey = this.getKey;
14442         var k = this.keys, it = this.items;
14443         for(var i = 0, len = it.length; i < len; i++){
14444             if(fn.call(scope||this, it[i], k[i])){
14445                                 r.add(k[i], it[i]);
14446                         }
14447         }
14448         return r;
14449     },
14450     
14451     /**
14452      * Creates a duplicate of this collection
14453      * @return {MixedCollection}
14454      */
14455     clone : function(){
14456         var r = new Roo.util.MixedCollection();
14457         var k = this.keys, it = this.items;
14458         for(var i = 0, len = it.length; i < len; i++){
14459             r.add(k[i], it[i]);
14460         }
14461         r.getKey = this.getKey;
14462         return r;
14463     }
14464 });
14465 /**
14466  * Returns the item associated with the passed key or index.
14467  * @method
14468  * @param {String/Number} key The key or index of the item.
14469  * @return {Object} The item associated with the passed key.
14470  */
14471 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14472  * Based on:
14473  * Ext JS Library 1.1.1
14474  * Copyright(c) 2006-2007, Ext JS, LLC.
14475  *
14476  * Originally Released Under LGPL - original licence link has changed is not relivant.
14477  *
14478  * Fork - LGPL
14479  * <script type="text/javascript">
14480  */
14481 /**
14482  * @class Roo.util.JSON
14483  * Modified version of Douglas Crockford"s json.js that doesn"t
14484  * mess with the Object prototype 
14485  * http://www.json.org/js.html
14486  * @static
14487  */
14488 Roo.util.JSON = new (function(){
14489     var useHasOwn = {}.hasOwnProperty ? true : false;
14490     
14491     // crashes Safari in some instances
14492     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14493     
14494     var pad = function(n) {
14495         return n < 10 ? "0" + n : n;
14496     };
14497     
14498     var m = {
14499         "\b": '\\b',
14500         "\t": '\\t',
14501         "\n": '\\n',
14502         "\f": '\\f',
14503         "\r": '\\r',
14504         '"' : '\\"',
14505         "\\": '\\\\'
14506     };
14507
14508     var encodeString = function(s){
14509         if (/["\\\x00-\x1f]/.test(s)) {
14510             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14511                 var c = m[b];
14512                 if(c){
14513                     return c;
14514                 }
14515                 c = b.charCodeAt();
14516                 return "\\u00" +
14517                     Math.floor(c / 16).toString(16) +
14518                     (c % 16).toString(16);
14519             }) + '"';
14520         }
14521         return '"' + s + '"';
14522     };
14523     
14524     var encodeArray = function(o){
14525         var a = ["["], b, i, l = o.length, v;
14526             for (i = 0; i < l; i += 1) {
14527                 v = o[i];
14528                 switch (typeof v) {
14529                     case "undefined":
14530                     case "function":
14531                     case "unknown":
14532                         break;
14533                     default:
14534                         if (b) {
14535                             a.push(',');
14536                         }
14537                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14538                         b = true;
14539                 }
14540             }
14541             a.push("]");
14542             return a.join("");
14543     };
14544     
14545     var encodeDate = function(o){
14546         return '"' + o.getFullYear() + "-" +
14547                 pad(o.getMonth() + 1) + "-" +
14548                 pad(o.getDate()) + "T" +
14549                 pad(o.getHours()) + ":" +
14550                 pad(o.getMinutes()) + ":" +
14551                 pad(o.getSeconds()) + '"';
14552     };
14553     
14554     /**
14555      * Encodes an Object, Array or other value
14556      * @param {Mixed} o The variable to encode
14557      * @return {String} The JSON string
14558      */
14559     this.encode = function(o)
14560     {
14561         // should this be extended to fully wrap stringify..
14562         
14563         if(typeof o == "undefined" || o === null){
14564             return "null";
14565         }else if(o instanceof Array){
14566             return encodeArray(o);
14567         }else if(o instanceof Date){
14568             return encodeDate(o);
14569         }else if(typeof o == "string"){
14570             return encodeString(o);
14571         }else if(typeof o == "number"){
14572             return isFinite(o) ? String(o) : "null";
14573         }else if(typeof o == "boolean"){
14574             return String(o);
14575         }else {
14576             var a = ["{"], b, i, v;
14577             for (i in o) {
14578                 if(!useHasOwn || o.hasOwnProperty(i)) {
14579                     v = o[i];
14580                     switch (typeof v) {
14581                     case "undefined":
14582                     case "function":
14583                     case "unknown":
14584                         break;
14585                     default:
14586                         if(b){
14587                             a.push(',');
14588                         }
14589                         a.push(this.encode(i), ":",
14590                                 v === null ? "null" : this.encode(v));
14591                         b = true;
14592                     }
14593                 }
14594             }
14595             a.push("}");
14596             return a.join("");
14597         }
14598     };
14599     
14600     /**
14601      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14602      * @param {String} json The JSON string
14603      * @return {Object} The resulting object
14604      */
14605     this.decode = function(json){
14606         
14607         return  /** eval:var:json */ eval("(" + json + ')');
14608     };
14609 })();
14610 /** 
14611  * Shorthand for {@link Roo.util.JSON#encode}
14612  * @member Roo encode 
14613  * @method */
14614 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14615 /** 
14616  * Shorthand for {@link Roo.util.JSON#decode}
14617  * @member Roo decode 
14618  * @method */
14619 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14620 /*
14621  * Based on:
14622  * Ext JS Library 1.1.1
14623  * Copyright(c) 2006-2007, Ext JS, LLC.
14624  *
14625  * Originally Released Under LGPL - original licence link has changed is not relivant.
14626  *
14627  * Fork - LGPL
14628  * <script type="text/javascript">
14629  */
14630  
14631 /**
14632  * @class Roo.util.Format
14633  * Reusable data formatting functions
14634  * @static
14635  */
14636 Roo.util.Format = function(){
14637     var trimRe = /^\s+|\s+$/g;
14638     return {
14639         /**
14640          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14641          * @param {String} value The string to truncate
14642          * @param {Number} length The maximum length to allow before truncating
14643          * @return {String} The converted text
14644          */
14645         ellipsis : function(value, len){
14646             if(value && value.length > len){
14647                 return value.substr(0, len-3)+"...";
14648             }
14649             return value;
14650         },
14651
14652         /**
14653          * Checks a reference and converts it to empty string if it is undefined
14654          * @param {Mixed} value Reference to check
14655          * @return {Mixed} Empty string if converted, otherwise the original value
14656          */
14657         undef : function(value){
14658             return typeof value != "undefined" ? value : "";
14659         },
14660
14661         /**
14662          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14663          * @param {String} value The string to encode
14664          * @return {String} The encoded text
14665          */
14666         htmlEncode : function(value){
14667             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14668         },
14669
14670         /**
14671          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14672          * @param {String} value The string to decode
14673          * @return {String} The decoded text
14674          */
14675         htmlDecode : function(value){
14676             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14677         },
14678
14679         /**
14680          * Trims any whitespace from either side of a string
14681          * @param {String} value The text to trim
14682          * @return {String} The trimmed text
14683          */
14684         trim : function(value){
14685             return String(value).replace(trimRe, "");
14686         },
14687
14688         /**
14689          * Returns a substring from within an original string
14690          * @param {String} value The original text
14691          * @param {Number} start The start index of the substring
14692          * @param {Number} length The length of the substring
14693          * @return {String} The substring
14694          */
14695         substr : function(value, start, length){
14696             return String(value).substr(start, length);
14697         },
14698
14699         /**
14700          * Converts a string to all lower case letters
14701          * @param {String} value The text to convert
14702          * @return {String} The converted text
14703          */
14704         lowercase : function(value){
14705             return String(value).toLowerCase();
14706         },
14707
14708         /**
14709          * Converts a string to all upper case letters
14710          * @param {String} value The text to convert
14711          * @return {String} The converted text
14712          */
14713         uppercase : function(value){
14714             return String(value).toUpperCase();
14715         },
14716
14717         /**
14718          * Converts the first character only of a string to upper case
14719          * @param {String} value The text to convert
14720          * @return {String} The converted text
14721          */
14722         capitalize : function(value){
14723             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14724         },
14725
14726         // private
14727         call : function(value, fn){
14728             if(arguments.length > 2){
14729                 var args = Array.prototype.slice.call(arguments, 2);
14730                 args.unshift(value);
14731                  
14732                 return /** eval:var:value */  eval(fn).apply(window, args);
14733             }else{
14734                 /** eval:var:value */
14735                 return /** eval:var:value */ eval(fn).call(window, value);
14736             }
14737         },
14738
14739        
14740         /**
14741          * safer version of Math.toFixed..??/
14742          * @param {Number/String} value The numeric value to format
14743          * @param {Number/String} value Decimal places 
14744          * @return {String} The formatted currency string
14745          */
14746         toFixed : function(v, n)
14747         {
14748             // why not use to fixed - precision is buggered???
14749             if (!n) {
14750                 return Math.round(v-0);
14751             }
14752             var fact = Math.pow(10,n+1);
14753             v = (Math.round((v-0)*fact))/fact;
14754             var z = (''+fact).substring(2);
14755             if (v == Math.floor(v)) {
14756                 return Math.floor(v) + '.' + z;
14757             }
14758             
14759             // now just padd decimals..
14760             var ps = String(v).split('.');
14761             var fd = (ps[1] + z);
14762             var r = fd.substring(0,n); 
14763             var rm = fd.substring(n); 
14764             if (rm < 5) {
14765                 return ps[0] + '.' + r;
14766             }
14767             r*=1; // turn it into a number;
14768             r++;
14769             if (String(r).length != n) {
14770                 ps[0]*=1;
14771                 ps[0]++;
14772                 r = String(r).substring(1); // chop the end off.
14773             }
14774             
14775             return ps[0] + '.' + r;
14776              
14777         },
14778         
14779         /**
14780          * Format a number as US currency
14781          * @param {Number/String} value The numeric value to format
14782          * @return {String} The formatted currency string
14783          */
14784         usMoney : function(v){
14785             return '$' + Roo.util.Format.number(v);
14786         },
14787         
14788         /**
14789          * Format a number
14790          * eventually this should probably emulate php's number_format
14791          * @param {Number/String} value The numeric value to format
14792          * @param {Number} decimals number of decimal places
14793          * @param {String} delimiter for thousands (default comma)
14794          * @return {String} The formatted currency string
14795          */
14796         number : function(v, decimals, thousandsDelimiter)
14797         {
14798             // multiply and round.
14799             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14800             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14801             
14802             var mul = Math.pow(10, decimals);
14803             var zero = String(mul).substring(1);
14804             v = (Math.round((v-0)*mul))/mul;
14805             
14806             // if it's '0' number.. then
14807             
14808             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14809             v = String(v);
14810             var ps = v.split('.');
14811             var whole = ps[0];
14812             
14813             var r = /(\d+)(\d{3})/;
14814             // add comma's
14815             
14816             if(thousandsDelimiter.length != 0) {
14817                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14818             } 
14819             
14820             var sub = ps[1] ?
14821                     // has decimals..
14822                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14823                     // does not have decimals
14824                     (decimals ? ('.' + zero) : '');
14825             
14826             
14827             return whole + sub ;
14828         },
14829         
14830         /**
14831          * Parse a value into a formatted date using the specified format pattern.
14832          * @param {Mixed} value The value to format
14833          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14834          * @return {String} The formatted date string
14835          */
14836         date : function(v, format){
14837             if(!v){
14838                 return "";
14839             }
14840             if(!(v instanceof Date)){
14841                 v = new Date(Date.parse(v));
14842             }
14843             return v.dateFormat(format || Roo.util.Format.defaults.date);
14844         },
14845
14846         /**
14847          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14848          * @param {String} format Any valid date format string
14849          * @return {Function} The date formatting function
14850          */
14851         dateRenderer : function(format){
14852             return function(v){
14853                 return Roo.util.Format.date(v, format);  
14854             };
14855         },
14856
14857         // private
14858         stripTagsRE : /<\/?[^>]+>/gi,
14859         
14860         /**
14861          * Strips all HTML tags
14862          * @param {Mixed} value The text from which to strip tags
14863          * @return {String} The stripped text
14864          */
14865         stripTags : function(v){
14866             return !v ? v : String(v).replace(this.stripTagsRE, "");
14867         },
14868         
14869         /**
14870          * Size in Mb,Gb etc.
14871          * @param {Number} value The number to be formated
14872          * @param {number} decimals how many decimal places
14873          * @return {String} the formated string
14874          */
14875         size : function(value, decimals)
14876         {
14877             var sizes = ['b', 'k', 'M', 'G', 'T'];
14878             if (value == 0) {
14879                 return 0;
14880             }
14881             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14882             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14883         }
14884         
14885         
14886         
14887     };
14888 }();
14889 Roo.util.Format.defaults = {
14890     date : 'd/M/Y'
14891 };/*
14892  * Based on:
14893  * Ext JS Library 1.1.1
14894  * Copyright(c) 2006-2007, Ext JS, LLC.
14895  *
14896  * Originally Released Under LGPL - original licence link has changed is not relivant.
14897  *
14898  * Fork - LGPL
14899  * <script type="text/javascript">
14900  */
14901
14902
14903  
14904
14905 /**
14906  * @class Roo.MasterTemplate
14907  * @extends Roo.Template
14908  * Provides a template that can have child templates. The syntax is:
14909 <pre><code>
14910 var t = new Roo.MasterTemplate(
14911         '&lt;select name="{name}"&gt;',
14912                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14913         '&lt;/select&gt;'
14914 );
14915 t.add('options', {value: 'foo', text: 'bar'});
14916 // or you can add multiple child elements in one shot
14917 t.addAll('options', [
14918     {value: 'foo', text: 'bar'},
14919     {value: 'foo2', text: 'bar2'},
14920     {value: 'foo3', text: 'bar3'}
14921 ]);
14922 // then append, applying the master template values
14923 t.append('my-form', {name: 'my-select'});
14924 </code></pre>
14925 * A name attribute for the child template is not required if you have only one child
14926 * template or you want to refer to them by index.
14927  */
14928 Roo.MasterTemplate = function(){
14929     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14930     this.originalHtml = this.html;
14931     var st = {};
14932     var m, re = this.subTemplateRe;
14933     re.lastIndex = 0;
14934     var subIndex = 0;
14935     while(m = re.exec(this.html)){
14936         var name = m[1], content = m[2];
14937         st[subIndex] = {
14938             name: name,
14939             index: subIndex,
14940             buffer: [],
14941             tpl : new Roo.Template(content)
14942         };
14943         if(name){
14944             st[name] = st[subIndex];
14945         }
14946         st[subIndex].tpl.compile();
14947         st[subIndex].tpl.call = this.call.createDelegate(this);
14948         subIndex++;
14949     }
14950     this.subCount = subIndex;
14951     this.subs = st;
14952 };
14953 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14954     /**
14955     * The regular expression used to match sub templates
14956     * @type RegExp
14957     * @property
14958     */
14959     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14960
14961     /**
14962      * Applies the passed values to a child template.
14963      * @param {String/Number} name (optional) The name or index of the child template
14964      * @param {Array/Object} values The values to be applied to the template
14965      * @return {MasterTemplate} this
14966      */
14967      add : function(name, values){
14968         if(arguments.length == 1){
14969             values = arguments[0];
14970             name = 0;
14971         }
14972         var s = this.subs[name];
14973         s.buffer[s.buffer.length] = s.tpl.apply(values);
14974         return this;
14975     },
14976
14977     /**
14978      * Applies all the passed values to a child template.
14979      * @param {String/Number} name (optional) The name or index of the child template
14980      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14981      * @param {Boolean} reset (optional) True to reset the template first
14982      * @return {MasterTemplate} this
14983      */
14984     fill : function(name, values, reset){
14985         var a = arguments;
14986         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14987             values = a[0];
14988             name = 0;
14989             reset = a[1];
14990         }
14991         if(reset){
14992             this.reset();
14993         }
14994         for(var i = 0, len = values.length; i < len; i++){
14995             this.add(name, values[i]);
14996         }
14997         return this;
14998     },
14999
15000     /**
15001      * Resets the template for reuse
15002      * @return {MasterTemplate} this
15003      */
15004      reset : function(){
15005         var s = this.subs;
15006         for(var i = 0; i < this.subCount; i++){
15007             s[i].buffer = [];
15008         }
15009         return this;
15010     },
15011
15012     applyTemplate : function(values){
15013         var s = this.subs;
15014         var replaceIndex = -1;
15015         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15016             return s[++replaceIndex].buffer.join("");
15017         });
15018         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15019     },
15020
15021     apply : function(){
15022         return this.applyTemplate.apply(this, arguments);
15023     },
15024
15025     compile : function(){return this;}
15026 });
15027
15028 /**
15029  * Alias for fill().
15030  * @method
15031  */
15032 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15033  /**
15034  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15035  * var tpl = Roo.MasterTemplate.from('element-id');
15036  * @param {String/HTMLElement} el
15037  * @param {Object} config
15038  * @static
15039  */
15040 Roo.MasterTemplate.from = function(el, config){
15041     el = Roo.getDom(el);
15042     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15043 };/*
15044  * Based on:
15045  * Ext JS Library 1.1.1
15046  * Copyright(c) 2006-2007, Ext JS, LLC.
15047  *
15048  * Originally Released Under LGPL - original licence link has changed is not relivant.
15049  *
15050  * Fork - LGPL
15051  * <script type="text/javascript">
15052  */
15053
15054  
15055 /**
15056  * @class Roo.util.CSS
15057  * Utility class for manipulating CSS rules
15058  * @static
15059
15060  */
15061 Roo.util.CSS = function(){
15062         var rules = null;
15063         var doc = document;
15064
15065     var camelRe = /(-[a-z])/gi;
15066     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15067
15068    return {
15069    /**
15070     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15071     * tag and appended to the HEAD of the document.
15072     * @param {String|Object} cssText The text containing the css rules
15073     * @param {String} id An id to add to the stylesheet for later removal
15074     * @return {StyleSheet}
15075     */
15076     createStyleSheet : function(cssText, id){
15077         var ss;
15078         var head = doc.getElementsByTagName("head")[0];
15079         var nrules = doc.createElement("style");
15080         nrules.setAttribute("type", "text/css");
15081         if(id){
15082             nrules.setAttribute("id", id);
15083         }
15084         if (typeof(cssText) != 'string') {
15085             // support object maps..
15086             // not sure if this a good idea.. 
15087             // perhaps it should be merged with the general css handling
15088             // and handle js style props.
15089             var cssTextNew = [];
15090             for(var n in cssText) {
15091                 var citems = [];
15092                 for(var k in cssText[n]) {
15093                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15094                 }
15095                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15096                 
15097             }
15098             cssText = cssTextNew.join("\n");
15099             
15100         }
15101        
15102        
15103        if(Roo.isIE){
15104            head.appendChild(nrules);
15105            ss = nrules.styleSheet;
15106            ss.cssText = cssText;
15107        }else{
15108            try{
15109                 nrules.appendChild(doc.createTextNode(cssText));
15110            }catch(e){
15111                nrules.cssText = cssText; 
15112            }
15113            head.appendChild(nrules);
15114            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15115        }
15116        this.cacheStyleSheet(ss);
15117        return ss;
15118    },
15119
15120    /**
15121     * Removes a style or link tag by id
15122     * @param {String} id The id of the tag
15123     */
15124    removeStyleSheet : function(id){
15125        var existing = doc.getElementById(id);
15126        if(existing){
15127            existing.parentNode.removeChild(existing);
15128        }
15129    },
15130
15131    /**
15132     * Dynamically swaps an existing stylesheet reference for a new one
15133     * @param {String} id The id of an existing link tag to remove
15134     * @param {String} url The href of the new stylesheet to include
15135     */
15136    swapStyleSheet : function(id, url){
15137        this.removeStyleSheet(id);
15138        var ss = doc.createElement("link");
15139        ss.setAttribute("rel", "stylesheet");
15140        ss.setAttribute("type", "text/css");
15141        ss.setAttribute("id", id);
15142        ss.setAttribute("href", url);
15143        doc.getElementsByTagName("head")[0].appendChild(ss);
15144    },
15145    
15146    /**
15147     * Refresh the rule cache if you have dynamically added stylesheets
15148     * @return {Object} An object (hash) of rules indexed by selector
15149     */
15150    refreshCache : function(){
15151        return this.getRules(true);
15152    },
15153
15154    // private
15155    cacheStyleSheet : function(stylesheet){
15156        if(!rules){
15157            rules = {};
15158        }
15159        try{// try catch for cross domain access issue
15160            var ssRules = stylesheet.cssRules || stylesheet.rules;
15161            for(var j = ssRules.length-1; j >= 0; --j){
15162                rules[ssRules[j].selectorText] = ssRules[j];
15163            }
15164        }catch(e){}
15165    },
15166    
15167    /**
15168     * Gets all css rules for the document
15169     * @param {Boolean} refreshCache true to refresh the internal cache
15170     * @return {Object} An object (hash) of rules indexed by selector
15171     */
15172    getRules : function(refreshCache){
15173                 if(rules == null || refreshCache){
15174                         rules = {};
15175                         var ds = doc.styleSheets;
15176                         for(var i =0, len = ds.length; i < len; i++){
15177                             try{
15178                         this.cacheStyleSheet(ds[i]);
15179                     }catch(e){} 
15180                 }
15181                 }
15182                 return rules;
15183         },
15184         
15185         /**
15186     * Gets an an individual CSS rule by selector(s)
15187     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15188     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15189     * @return {CSSRule} The CSS rule or null if one is not found
15190     */
15191    getRule : function(selector, refreshCache){
15192                 var rs = this.getRules(refreshCache);
15193                 if(!(selector instanceof Array)){
15194                     return rs[selector];
15195                 }
15196                 for(var i = 0; i < selector.length; i++){
15197                         if(rs[selector[i]]){
15198                                 return rs[selector[i]];
15199                         }
15200                 }
15201                 return null;
15202         },
15203         
15204         
15205         /**
15206     * Updates a rule property
15207     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15208     * @param {String} property The css property
15209     * @param {String} value The new value for the property
15210     * @return {Boolean} true If a rule was found and updated
15211     */
15212    updateRule : function(selector, property, value){
15213                 if(!(selector instanceof Array)){
15214                         var rule = this.getRule(selector);
15215                         if(rule){
15216                                 rule.style[property.replace(camelRe, camelFn)] = value;
15217                                 return true;
15218                         }
15219                 }else{
15220                         for(var i = 0; i < selector.length; i++){
15221                                 if(this.updateRule(selector[i], property, value)){
15222                                         return true;
15223                                 }
15224                         }
15225                 }
15226                 return false;
15227         }
15228    };   
15229 }();/*
15230  * Based on:
15231  * Ext JS Library 1.1.1
15232  * Copyright(c) 2006-2007, Ext JS, LLC.
15233  *
15234  * Originally Released Under LGPL - original licence link has changed is not relivant.
15235  *
15236  * Fork - LGPL
15237  * <script type="text/javascript">
15238  */
15239
15240  
15241
15242 /**
15243  * @class Roo.util.ClickRepeater
15244  * @extends Roo.util.Observable
15245  * 
15246  * A wrapper class which can be applied to any element. Fires a "click" event while the
15247  * mouse is pressed. The interval between firings may be specified in the config but
15248  * defaults to 10 milliseconds.
15249  * 
15250  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15251  * 
15252  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15253  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15254  * Similar to an autorepeat key delay.
15255  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15256  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15257  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15258  *           "interval" and "delay" are ignored. "immediate" is honored.
15259  * @cfg {Boolean} preventDefault True to prevent the default click event
15260  * @cfg {Boolean} stopDefault True to stop the default click event
15261  * 
15262  * @history
15263  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15264  *     2007-02-02 jvs Renamed to ClickRepeater
15265  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15266  *
15267  *  @constructor
15268  * @param {String/HTMLElement/Element} el The element to listen on
15269  * @param {Object} config
15270  **/
15271 Roo.util.ClickRepeater = function(el, config)
15272 {
15273     this.el = Roo.get(el);
15274     this.el.unselectable();
15275
15276     Roo.apply(this, config);
15277
15278     this.addEvents({
15279     /**
15280      * @event mousedown
15281      * Fires when the mouse button is depressed.
15282      * @param {Roo.util.ClickRepeater} this
15283      */
15284         "mousedown" : true,
15285     /**
15286      * @event click
15287      * Fires on a specified interval during the time the element is pressed.
15288      * @param {Roo.util.ClickRepeater} this
15289      */
15290         "click" : true,
15291     /**
15292      * @event mouseup
15293      * Fires when the mouse key is released.
15294      * @param {Roo.util.ClickRepeater} this
15295      */
15296         "mouseup" : true
15297     });
15298
15299     this.el.on("mousedown", this.handleMouseDown, this);
15300     if(this.preventDefault || this.stopDefault){
15301         this.el.on("click", function(e){
15302             if(this.preventDefault){
15303                 e.preventDefault();
15304             }
15305             if(this.stopDefault){
15306                 e.stopEvent();
15307             }
15308         }, this);
15309     }
15310
15311     // allow inline handler
15312     if(this.handler){
15313         this.on("click", this.handler,  this.scope || this);
15314     }
15315
15316     Roo.util.ClickRepeater.superclass.constructor.call(this);
15317 };
15318
15319 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15320     interval : 20,
15321     delay: 250,
15322     preventDefault : true,
15323     stopDefault : false,
15324     timer : 0,
15325
15326     // private
15327     handleMouseDown : function(){
15328         clearTimeout(this.timer);
15329         this.el.blur();
15330         if(this.pressClass){
15331             this.el.addClass(this.pressClass);
15332         }
15333         this.mousedownTime = new Date();
15334
15335         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15336         this.el.on("mouseout", this.handleMouseOut, this);
15337
15338         this.fireEvent("mousedown", this);
15339         this.fireEvent("click", this);
15340         
15341         this.timer = this.click.defer(this.delay || this.interval, this);
15342     },
15343
15344     // private
15345     click : function(){
15346         this.fireEvent("click", this);
15347         this.timer = this.click.defer(this.getInterval(), this);
15348     },
15349
15350     // private
15351     getInterval: function(){
15352         if(!this.accelerate){
15353             return this.interval;
15354         }
15355         var pressTime = this.mousedownTime.getElapsed();
15356         if(pressTime < 500){
15357             return 400;
15358         }else if(pressTime < 1700){
15359             return 320;
15360         }else if(pressTime < 2600){
15361             return 250;
15362         }else if(pressTime < 3500){
15363             return 180;
15364         }else if(pressTime < 4400){
15365             return 140;
15366         }else if(pressTime < 5300){
15367             return 80;
15368         }else if(pressTime < 6200){
15369             return 50;
15370         }else{
15371             return 10;
15372         }
15373     },
15374
15375     // private
15376     handleMouseOut : function(){
15377         clearTimeout(this.timer);
15378         if(this.pressClass){
15379             this.el.removeClass(this.pressClass);
15380         }
15381         this.el.on("mouseover", this.handleMouseReturn, this);
15382     },
15383
15384     // private
15385     handleMouseReturn : function(){
15386         this.el.un("mouseover", this.handleMouseReturn);
15387         if(this.pressClass){
15388             this.el.addClass(this.pressClass);
15389         }
15390         this.click();
15391     },
15392
15393     // private
15394     handleMouseUp : function(){
15395         clearTimeout(this.timer);
15396         this.el.un("mouseover", this.handleMouseReturn);
15397         this.el.un("mouseout", this.handleMouseOut);
15398         Roo.get(document).un("mouseup", this.handleMouseUp);
15399         this.el.removeClass(this.pressClass);
15400         this.fireEvent("mouseup", this);
15401     }
15402 });/**
15403  * @class Roo.util.Clipboard
15404  * @static
15405  * 
15406  * Clipboard UTILS
15407  * 
15408  **/
15409 Roo.util.Clipboard = {
15410     /**
15411      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15412      * @param {String} text to copy to clipboard
15413      */
15414     write : function(text) {
15415         // navigator clipboard api needs a secure context (https)
15416         if (navigator.clipboard && window.isSecureContext) {
15417             // navigator clipboard api method'
15418             navigator.clipboard.writeText(text);
15419             return ;
15420         } 
15421         // text area method
15422         var ta = document.createElement("textarea");
15423         ta.value = text;
15424         // make the textarea out of viewport
15425         ta.style.position = "fixed";
15426         ta.style.left = "-999999px";
15427         ta.style.top = "-999999px";
15428         document.body.appendChild(ta);
15429         ta.focus();
15430         ta.select();
15431         document.execCommand('copy');
15432         (function() {
15433             ta.remove();
15434         }).defer(100);
15435         
15436     }
15437         
15438 }
15439     /*
15440  * Based on:
15441  * Ext JS Library 1.1.1
15442  * Copyright(c) 2006-2007, Ext JS, LLC.
15443  *
15444  * Originally Released Under LGPL - original licence link has changed is not relivant.
15445  *
15446  * Fork - LGPL
15447  * <script type="text/javascript">
15448  */
15449
15450  
15451 /**
15452  * @class Roo.KeyNav
15453  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15454  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15455  * way to implement custom navigation schemes for any UI component.</p>
15456  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15457  * pageUp, pageDown, del, home, end.  Usage:</p>
15458  <pre><code>
15459 var nav = new Roo.KeyNav("my-element", {
15460     "left" : function(e){
15461         this.moveLeft(e.ctrlKey);
15462     },
15463     "right" : function(e){
15464         this.moveRight(e.ctrlKey);
15465     },
15466     "enter" : function(e){
15467         this.save();
15468     },
15469     scope : this
15470 });
15471 </code></pre>
15472  * @constructor
15473  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15474  * @param {Object} config The config
15475  */
15476 Roo.KeyNav = function(el, config){
15477     this.el = Roo.get(el);
15478     Roo.apply(this, config);
15479     if(!this.disabled){
15480         this.disabled = true;
15481         this.enable();
15482     }
15483 };
15484
15485 Roo.KeyNav.prototype = {
15486     /**
15487      * @cfg {Boolean} disabled
15488      * True to disable this KeyNav instance (defaults to false)
15489      */
15490     disabled : false,
15491     /**
15492      * @cfg {String} defaultEventAction
15493      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15494      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15495      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15496      */
15497     defaultEventAction: "stopEvent",
15498     /**
15499      * @cfg {Boolean} forceKeyDown
15500      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15501      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15502      * handle keydown instead of keypress.
15503      */
15504     forceKeyDown : false,
15505
15506     // private
15507     prepareEvent : function(e){
15508         var k = e.getKey();
15509         var h = this.keyToHandler[k];
15510         //if(h && this[h]){
15511         //    e.stopPropagation();
15512         //}
15513         if(Roo.isSafari && h && k >= 37 && k <= 40){
15514             e.stopEvent();
15515         }
15516     },
15517
15518     // private
15519     relay : function(e){
15520         var k = e.getKey();
15521         var h = this.keyToHandler[k];
15522         if(h && this[h]){
15523             if(this.doRelay(e, this[h], h) !== true){
15524                 e[this.defaultEventAction]();
15525             }
15526         }
15527     },
15528
15529     // private
15530     doRelay : function(e, h, hname){
15531         return h.call(this.scope || this, e);
15532     },
15533
15534     // possible handlers
15535     enter : false,
15536     left : false,
15537     right : false,
15538     up : false,
15539     down : false,
15540     tab : false,
15541     esc : false,
15542     pageUp : false,
15543     pageDown : false,
15544     del : false,
15545     home : false,
15546     end : false,
15547
15548     // quick lookup hash
15549     keyToHandler : {
15550         37 : "left",
15551         39 : "right",
15552         38 : "up",
15553         40 : "down",
15554         33 : "pageUp",
15555         34 : "pageDown",
15556         46 : "del",
15557         36 : "home",
15558         35 : "end",
15559         13 : "enter",
15560         27 : "esc",
15561         9  : "tab"
15562     },
15563
15564         /**
15565          * Enable this KeyNav
15566          */
15567         enable: function(){
15568                 if(this.disabled){
15569             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15570             // the EventObject will normalize Safari automatically
15571             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15572                 this.el.on("keydown", this.relay,  this);
15573             }else{
15574                 this.el.on("keydown", this.prepareEvent,  this);
15575                 this.el.on("keypress", this.relay,  this);
15576             }
15577                     this.disabled = false;
15578                 }
15579         },
15580
15581         /**
15582          * Disable this KeyNav
15583          */
15584         disable: function(){
15585                 if(!this.disabled){
15586                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15587                 this.el.un("keydown", this.relay);
15588             }else{
15589                 this.el.un("keydown", this.prepareEvent);
15590                 this.el.un("keypress", this.relay);
15591             }
15592                     this.disabled = true;
15593                 }
15594         }
15595 };/*
15596  * Based on:
15597  * Ext JS Library 1.1.1
15598  * Copyright(c) 2006-2007, Ext JS, LLC.
15599  *
15600  * Originally Released Under LGPL - original licence link has changed is not relivant.
15601  *
15602  * Fork - LGPL
15603  * <script type="text/javascript">
15604  */
15605
15606  
15607 /**
15608  * @class Roo.KeyMap
15609  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15610  * The constructor accepts the same config object as defined by {@link #addBinding}.
15611  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15612  * combination it will call the function with this signature (if the match is a multi-key
15613  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15614  * A KeyMap can also handle a string representation of keys.<br />
15615  * Usage:
15616  <pre><code>
15617 // map one key by key code
15618 var map = new Roo.KeyMap("my-element", {
15619     key: 13, // or Roo.EventObject.ENTER
15620     fn: myHandler,
15621     scope: myObject
15622 });
15623
15624 // map multiple keys to one action by string
15625 var map = new Roo.KeyMap("my-element", {
15626     key: "a\r\n\t",
15627     fn: myHandler,
15628     scope: myObject
15629 });
15630
15631 // map multiple keys to multiple actions by strings and array of codes
15632 var map = new Roo.KeyMap("my-element", [
15633     {
15634         key: [10,13],
15635         fn: function(){ alert("Return was pressed"); }
15636     }, {
15637         key: "abc",
15638         fn: function(){ alert('a, b or c was pressed'); }
15639     }, {
15640         key: "\t",
15641         ctrl:true,
15642         shift:true,
15643         fn: function(){ alert('Control + shift + tab was pressed.'); }
15644     }
15645 ]);
15646 </code></pre>
15647  * <b>Note: A KeyMap starts enabled</b>
15648  * @constructor
15649  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15650  * @param {Object} config The config (see {@link #addBinding})
15651  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15652  */
15653 Roo.KeyMap = function(el, config, eventName){
15654     this.el  = Roo.get(el);
15655     this.eventName = eventName || "keydown";
15656     this.bindings = [];
15657     if(config){
15658         this.addBinding(config);
15659     }
15660     this.enable();
15661 };
15662
15663 Roo.KeyMap.prototype = {
15664     /**
15665      * True to stop the event from bubbling and prevent the default browser action if the
15666      * key was handled by the KeyMap (defaults to false)
15667      * @type Boolean
15668      */
15669     stopEvent : false,
15670
15671     /**
15672      * Add a new binding to this KeyMap. The following config object properties are supported:
15673      * <pre>
15674 Property    Type             Description
15675 ----------  ---------------  ----------------------------------------------------------------------
15676 key         String/Array     A single keycode or an array of keycodes to handle
15677 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15678 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15679 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15680 fn          Function         The function to call when KeyMap finds the expected key combination
15681 scope       Object           The scope of the callback function
15682 </pre>
15683      *
15684      * Usage:
15685      * <pre><code>
15686 // Create a KeyMap
15687 var map = new Roo.KeyMap(document, {
15688     key: Roo.EventObject.ENTER,
15689     fn: handleKey,
15690     scope: this
15691 });
15692
15693 //Add a new binding to the existing KeyMap later
15694 map.addBinding({
15695     key: 'abc',
15696     shift: true,
15697     fn: handleKey,
15698     scope: this
15699 });
15700 </code></pre>
15701      * @param {Object/Array} config A single KeyMap config or an array of configs
15702      */
15703         addBinding : function(config){
15704         if(config instanceof Array){
15705             for(var i = 0, len = config.length; i < len; i++){
15706                 this.addBinding(config[i]);
15707             }
15708             return;
15709         }
15710         var keyCode = config.key,
15711             shift = config.shift, 
15712             ctrl = config.ctrl, 
15713             alt = config.alt,
15714             fn = config.fn,
15715             scope = config.scope;
15716         if(typeof keyCode == "string"){
15717             var ks = [];
15718             var keyString = keyCode.toUpperCase();
15719             for(var j = 0, len = keyString.length; j < len; j++){
15720                 ks.push(keyString.charCodeAt(j));
15721             }
15722             keyCode = ks;
15723         }
15724         var keyArray = keyCode instanceof Array;
15725         var handler = function(e){
15726             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15727                 var k = e.getKey();
15728                 if(keyArray){
15729                     for(var i = 0, len = keyCode.length; i < len; i++){
15730                         if(keyCode[i] == k){
15731                           if(this.stopEvent){
15732                               e.stopEvent();
15733                           }
15734                           fn.call(scope || window, k, e);
15735                           return;
15736                         }
15737                     }
15738                 }else{
15739                     if(k == keyCode){
15740                         if(this.stopEvent){
15741                            e.stopEvent();
15742                         }
15743                         fn.call(scope || window, k, e);
15744                     }
15745                 }
15746             }
15747         };
15748         this.bindings.push(handler);  
15749         },
15750
15751     /**
15752      * Shorthand for adding a single key listener
15753      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15754      * following options:
15755      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15756      * @param {Function} fn The function to call
15757      * @param {Object} scope (optional) The scope of the function
15758      */
15759     on : function(key, fn, scope){
15760         var keyCode, shift, ctrl, alt;
15761         if(typeof key == "object" && !(key instanceof Array)){
15762             keyCode = key.key;
15763             shift = key.shift;
15764             ctrl = key.ctrl;
15765             alt = key.alt;
15766         }else{
15767             keyCode = key;
15768         }
15769         this.addBinding({
15770             key: keyCode,
15771             shift: shift,
15772             ctrl: ctrl,
15773             alt: alt,
15774             fn: fn,
15775             scope: scope
15776         })
15777     },
15778
15779     // private
15780     handleKeyDown : function(e){
15781             if(this.enabled){ //just in case
15782             var b = this.bindings;
15783             for(var i = 0, len = b.length; i < len; i++){
15784                 b[i].call(this, e);
15785             }
15786             }
15787         },
15788         
15789         /**
15790          * Returns true if this KeyMap is enabled
15791          * @return {Boolean} 
15792          */
15793         isEnabled : function(){
15794             return this.enabled;  
15795         },
15796         
15797         /**
15798          * Enables this KeyMap
15799          */
15800         enable: function(){
15801                 if(!this.enabled){
15802                     this.el.on(this.eventName, this.handleKeyDown, this);
15803                     this.enabled = true;
15804                 }
15805         },
15806
15807         /**
15808          * Disable this KeyMap
15809          */
15810         disable: function(){
15811                 if(this.enabled){
15812                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15813                     this.enabled = false;
15814                 }
15815         }
15816 };/*
15817  * Based on:
15818  * Ext JS Library 1.1.1
15819  * Copyright(c) 2006-2007, Ext JS, LLC.
15820  *
15821  * Originally Released Under LGPL - original licence link has changed is not relivant.
15822  *
15823  * Fork - LGPL
15824  * <script type="text/javascript">
15825  */
15826
15827  
15828 /**
15829  * @class Roo.util.TextMetrics
15830  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15831  * wide, in pixels, a given block of text will be.
15832  * @static
15833  */
15834 Roo.util.TextMetrics = function(){
15835     var shared;
15836     return {
15837         /**
15838          * Measures the size of the specified text
15839          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15840          * that can affect the size of the rendered text
15841          * @param {String} text The text to measure
15842          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15843          * in order to accurately measure the text height
15844          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15845          */
15846         measure : function(el, text, fixedWidth){
15847             if(!shared){
15848                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15849             }
15850             shared.bind(el);
15851             shared.setFixedWidth(fixedWidth || 'auto');
15852             return shared.getSize(text);
15853         },
15854
15855         /**
15856          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15857          * the overhead of multiple calls to initialize the style properties on each measurement.
15858          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15859          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15860          * in order to accurately measure the text height
15861          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15862          */
15863         createInstance : function(el, fixedWidth){
15864             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15865         }
15866     };
15867 }();
15868
15869 /**
15870  * @class Roo.util.TextMetrics.Instance
15871  * Instance of  TextMetrics Calcuation
15872  * @constructor
15873  * Create a new TextMetrics Instance
15874  * @param {Object} bindto
15875  * @param {Boolean} fixedWidth
15876  */
15877
15878 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15879 {
15880     var ml = new Roo.Element(document.createElement('div'));
15881     document.body.appendChild(ml.dom);
15882     ml.position('absolute');
15883     ml.setLeftTop(-1000, -1000);
15884     ml.hide();
15885
15886     if(fixedWidth){
15887         ml.setWidth(fixedWidth);
15888     }
15889      
15890     var instance = {
15891         /**
15892          * Returns the size of the specified text based on the internal element's style and width properties
15893          * @param {String} text The text to measure
15894          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15895          */
15896         getSize : function(text){
15897             ml.update(text);
15898             var s = ml.getSize();
15899             ml.update('');
15900             return s;
15901         },
15902
15903         /**
15904          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15905          * that can affect the size of the rendered text
15906          * @param {String/HTMLElement} el The element, dom node or id
15907          */
15908         bind : function(el){
15909             ml.setStyle(
15910                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15911             );
15912         },
15913
15914         /**
15915          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15916          * to set a fixed width in order to accurately measure the text height.
15917          * @param {Number} width The width to set on the element
15918          */
15919         setFixedWidth : function(width){
15920             ml.setWidth(width);
15921         },
15922
15923         /**
15924          * Returns the measured width of the specified text
15925          * @param {String} text The text to measure
15926          * @return {Number} width The width in pixels
15927          */
15928         getWidth : function(text){
15929             ml.dom.style.width = 'auto';
15930             return this.getSize(text).width;
15931         },
15932
15933         /**
15934          * Returns the measured height of the specified text.  For multiline text, be sure to call
15935          * {@link #setFixedWidth} if necessary.
15936          * @param {String} text The text to measure
15937          * @return {Number} height The height in pixels
15938          */
15939         getHeight : function(text){
15940             return this.getSize(text).height;
15941         }
15942     };
15943
15944     instance.bind(bindTo);
15945
15946     return instance;
15947 };
15948
15949 // backwards compat
15950 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15951  * Based on:
15952  * Ext JS Library 1.1.1
15953  * Copyright(c) 2006-2007, Ext JS, LLC.
15954  *
15955  * Originally Released Under LGPL - original licence link has changed is not relivant.
15956  *
15957  * Fork - LGPL
15958  * <script type="text/javascript">
15959  */
15960
15961 /**
15962  * @class Roo.state.Provider
15963  * Abstract base class for state provider implementations. This class provides methods
15964  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15965  * Provider interface.
15966  */
15967 Roo.state.Provider = function(){
15968     /**
15969      * @event statechange
15970      * Fires when a state change occurs.
15971      * @param {Provider} this This state provider
15972      * @param {String} key The state key which was changed
15973      * @param {String} value The encoded value for the state
15974      */
15975     this.addEvents({
15976         "statechange": true
15977     });
15978     this.state = {};
15979     Roo.state.Provider.superclass.constructor.call(this);
15980 };
15981 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15982     /**
15983      * Returns the current value for a key
15984      * @param {String} name The key name
15985      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15986      * @return {Mixed} The state data
15987      */
15988     get : function(name, defaultValue){
15989         return typeof this.state[name] == "undefined" ?
15990             defaultValue : this.state[name];
15991     },
15992     
15993     /**
15994      * Clears a value from the state
15995      * @param {String} name The key name
15996      */
15997     clear : function(name){
15998         delete this.state[name];
15999         this.fireEvent("statechange", this, name, null);
16000     },
16001     
16002     /**
16003      * Sets the value for a key
16004      * @param {String} name The key name
16005      * @param {Mixed} value The value to set
16006      */
16007     set : function(name, value){
16008         this.state[name] = value;
16009         this.fireEvent("statechange", this, name, value);
16010     },
16011     
16012     /**
16013      * Decodes a string previously encoded with {@link #encodeValue}.
16014      * @param {String} value The value to decode
16015      * @return {Mixed} The decoded value
16016      */
16017     decodeValue : function(cookie){
16018         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16019         var matches = re.exec(unescape(cookie));
16020         if(!matches || !matches[1]) {
16021             return; // non state cookie
16022         }
16023         var type = matches[1];
16024         var v = matches[2];
16025         switch(type){
16026             case "n":
16027                 return parseFloat(v);
16028             case "d":
16029                 return new Date(Date.parse(v));
16030             case "b":
16031                 return (v == "1");
16032             case "a":
16033                 var all = [];
16034                 var values = v.split("^");
16035                 for(var i = 0, len = values.length; i < len; i++){
16036                     all.push(this.decodeValue(values[i]));
16037                 }
16038                 return all;
16039            case "o":
16040                 var all = {};
16041                 var values = v.split("^");
16042                 for(var i = 0, len = values.length; i < len; i++){
16043                     var kv = values[i].split("=");
16044                     all[kv[0]] = this.decodeValue(kv[1]);
16045                 }
16046                 return all;
16047            default:
16048                 return v;
16049         }
16050     },
16051     
16052     /**
16053      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16054      * @param {Mixed} value The value to encode
16055      * @return {String} The encoded value
16056      */
16057     encodeValue : function(v){
16058         var enc;
16059         if(typeof v == "number"){
16060             enc = "n:" + v;
16061         }else if(typeof v == "boolean"){
16062             enc = "b:" + (v ? "1" : "0");
16063         }else if(v instanceof Date){
16064             enc = "d:" + v.toGMTString();
16065         }else if(v instanceof Array){
16066             var flat = "";
16067             for(var i = 0, len = v.length; i < len; i++){
16068                 flat += this.encodeValue(v[i]);
16069                 if(i != len-1) {
16070                     flat += "^";
16071                 }
16072             }
16073             enc = "a:" + flat;
16074         }else if(typeof v == "object"){
16075             var flat = "";
16076             for(var key in v){
16077                 if(typeof v[key] != "function"){
16078                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16079                 }
16080             }
16081             enc = "o:" + flat.substring(0, flat.length-1);
16082         }else{
16083             enc = "s:" + v;
16084         }
16085         return escape(enc);        
16086     }
16087 });
16088
16089 /*
16090  * Based on:
16091  * Ext JS Library 1.1.1
16092  * Copyright(c) 2006-2007, Ext JS, LLC.
16093  *
16094  * Originally Released Under LGPL - original licence link has changed is not relivant.
16095  *
16096  * Fork - LGPL
16097  * <script type="text/javascript">
16098  */
16099 /**
16100  * @class Roo.state.Manager
16101  * This is the global state manager. By default all components that are "state aware" check this class
16102  * for state information if you don't pass them a custom state provider. In order for this class
16103  * to be useful, it must be initialized with a provider when your application initializes.
16104  <pre><code>
16105 // in your initialization function
16106 init : function(){
16107    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16108    ...
16109    // supposed you have a {@link Roo.BorderLayout}
16110    var layout = new Roo.BorderLayout(...);
16111    layout.restoreState();
16112    // or a {Roo.BasicDialog}
16113    var dialog = new Roo.BasicDialog(...);
16114    dialog.restoreState();
16115  </code></pre>
16116  * @static
16117  */
16118 Roo.state.Manager = function(){
16119     var provider = new Roo.state.Provider();
16120     
16121     return {
16122         /**
16123          * Configures the default state provider for your application
16124          * @param {Provider} stateProvider The state provider to set
16125          */
16126         setProvider : function(stateProvider){
16127             provider = stateProvider;
16128         },
16129         
16130         /**
16131          * Returns the current value for a key
16132          * @param {String} name The key name
16133          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16134          * @return {Mixed} The state data
16135          */
16136         get : function(key, defaultValue){
16137             return provider.get(key, defaultValue);
16138         },
16139         
16140         /**
16141          * Sets the value for a key
16142          * @param {String} name The key name
16143          * @param {Mixed} value The state data
16144          */
16145          set : function(key, value){
16146             provider.set(key, value);
16147         },
16148         
16149         /**
16150          * Clears a value from the state
16151          * @param {String} name The key name
16152          */
16153         clear : function(key){
16154             provider.clear(key);
16155         },
16156         
16157         /**
16158          * Gets the currently configured state provider
16159          * @return {Provider} The state provider
16160          */
16161         getProvider : function(){
16162             return provider;
16163         }
16164     };
16165 }();
16166 /*
16167  * Based on:
16168  * Ext JS Library 1.1.1
16169  * Copyright(c) 2006-2007, Ext JS, LLC.
16170  *
16171  * Originally Released Under LGPL - original licence link has changed is not relivant.
16172  *
16173  * Fork - LGPL
16174  * <script type="text/javascript">
16175  */
16176 /**
16177  * @class Roo.state.CookieProvider
16178  * @extends Roo.state.Provider
16179  * The default Provider implementation which saves state via cookies.
16180  * <br />Usage:
16181  <pre><code>
16182    var cp = new Roo.state.CookieProvider({
16183        path: "/cgi-bin/",
16184        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16185        domain: "roojs.com"
16186    })
16187    Roo.state.Manager.setProvider(cp);
16188  </code></pre>
16189  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16190  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16191  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16192  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16193  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16194  * domain the page is running on including the 'www' like 'www.roojs.com')
16195  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16196  * @constructor
16197  * Create a new CookieProvider
16198  * @param {Object} config The configuration object
16199  */
16200 Roo.state.CookieProvider = function(config){
16201     Roo.state.CookieProvider.superclass.constructor.call(this);
16202     this.path = "/";
16203     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16204     this.domain = null;
16205     this.secure = false;
16206     Roo.apply(this, config);
16207     this.state = this.readCookies();
16208 };
16209
16210 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16211     // private
16212     set : function(name, value){
16213         if(typeof value == "undefined" || value === null){
16214             this.clear(name);
16215             return;
16216         }
16217         this.setCookie(name, value);
16218         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16219     },
16220
16221     // private
16222     clear : function(name){
16223         this.clearCookie(name);
16224         Roo.state.CookieProvider.superclass.clear.call(this, name);
16225     },
16226
16227     // private
16228     readCookies : function(){
16229         var cookies = {};
16230         var c = document.cookie + ";";
16231         var re = /\s?(.*?)=(.*?);/g;
16232         var matches;
16233         while((matches = re.exec(c)) != null){
16234             var name = matches[1];
16235             var value = matches[2];
16236             if(name && name.substring(0,3) == "ys-"){
16237                 cookies[name.substr(3)] = this.decodeValue(value);
16238             }
16239         }
16240         return cookies;
16241     },
16242
16243     // private
16244     setCookie : function(name, value){
16245         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16246            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16247            ((this.path == null) ? "" : ("; path=" + this.path)) +
16248            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16249            ((this.secure == true) ? "; secure" : "");
16250     },
16251
16252     // private
16253     clearCookie : function(name){
16254         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16255            ((this.path == null) ? "" : ("; path=" + this.path)) +
16256            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16257            ((this.secure == true) ? "; secure" : "");
16258     }
16259 });/*
16260  * Based on:
16261  * Ext JS Library 1.1.1
16262  * Copyright(c) 2006-2007, Ext JS, LLC.
16263  *
16264  * Originally Released Under LGPL - original licence link has changed is not relivant.
16265  *
16266  * Fork - LGPL
16267  * <script type="text/javascript">
16268  */
16269  
16270
16271 /**
16272  * @class Roo.ComponentMgr
16273  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16274  * @static
16275  */
16276 Roo.ComponentMgr = function(){
16277     var all = new Roo.util.MixedCollection();
16278
16279     return {
16280         /**
16281          * Registers a component.
16282          * @param {Roo.Component} c The component
16283          */
16284         register : function(c){
16285             all.add(c);
16286         },
16287
16288         /**
16289          * Unregisters a component.
16290          * @param {Roo.Component} c The component
16291          */
16292         unregister : function(c){
16293             all.remove(c);
16294         },
16295
16296         /**
16297          * Returns a component by id
16298          * @param {String} id The component id
16299          */
16300         get : function(id){
16301             return all.get(id);
16302         },
16303
16304         /**
16305          * Registers a function that will be called when a specified component is added to ComponentMgr
16306          * @param {String} id The component id
16307          * @param {Funtction} fn The callback function
16308          * @param {Object} scope The scope of the callback
16309          */
16310         onAvailable : function(id, fn, scope){
16311             all.on("add", function(index, o){
16312                 if(o.id == id){
16313                     fn.call(scope || o, o);
16314                     all.un("add", fn, scope);
16315                 }
16316             });
16317         }
16318     };
16319 }();/*
16320  * Based on:
16321  * Ext JS Library 1.1.1
16322  * Copyright(c) 2006-2007, Ext JS, LLC.
16323  *
16324  * Originally Released Under LGPL - original licence link has changed is not relivant.
16325  *
16326  * Fork - LGPL
16327  * <script type="text/javascript">
16328  */
16329  
16330 /**
16331  * @class Roo.Component
16332  * @extends Roo.util.Observable
16333  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16334  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16335  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16336  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16337  * All visual components (widgets) that require rendering into a layout should subclass Component.
16338  * @constructor
16339  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16340  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
16341  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16342  */
16343 Roo.Component = function(config){
16344     config = config || {};
16345     if(config.tagName || config.dom || typeof config == "string"){ // element object
16346         config = {el: config, id: config.id || config};
16347     }
16348     this.initialConfig = config;
16349
16350     Roo.apply(this, config);
16351     this.addEvents({
16352         /**
16353          * @event disable
16354          * Fires after the component is disabled.
16355              * @param {Roo.Component} this
16356              */
16357         disable : true,
16358         /**
16359          * @event enable
16360          * Fires after the component is enabled.
16361              * @param {Roo.Component} this
16362              */
16363         enable : true,
16364         /**
16365          * @event beforeshow
16366          * Fires before the component is shown.  Return false to stop the show.
16367              * @param {Roo.Component} this
16368              */
16369         beforeshow : true,
16370         /**
16371          * @event show
16372          * Fires after the component is shown.
16373              * @param {Roo.Component} this
16374              */
16375         show : true,
16376         /**
16377          * @event beforehide
16378          * Fires before the component is hidden. Return false to stop the hide.
16379              * @param {Roo.Component} this
16380              */
16381         beforehide : true,
16382         /**
16383          * @event hide
16384          * Fires after the component is hidden.
16385              * @param {Roo.Component} this
16386              */
16387         hide : true,
16388         /**
16389          * @event beforerender
16390          * Fires before the component is rendered. Return false to stop the render.
16391              * @param {Roo.Component} this
16392              */
16393         beforerender : true,
16394         /**
16395          * @event render
16396          * Fires after the component is rendered.
16397              * @param {Roo.Component} this
16398              */
16399         render : true,
16400         /**
16401          * @event beforedestroy
16402          * Fires before the component is destroyed. Return false to stop the destroy.
16403              * @param {Roo.Component} this
16404              */
16405         beforedestroy : true,
16406         /**
16407          * @event destroy
16408          * Fires after the component is destroyed.
16409              * @param {Roo.Component} this
16410              */
16411         destroy : true
16412     });
16413     if(!this.id){
16414         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16415     }
16416     Roo.ComponentMgr.register(this);
16417     Roo.Component.superclass.constructor.call(this);
16418     this.initComponent();
16419     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16420         this.render(this.renderTo);
16421         delete this.renderTo;
16422     }
16423 };
16424
16425 /** @private */
16426 Roo.Component.AUTO_ID = 1000;
16427
16428 Roo.extend(Roo.Component, Roo.util.Observable, {
16429     /**
16430      * @scope Roo.Component.prototype
16431      * @type {Boolean}
16432      * true if this component is hidden. Read-only.
16433      */
16434     hidden : false,
16435     /**
16436      * @type {Boolean}
16437      * true if this component is disabled. Read-only.
16438      */
16439     disabled : false,
16440     /**
16441      * @type {Boolean}
16442      * true if this component has been rendered. Read-only.
16443      */
16444     rendered : false,
16445     
16446     /** @cfg {String} disableClass
16447      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16448      */
16449     disabledClass : "x-item-disabled",
16450         /** @cfg {Boolean} allowDomMove
16451          * Whether the component can move the Dom node when rendering (defaults to true).
16452          */
16453     allowDomMove : true,
16454     /** @cfg {String} hideMode (display|visibility)
16455      * How this component should hidden. Supported values are
16456      * "visibility" (css visibility), "offsets" (negative offset position) and
16457      * "display" (css display) - defaults to "display".
16458      */
16459     hideMode: 'display',
16460
16461     /** @private */
16462     ctype : "Roo.Component",
16463
16464     /**
16465      * @cfg {String} actionMode 
16466      * which property holds the element that used for  hide() / show() / disable() / enable()
16467      * default is 'el' for forms you probably want to set this to fieldEl 
16468      */
16469     actionMode : "el",
16470
16471     /** @private */
16472     getActionEl : function(){
16473         return this[this.actionMode];
16474     },
16475
16476     initComponent : Roo.emptyFn,
16477     /**
16478      * If this is a lazy rendering component, render it to its container element.
16479      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
16480      */
16481     render : function(container, position){
16482         
16483         if(this.rendered){
16484             return this;
16485         }
16486         
16487         if(this.fireEvent("beforerender", this) === false){
16488             return false;
16489         }
16490         
16491         if(!container && this.el){
16492             this.el = Roo.get(this.el);
16493             container = this.el.dom.parentNode;
16494             this.allowDomMove = false;
16495         }
16496         this.container = Roo.get(container);
16497         this.rendered = true;
16498         if(position !== undefined){
16499             if(typeof position == 'number'){
16500                 position = this.container.dom.childNodes[position];
16501             }else{
16502                 position = Roo.getDom(position);
16503             }
16504         }
16505         this.onRender(this.container, position || null);
16506         if(this.cls){
16507             this.el.addClass(this.cls);
16508             delete this.cls;
16509         }
16510         if(this.style){
16511             this.el.applyStyles(this.style);
16512             delete this.style;
16513         }
16514         this.fireEvent("render", this);
16515         this.afterRender(this.container);
16516         if(this.hidden){
16517             this.hide();
16518         }
16519         if(this.disabled){
16520             this.disable();
16521         }
16522
16523         return this;
16524         
16525     },
16526
16527     /** @private */
16528     // default function is not really useful
16529     onRender : function(ct, position){
16530         if(this.el){
16531             this.el = Roo.get(this.el);
16532             if(this.allowDomMove !== false){
16533                 ct.dom.insertBefore(this.el.dom, position);
16534             }
16535         }
16536     },
16537
16538     /** @private */
16539     getAutoCreate : function(){
16540         var cfg = typeof this.autoCreate == "object" ?
16541                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16542         if(this.id && !cfg.id){
16543             cfg.id = this.id;
16544         }
16545         return cfg;
16546     },
16547
16548     /** @private */
16549     afterRender : Roo.emptyFn,
16550
16551     /**
16552      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16553      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16554      */
16555     destroy : function(){
16556         if(this.fireEvent("beforedestroy", this) !== false){
16557             this.purgeListeners();
16558             this.beforeDestroy();
16559             if(this.rendered){
16560                 this.el.removeAllListeners();
16561                 this.el.remove();
16562                 if(this.actionMode == "container"){
16563                     this.container.remove();
16564                 }
16565             }
16566             this.onDestroy();
16567             Roo.ComponentMgr.unregister(this);
16568             this.fireEvent("destroy", this);
16569         }
16570     },
16571
16572         /** @private */
16573     beforeDestroy : function(){
16574
16575     },
16576
16577         /** @private */
16578         onDestroy : function(){
16579
16580     },
16581
16582     /**
16583      * Returns the underlying {@link Roo.Element}.
16584      * @return {Roo.Element} The element
16585      */
16586     getEl : function(){
16587         return this.el;
16588     },
16589
16590     /**
16591      * Returns the id of this component.
16592      * @return {String}
16593      */
16594     getId : function(){
16595         return this.id;
16596     },
16597
16598     /**
16599      * Try to focus this component.
16600      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16601      * @return {Roo.Component} this
16602      */
16603     focus : function(selectText){
16604         if(this.rendered){
16605             this.el.focus();
16606             if(selectText === true){
16607                 this.el.dom.select();
16608             }
16609         }
16610         return this;
16611     },
16612
16613     /** @private */
16614     blur : function(){
16615         if(this.rendered){
16616             this.el.blur();
16617         }
16618         return this;
16619     },
16620
16621     /**
16622      * Disable this component.
16623      * @return {Roo.Component} this
16624      */
16625     disable : function(){
16626         if(this.rendered){
16627             this.onDisable();
16628         }
16629         this.disabled = true;
16630         this.fireEvent("disable", this);
16631         return this;
16632     },
16633
16634         // private
16635     onDisable : function(){
16636         this.getActionEl().addClass(this.disabledClass);
16637         this.el.dom.disabled = true;
16638     },
16639
16640     /**
16641      * Enable this component.
16642      * @return {Roo.Component} this
16643      */
16644     enable : function(){
16645         if(this.rendered){
16646             this.onEnable();
16647         }
16648         this.disabled = false;
16649         this.fireEvent("enable", this);
16650         return this;
16651     },
16652
16653         // private
16654     onEnable : function(){
16655         this.getActionEl().removeClass(this.disabledClass);
16656         this.el.dom.disabled = false;
16657     },
16658
16659     /**
16660      * Convenience function for setting disabled/enabled by boolean.
16661      * @param {Boolean} disabled
16662      */
16663     setDisabled : function(disabled){
16664         this[disabled ? "disable" : "enable"]();
16665     },
16666
16667     /**
16668      * Show this component.
16669      * @return {Roo.Component} this
16670      */
16671     show: function(){
16672         if(this.fireEvent("beforeshow", this) !== false){
16673             this.hidden = false;
16674             if(this.rendered){
16675                 this.onShow();
16676             }
16677             this.fireEvent("show", this);
16678         }
16679         return this;
16680     },
16681
16682     // private
16683     onShow : function(){
16684         var ae = this.getActionEl();
16685         if(this.hideMode == 'visibility'){
16686             ae.dom.style.visibility = "visible";
16687         }else if(this.hideMode == 'offsets'){
16688             ae.removeClass('x-hidden');
16689         }else{
16690             ae.dom.style.display = "";
16691         }
16692     },
16693
16694     /**
16695      * Hide this component.
16696      * @return {Roo.Component} this
16697      */
16698     hide: function(){
16699         if(this.fireEvent("beforehide", this) !== false){
16700             this.hidden = true;
16701             if(this.rendered){
16702                 this.onHide();
16703             }
16704             this.fireEvent("hide", this);
16705         }
16706         return this;
16707     },
16708
16709     // private
16710     onHide : function(){
16711         var ae = this.getActionEl();
16712         if(this.hideMode == 'visibility'){
16713             ae.dom.style.visibility = "hidden";
16714         }else if(this.hideMode == 'offsets'){
16715             ae.addClass('x-hidden');
16716         }else{
16717             ae.dom.style.display = "none";
16718         }
16719     },
16720
16721     /**
16722      * Convenience function to hide or show this component by boolean.
16723      * @param {Boolean} visible True to show, false to hide
16724      * @return {Roo.Component} this
16725      */
16726     setVisible: function(visible){
16727         if(visible) {
16728             this.show();
16729         }else{
16730             this.hide();
16731         }
16732         return this;
16733     },
16734
16735     /**
16736      * Returns true if this component is visible.
16737      */
16738     isVisible : function(){
16739         return this.getActionEl().isVisible();
16740     },
16741
16742     cloneConfig : function(overrides){
16743         overrides = overrides || {};
16744         var id = overrides.id || Roo.id();
16745         var cfg = Roo.applyIf(overrides, this.initialConfig);
16746         cfg.id = id; // prevent dup id
16747         return new this.constructor(cfg);
16748     }
16749 });/*
16750  * Based on:
16751  * Ext JS Library 1.1.1
16752  * Copyright(c) 2006-2007, Ext JS, LLC.
16753  *
16754  * Originally Released Under LGPL - original licence link has changed is not relivant.
16755  *
16756  * Fork - LGPL
16757  * <script type="text/javascript">
16758  */
16759
16760 /**
16761  * @class Roo.BoxComponent
16762  * @extends Roo.Component
16763  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16764  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16765  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16766  * layout containers.
16767  * @constructor
16768  * @param {Roo.Element/String/Object} config The configuration options.
16769  */
16770 Roo.BoxComponent = function(config){
16771     Roo.Component.call(this, config);
16772     this.addEvents({
16773         /**
16774          * @event resize
16775          * Fires after the component is resized.
16776              * @param {Roo.Component} this
16777              * @param {Number} adjWidth The box-adjusted width that was set
16778              * @param {Number} adjHeight The box-adjusted height that was set
16779              * @param {Number} rawWidth The width that was originally specified
16780              * @param {Number} rawHeight The height that was originally specified
16781              */
16782         resize : true,
16783         /**
16784          * @event move
16785          * Fires after the component is moved.
16786              * @param {Roo.Component} this
16787              * @param {Number} x The new x position
16788              * @param {Number} y The new y position
16789              */
16790         move : true
16791     });
16792 };
16793
16794 Roo.extend(Roo.BoxComponent, Roo.Component, {
16795     // private, set in afterRender to signify that the component has been rendered
16796     boxReady : false,
16797     // private, used to defer height settings to subclasses
16798     deferHeight: false,
16799     /** @cfg {Number} width
16800      * width (optional) size of component
16801      */
16802      /** @cfg {Number} height
16803      * height (optional) size of component
16804      */
16805      
16806     /**
16807      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16808      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16809      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16810      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16811      * @return {Roo.BoxComponent} this
16812      */
16813     setSize : function(w, h){
16814         // support for standard size objects
16815         if(typeof w == 'object'){
16816             h = w.height;
16817             w = w.width;
16818         }
16819         // not rendered
16820         if(!this.boxReady){
16821             this.width = w;
16822             this.height = h;
16823             return this;
16824         }
16825
16826         // prevent recalcs when not needed
16827         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16828             return this;
16829         }
16830         this.lastSize = {width: w, height: h};
16831
16832         var adj = this.adjustSize(w, h);
16833         var aw = adj.width, ah = adj.height;
16834         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16835             var rz = this.getResizeEl();
16836             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16837                 rz.setSize(aw, ah);
16838             }else if(!this.deferHeight && ah !== undefined){
16839                 rz.setHeight(ah);
16840             }else if(aw !== undefined){
16841                 rz.setWidth(aw);
16842             }
16843             this.onResize(aw, ah, w, h);
16844             this.fireEvent('resize', this, aw, ah, w, h);
16845         }
16846         return this;
16847     },
16848
16849     /**
16850      * Gets the current size of the component's underlying element.
16851      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16852      */
16853     getSize : function(){
16854         return this.el.getSize();
16855     },
16856
16857     /**
16858      * Gets the current XY position of the component's underlying element.
16859      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16860      * @return {Array} The XY position of the element (e.g., [100, 200])
16861      */
16862     getPosition : function(local){
16863         if(local === true){
16864             return [this.el.getLeft(true), this.el.getTop(true)];
16865         }
16866         return this.xy || this.el.getXY();
16867     },
16868
16869     /**
16870      * Gets the current box measurements of the component's underlying element.
16871      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16872      * @returns {Object} box An object in the format {x, y, width, height}
16873      */
16874     getBox : function(local){
16875         var s = this.el.getSize();
16876         if(local){
16877             s.x = this.el.getLeft(true);
16878             s.y = this.el.getTop(true);
16879         }else{
16880             var xy = this.xy || this.el.getXY();
16881             s.x = xy[0];
16882             s.y = xy[1];
16883         }
16884         return s;
16885     },
16886
16887     /**
16888      * Sets the current box measurements of the component's underlying element.
16889      * @param {Object} box An object in the format {x, y, width, height}
16890      * @returns {Roo.BoxComponent} this
16891      */
16892     updateBox : function(box){
16893         this.setSize(box.width, box.height);
16894         this.setPagePosition(box.x, box.y);
16895         return this;
16896     },
16897
16898     // protected
16899     getResizeEl : function(){
16900         return this.resizeEl || this.el;
16901     },
16902
16903     // protected
16904     getPositionEl : function(){
16905         return this.positionEl || this.el;
16906     },
16907
16908     /**
16909      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16910      * This method fires the move event.
16911      * @param {Number} left The new left
16912      * @param {Number} top The new top
16913      * @returns {Roo.BoxComponent} this
16914      */
16915     setPosition : function(x, y){
16916         this.x = x;
16917         this.y = y;
16918         if(!this.boxReady){
16919             return this;
16920         }
16921         var adj = this.adjustPosition(x, y);
16922         var ax = adj.x, ay = adj.y;
16923
16924         var el = this.getPositionEl();
16925         if(ax !== undefined || ay !== undefined){
16926             if(ax !== undefined && ay !== undefined){
16927                 el.setLeftTop(ax, ay);
16928             }else if(ax !== undefined){
16929                 el.setLeft(ax);
16930             }else if(ay !== undefined){
16931                 el.setTop(ay);
16932             }
16933             this.onPosition(ax, ay);
16934             this.fireEvent('move', this, ax, ay);
16935         }
16936         return this;
16937     },
16938
16939     /**
16940      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16941      * This method fires the move event.
16942      * @param {Number} x The new x position
16943      * @param {Number} y The new y position
16944      * @returns {Roo.BoxComponent} this
16945      */
16946     setPagePosition : function(x, y){
16947         this.pageX = x;
16948         this.pageY = y;
16949         if(!this.boxReady){
16950             return;
16951         }
16952         if(x === undefined || y === undefined){ // cannot translate undefined points
16953             return;
16954         }
16955         var p = this.el.translatePoints(x, y);
16956         this.setPosition(p.left, p.top);
16957         return this;
16958     },
16959
16960     // private
16961     onRender : function(ct, position){
16962         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16963         if(this.resizeEl){
16964             this.resizeEl = Roo.get(this.resizeEl);
16965         }
16966         if(this.positionEl){
16967             this.positionEl = Roo.get(this.positionEl);
16968         }
16969     },
16970
16971     // private
16972     afterRender : function(){
16973         Roo.BoxComponent.superclass.afterRender.call(this);
16974         this.boxReady = true;
16975         this.setSize(this.width, this.height);
16976         if(this.x || this.y){
16977             this.setPosition(this.x, this.y);
16978         }
16979         if(this.pageX || this.pageY){
16980             this.setPagePosition(this.pageX, this.pageY);
16981         }
16982     },
16983
16984     /**
16985      * Force the component's size to recalculate based on the underlying element's current height and width.
16986      * @returns {Roo.BoxComponent} this
16987      */
16988     syncSize : function(){
16989         delete this.lastSize;
16990         this.setSize(this.el.getWidth(), this.el.getHeight());
16991         return this;
16992     },
16993
16994     /**
16995      * Called after the component is resized, this method is empty by default but can be implemented by any
16996      * subclass that needs to perform custom logic after a resize occurs.
16997      * @param {Number} adjWidth The box-adjusted width that was set
16998      * @param {Number} adjHeight The box-adjusted height that was set
16999      * @param {Number} rawWidth The width that was originally specified
17000      * @param {Number} rawHeight The height that was originally specified
17001      */
17002     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17003
17004     },
17005
17006     /**
17007      * Called after the component is moved, this method is empty by default but can be implemented by any
17008      * subclass that needs to perform custom logic after a move occurs.
17009      * @param {Number} x The new x position
17010      * @param {Number} y The new y position
17011      */
17012     onPosition : function(x, y){
17013
17014     },
17015
17016     // private
17017     adjustSize : function(w, h){
17018         if(this.autoWidth){
17019             w = 'auto';
17020         }
17021         if(this.autoHeight){
17022             h = 'auto';
17023         }
17024         return {width : w, height: h};
17025     },
17026
17027     // private
17028     adjustPosition : function(x, y){
17029         return {x : x, y: y};
17030     }
17031 });/*
17032  * Based on:
17033  * Ext JS Library 1.1.1
17034  * Copyright(c) 2006-2007, Ext JS, LLC.
17035  *
17036  * Originally Released Under LGPL - original licence link has changed is not relivant.
17037  *
17038  * Fork - LGPL
17039  * <script type="text/javascript">
17040  */
17041  (function(){ 
17042 /**
17043  * @class Roo.Layer
17044  * @extends Roo.Element
17045  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17046  * automatic maintaining of shadow/shim positions.
17047  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17048  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17049  * you can pass a string with a CSS class name. False turns off the shadow.
17050  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17051  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17052  * @cfg {String} cls CSS class to add to the element
17053  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17054  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17055  * @constructor
17056  * @param {Object} config An object with config options.
17057  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17058  */
17059
17060 Roo.Layer = function(config, existingEl){
17061     config = config || {};
17062     var dh = Roo.DomHelper;
17063     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17064     if(existingEl){
17065         this.dom = Roo.getDom(existingEl);
17066     }
17067     if(!this.dom){
17068         var o = config.dh || {tag: "div", cls: "x-layer"};
17069         this.dom = dh.append(pel, o);
17070     }
17071     if(config.cls){
17072         this.addClass(config.cls);
17073     }
17074     this.constrain = config.constrain !== false;
17075     this.visibilityMode = Roo.Element.VISIBILITY;
17076     if(config.id){
17077         this.id = this.dom.id = config.id;
17078     }else{
17079         this.id = Roo.id(this.dom);
17080     }
17081     this.zindex = config.zindex || this.getZIndex();
17082     this.position("absolute", this.zindex);
17083     if(config.shadow){
17084         this.shadowOffset = config.shadowOffset || 4;
17085         this.shadow = new Roo.Shadow({
17086             offset : this.shadowOffset,
17087             mode : config.shadow
17088         });
17089     }else{
17090         this.shadowOffset = 0;
17091     }
17092     this.useShim = config.shim !== false && Roo.useShims;
17093     this.useDisplay = config.useDisplay;
17094     this.hide();
17095 };
17096
17097 var supr = Roo.Element.prototype;
17098
17099 // shims are shared among layer to keep from having 100 iframes
17100 var shims = [];
17101
17102 Roo.extend(Roo.Layer, Roo.Element, {
17103
17104     getZIndex : function(){
17105         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17106     },
17107
17108     getShim : function(){
17109         if(!this.useShim){
17110             return null;
17111         }
17112         if(this.shim){
17113             return this.shim;
17114         }
17115         var shim = shims.shift();
17116         if(!shim){
17117             shim = this.createShim();
17118             shim.enableDisplayMode('block');
17119             shim.dom.style.display = 'none';
17120             shim.dom.style.visibility = 'visible';
17121         }
17122         var pn = this.dom.parentNode;
17123         if(shim.dom.parentNode != pn){
17124             pn.insertBefore(shim.dom, this.dom);
17125         }
17126         shim.setStyle('z-index', this.getZIndex()-2);
17127         this.shim = shim;
17128         return shim;
17129     },
17130
17131     hideShim : function(){
17132         if(this.shim){
17133             this.shim.setDisplayed(false);
17134             shims.push(this.shim);
17135             delete this.shim;
17136         }
17137     },
17138
17139     disableShadow : function(){
17140         if(this.shadow){
17141             this.shadowDisabled = true;
17142             this.shadow.hide();
17143             this.lastShadowOffset = this.shadowOffset;
17144             this.shadowOffset = 0;
17145         }
17146     },
17147
17148     enableShadow : function(show){
17149         if(this.shadow){
17150             this.shadowDisabled = false;
17151             this.shadowOffset = this.lastShadowOffset;
17152             delete this.lastShadowOffset;
17153             if(show){
17154                 this.sync(true);
17155             }
17156         }
17157     },
17158
17159     // private
17160     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17161     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17162     sync : function(doShow){
17163         var sw = this.shadow;
17164         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17165             var sh = this.getShim();
17166
17167             var w = this.getWidth(),
17168                 h = this.getHeight();
17169
17170             var l = this.getLeft(true),
17171                 t = this.getTop(true);
17172
17173             if(sw && !this.shadowDisabled){
17174                 if(doShow && !sw.isVisible()){
17175                     sw.show(this);
17176                 }else{
17177                     sw.realign(l, t, w, h);
17178                 }
17179                 if(sh){
17180                     if(doShow){
17181                        sh.show();
17182                     }
17183                     // fit the shim behind the shadow, so it is shimmed too
17184                     var a = sw.adjusts, s = sh.dom.style;
17185                     s.left = (Math.min(l, l+a.l))+"px";
17186                     s.top = (Math.min(t, t+a.t))+"px";
17187                     s.width = (w+a.w)+"px";
17188                     s.height = (h+a.h)+"px";
17189                 }
17190             }else if(sh){
17191                 if(doShow){
17192                    sh.show();
17193                 }
17194                 sh.setSize(w, h);
17195                 sh.setLeftTop(l, t);
17196             }
17197             
17198         }
17199     },
17200
17201     // private
17202     destroy : function(){
17203         this.hideShim();
17204         if(this.shadow){
17205             this.shadow.hide();
17206         }
17207         this.removeAllListeners();
17208         var pn = this.dom.parentNode;
17209         if(pn){
17210             pn.removeChild(this.dom);
17211         }
17212         Roo.Element.uncache(this.id);
17213     },
17214
17215     remove : function(){
17216         this.destroy();
17217     },
17218
17219     // private
17220     beginUpdate : function(){
17221         this.updating = true;
17222     },
17223
17224     // private
17225     endUpdate : function(){
17226         this.updating = false;
17227         this.sync(true);
17228     },
17229
17230     // private
17231     hideUnders : function(negOffset){
17232         if(this.shadow){
17233             this.shadow.hide();
17234         }
17235         this.hideShim();
17236     },
17237
17238     // private
17239     constrainXY : function(){
17240         if(this.constrain){
17241             var vw = Roo.lib.Dom.getViewWidth(),
17242                 vh = Roo.lib.Dom.getViewHeight();
17243             var s = Roo.get(document).getScroll();
17244
17245             var xy = this.getXY();
17246             var x = xy[0], y = xy[1];   
17247             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17248             // only move it if it needs it
17249             var moved = false;
17250             // first validate right/bottom
17251             if((x + w) > vw+s.left){
17252                 x = vw - w - this.shadowOffset;
17253                 moved = true;
17254             }
17255             if((y + h) > vh+s.top){
17256                 y = vh - h - this.shadowOffset;
17257                 moved = true;
17258             }
17259             // then make sure top/left isn't negative
17260             if(x < s.left){
17261                 x = s.left;
17262                 moved = true;
17263             }
17264             if(y < s.top){
17265                 y = s.top;
17266                 moved = true;
17267             }
17268             if(moved){
17269                 if(this.avoidY){
17270                     var ay = this.avoidY;
17271                     if(y <= ay && (y+h) >= ay){
17272                         y = ay-h-5;   
17273                     }
17274                 }
17275                 xy = [x, y];
17276                 this.storeXY(xy);
17277                 supr.setXY.call(this, xy);
17278                 this.sync();
17279             }
17280         }
17281     },
17282
17283     isVisible : function(){
17284         return this.visible;    
17285     },
17286
17287     // private
17288     showAction : function(){
17289         this.visible = true; // track visibility to prevent getStyle calls
17290         if(this.useDisplay === true){
17291             this.setDisplayed("");
17292         }else if(this.lastXY){
17293             supr.setXY.call(this, this.lastXY);
17294         }else if(this.lastLT){
17295             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17296         }
17297     },
17298
17299     // private
17300     hideAction : function(){
17301         this.visible = false;
17302         if(this.useDisplay === true){
17303             this.setDisplayed(false);
17304         }else{
17305             this.setLeftTop(-10000,-10000);
17306         }
17307     },
17308
17309     // overridden Element method
17310     setVisible : function(v, a, d, c, e){
17311         if(v){
17312             this.showAction();
17313         }
17314         if(a && v){
17315             var cb = function(){
17316                 this.sync(true);
17317                 if(c){
17318                     c();
17319                 }
17320             }.createDelegate(this);
17321             supr.setVisible.call(this, true, true, d, cb, e);
17322         }else{
17323             if(!v){
17324                 this.hideUnders(true);
17325             }
17326             var cb = c;
17327             if(a){
17328                 cb = function(){
17329                     this.hideAction();
17330                     if(c){
17331                         c();
17332                     }
17333                 }.createDelegate(this);
17334             }
17335             supr.setVisible.call(this, v, a, d, cb, e);
17336             if(v){
17337                 this.sync(true);
17338             }else if(!a){
17339                 this.hideAction();
17340             }
17341         }
17342     },
17343
17344     storeXY : function(xy){
17345         delete this.lastLT;
17346         this.lastXY = xy;
17347     },
17348
17349     storeLeftTop : function(left, top){
17350         delete this.lastXY;
17351         this.lastLT = [left, top];
17352     },
17353
17354     // private
17355     beforeFx : function(){
17356         this.beforeAction();
17357         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17358     },
17359
17360     // private
17361     afterFx : function(){
17362         Roo.Layer.superclass.afterFx.apply(this, arguments);
17363         this.sync(this.isVisible());
17364     },
17365
17366     // private
17367     beforeAction : function(){
17368         if(!this.updating && this.shadow){
17369             this.shadow.hide();
17370         }
17371     },
17372
17373     // overridden Element method
17374     setLeft : function(left){
17375         this.storeLeftTop(left, this.getTop(true));
17376         supr.setLeft.apply(this, arguments);
17377         this.sync();
17378     },
17379
17380     setTop : function(top){
17381         this.storeLeftTop(this.getLeft(true), top);
17382         supr.setTop.apply(this, arguments);
17383         this.sync();
17384     },
17385
17386     setLeftTop : function(left, top){
17387         this.storeLeftTop(left, top);
17388         supr.setLeftTop.apply(this, arguments);
17389         this.sync();
17390     },
17391
17392     setXY : function(xy, a, d, c, e){
17393         this.fixDisplay();
17394         this.beforeAction();
17395         this.storeXY(xy);
17396         var cb = this.createCB(c);
17397         supr.setXY.call(this, xy, a, d, cb, e);
17398         if(!a){
17399             cb();
17400         }
17401     },
17402
17403     // private
17404     createCB : function(c){
17405         var el = this;
17406         return function(){
17407             el.constrainXY();
17408             el.sync(true);
17409             if(c){
17410                 c();
17411             }
17412         };
17413     },
17414
17415     // overridden Element method
17416     setX : function(x, a, d, c, e){
17417         this.setXY([x, this.getY()], a, d, c, e);
17418     },
17419
17420     // overridden Element method
17421     setY : function(y, a, d, c, e){
17422         this.setXY([this.getX(), y], a, d, c, e);
17423     },
17424
17425     // overridden Element method
17426     setSize : function(w, h, a, d, c, e){
17427         this.beforeAction();
17428         var cb = this.createCB(c);
17429         supr.setSize.call(this, w, h, a, d, cb, e);
17430         if(!a){
17431             cb();
17432         }
17433     },
17434
17435     // overridden Element method
17436     setWidth : function(w, a, d, c, e){
17437         this.beforeAction();
17438         var cb = this.createCB(c);
17439         supr.setWidth.call(this, w, a, d, cb, e);
17440         if(!a){
17441             cb();
17442         }
17443     },
17444
17445     // overridden Element method
17446     setHeight : function(h, a, d, c, e){
17447         this.beforeAction();
17448         var cb = this.createCB(c);
17449         supr.setHeight.call(this, h, a, d, cb, e);
17450         if(!a){
17451             cb();
17452         }
17453     },
17454
17455     // overridden Element method
17456     setBounds : function(x, y, w, h, a, d, c, e){
17457         this.beforeAction();
17458         var cb = this.createCB(c);
17459         if(!a){
17460             this.storeXY([x, y]);
17461             supr.setXY.call(this, [x, y]);
17462             supr.setSize.call(this, w, h, a, d, cb, e);
17463             cb();
17464         }else{
17465             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17466         }
17467         return this;
17468     },
17469     
17470     /**
17471      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17472      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17473      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17474      * @param {Number} zindex The new z-index to set
17475      * @return {this} The Layer
17476      */
17477     setZIndex : function(zindex){
17478         this.zindex = zindex;
17479         this.setStyle("z-index", zindex + 2);
17480         if(this.shadow){
17481             this.shadow.setZIndex(zindex + 1);
17482         }
17483         if(this.shim){
17484             this.shim.setStyle("z-index", zindex);
17485         }
17486     }
17487 });
17488 })();/*
17489  * Original code for Roojs - LGPL
17490  * <script type="text/javascript">
17491  */
17492  
17493 /**
17494  * @class Roo.XComponent
17495  * A delayed Element creator...
17496  * Or a way to group chunks of interface together.
17497  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17498  *  used in conjunction with XComponent.build() it will create an instance of each element,
17499  *  then call addxtype() to build the User interface.
17500  * 
17501  * Mypart.xyx = new Roo.XComponent({
17502
17503     parent : 'Mypart.xyz', // empty == document.element.!!
17504     order : '001',
17505     name : 'xxxx'
17506     region : 'xxxx'
17507     disabled : function() {} 
17508      
17509     tree : function() { // return an tree of xtype declared components
17510         var MODULE = this;
17511         return 
17512         {
17513             xtype : 'NestedLayoutPanel',
17514             // technicall
17515         }
17516      ]
17517  *})
17518  *
17519  *
17520  * It can be used to build a big heiracy, with parent etc.
17521  * or you can just use this to render a single compoent to a dom element
17522  * MYPART.render(Roo.Element | String(id) | dom_element )
17523  *
17524  *
17525  * Usage patterns.
17526  *
17527  * Classic Roo
17528  *
17529  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17530  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17531  *
17532  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17533  *
17534  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17535  * - if mulitple topModules exist, the last one is defined as the top module.
17536  *
17537  * Embeded Roo
17538  * 
17539  * When the top level or multiple modules are to embedded into a existing HTML page,
17540  * the parent element can container '#id' of the element where the module will be drawn.
17541  *
17542  * Bootstrap Roo
17543  *
17544  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17545  * it relies more on a include mechanism, where sub modules are included into an outer page.
17546  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17547  * 
17548  * Bootstrap Roo Included elements
17549  *
17550  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17551  * hence confusing the component builder as it thinks there are multiple top level elements. 
17552  *
17553  * String Over-ride & Translations
17554  *
17555  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17556  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17557  * are needed. @see Roo.XComponent.overlayString  
17558  * 
17559  * 
17560  * 
17561  * @extends Roo.util.Observable
17562  * @constructor
17563  * @param cfg {Object} configuration of component
17564  * 
17565  */
17566 Roo.XComponent = function(cfg) {
17567     Roo.apply(this, cfg);
17568     this.addEvents({ 
17569         /**
17570              * @event built
17571              * Fires when this the componnt is built
17572              * @param {Roo.XComponent} c the component
17573              */
17574         'built' : true
17575         
17576     });
17577     this.region = this.region || 'center'; // default..
17578     Roo.XComponent.register(this);
17579     this.modules = false;
17580     this.el = false; // where the layout goes..
17581     
17582     
17583 }
17584 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17585     /**
17586      * @property el
17587      * The created element (with Roo.factory())
17588      * @type {Roo.Layout}
17589      */
17590     el  : false,
17591     
17592     /**
17593      * @property el
17594      * for BC  - use el in new code
17595      * @type {Roo.Layout}
17596      */
17597     panel : false,
17598     
17599     /**
17600      * @property layout
17601      * for BC  - use el in new code
17602      * @type {Roo.Layout}
17603      */
17604     layout : false,
17605     
17606      /**
17607      * @cfg {Function|boolean} disabled
17608      * If this module is disabled by some rule, return true from the funtion
17609      */
17610     disabled : false,
17611     
17612     /**
17613      * @cfg {String} parent 
17614      * Name of parent element which it get xtype added to..
17615      */
17616     parent: false,
17617     
17618     /**
17619      * @cfg {String} order
17620      * Used to set the order in which elements are created (usefull for multiple tabs)
17621      */
17622     
17623     order : false,
17624     /**
17625      * @cfg {String} name
17626      * String to display while loading.
17627      */
17628     name : false,
17629     /**
17630      * @cfg {String} region
17631      * Region to render component to (defaults to center)
17632      */
17633     region : 'center',
17634     
17635     /**
17636      * @cfg {Array} items
17637      * A single item array - the first element is the root of the tree..
17638      * It's done this way to stay compatible with the Xtype system...
17639      */
17640     items : false,
17641     
17642     /**
17643      * @property _tree
17644      * The method that retuns the tree of parts that make up this compoennt 
17645      * @type {function}
17646      */
17647     _tree  : false,
17648     
17649      /**
17650      * render
17651      * render element to dom or tree
17652      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17653      */
17654     
17655     render : function(el)
17656     {
17657         
17658         el = el || false;
17659         var hp = this.parent ? 1 : 0;
17660         Roo.debug &&  Roo.log(this);
17661         
17662         var tree = this._tree ? this._tree() : this.tree();
17663
17664         
17665         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17666             // if parent is a '#.....' string, then let's use that..
17667             var ename = this.parent.substr(1);
17668             this.parent = false;
17669             Roo.debug && Roo.log(ename);
17670             switch (ename) {
17671                 case 'bootstrap-body':
17672                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17673                         // this is the BorderLayout standard?
17674                        this.parent = { el : true };
17675                        break;
17676                     }
17677                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17678                         // need to insert stuff...
17679                         this.parent =  {
17680                              el : new Roo.bootstrap.layout.Border({
17681                                  el : document.body, 
17682                      
17683                                  center: {
17684                                     titlebar: false,
17685                                     autoScroll:false,
17686                                     closeOnTab: true,
17687                                     tabPosition: 'top',
17688                                       //resizeTabs: true,
17689                                     alwaysShowTabs: true,
17690                                     hideTabs: false
17691                                      //minTabWidth: 140
17692                                  }
17693                              })
17694                         
17695                          };
17696                          break;
17697                     }
17698                          
17699                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17700                         this.parent = { el :  new  Roo.bootstrap.Body() };
17701                         Roo.debug && Roo.log("setting el to doc body");
17702                          
17703                     } else {
17704                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17705                     }
17706                     break;
17707                 case 'bootstrap':
17708                     this.parent = { el : true};
17709                     // fall through
17710                 default:
17711                     el = Roo.get(ename);
17712                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17713                         this.parent = { el : true};
17714                     }
17715                     
17716                     break;
17717             }
17718                 
17719             
17720             if (!el && !this.parent) {
17721                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17722                 return;
17723             }
17724         }
17725         
17726         Roo.debug && Roo.log("EL:");
17727         Roo.debug && Roo.log(el);
17728         Roo.debug && Roo.log("this.parent.el:");
17729         Roo.debug && Roo.log(this.parent.el);
17730         
17731
17732         // altertive root elements ??? - we need a better way to indicate these.
17733         var is_alt = Roo.XComponent.is_alt ||
17734                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17735                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17736                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17737         
17738         
17739         
17740         if (!this.parent && is_alt) {
17741             //el = Roo.get(document.body);
17742             this.parent = { el : true };
17743         }
17744             
17745             
17746         
17747         if (!this.parent) {
17748             
17749             Roo.debug && Roo.log("no parent - creating one");
17750             
17751             el = el ? Roo.get(el) : false;      
17752             
17753             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17754                 
17755                 this.parent =  {
17756                     el : new Roo.bootstrap.layout.Border({
17757                         el: el || document.body,
17758                     
17759                         center: {
17760                             titlebar: false,
17761                             autoScroll:false,
17762                             closeOnTab: true,
17763                             tabPosition: 'top',
17764                              //resizeTabs: true,
17765                             alwaysShowTabs: false,
17766                             hideTabs: true,
17767                             minTabWidth: 140,
17768                             overflow: 'visible'
17769                          }
17770                      })
17771                 };
17772             } else {
17773             
17774                 // it's a top level one..
17775                 this.parent =  {
17776                     el : new Roo.BorderLayout(el || document.body, {
17777                         center: {
17778                             titlebar: false,
17779                             autoScroll:false,
17780                             closeOnTab: true,
17781                             tabPosition: 'top',
17782                              //resizeTabs: true,
17783                             alwaysShowTabs: el && hp? false :  true,
17784                             hideTabs: el || !hp ? true :  false,
17785                             minTabWidth: 140
17786                          }
17787                     })
17788                 };
17789             }
17790         }
17791         
17792         if (!this.parent.el) {
17793                 // probably an old style ctor, which has been disabled.
17794                 return;
17795
17796         }
17797                 // The 'tree' method is  '_tree now' 
17798             
17799         tree.region = tree.region || this.region;
17800         var is_body = false;
17801         if (this.parent.el === true) {
17802             // bootstrap... - body..
17803             if (el) {
17804                 tree.el = el;
17805             }
17806             this.parent.el = Roo.factory(tree);
17807             is_body = true;
17808         }
17809         
17810         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17811         this.fireEvent('built', this);
17812         
17813         this.panel = this.el;
17814         this.layout = this.panel.layout;
17815         this.parentLayout = this.parent.layout  || false;  
17816          
17817     }
17818     
17819 });
17820
17821 Roo.apply(Roo.XComponent, {
17822     /**
17823      * @property  hideProgress
17824      * true to disable the building progress bar.. usefull on single page renders.
17825      * @type Boolean
17826      */
17827     hideProgress : false,
17828     /**
17829      * @property  buildCompleted
17830      * True when the builder has completed building the interface.
17831      * @type Boolean
17832      */
17833     buildCompleted : false,
17834      
17835     /**
17836      * @property  topModule
17837      * the upper most module - uses document.element as it's constructor.
17838      * @type Object
17839      */
17840      
17841     topModule  : false,
17842       
17843     /**
17844      * @property  modules
17845      * array of modules to be created by registration system.
17846      * @type {Array} of Roo.XComponent
17847      */
17848     
17849     modules : [],
17850     /**
17851      * @property  elmodules
17852      * array of modules to be created by which use #ID 
17853      * @type {Array} of Roo.XComponent
17854      */
17855      
17856     elmodules : [],
17857
17858      /**
17859      * @property  is_alt
17860      * Is an alternative Root - normally used by bootstrap or other systems,
17861      *    where the top element in the tree can wrap 'body' 
17862      * @type {boolean}  (default false)
17863      */
17864      
17865     is_alt : false,
17866     /**
17867      * @property  build_from_html
17868      * Build elements from html - used by bootstrap HTML stuff 
17869      *    - this is cleared after build is completed
17870      * @type {boolean}    (default false)
17871      */
17872      
17873     build_from_html : false,
17874     /**
17875      * Register components to be built later.
17876      *
17877      * This solves the following issues
17878      * - Building is not done on page load, but after an authentication process has occured.
17879      * - Interface elements are registered on page load
17880      * - Parent Interface elements may not be loaded before child, so this handles that..
17881      * 
17882      *
17883      * example:
17884      * 
17885      * MyApp.register({
17886           order : '000001',
17887           module : 'Pman.Tab.projectMgr',
17888           region : 'center',
17889           parent : 'Pman.layout',
17890           disabled : false,  // or use a function..
17891         })
17892      
17893      * * @param {Object} details about module
17894      */
17895     register : function(obj) {
17896                 
17897         Roo.XComponent.event.fireEvent('register', obj);
17898         switch(typeof(obj.disabled) ) {
17899                 
17900             case 'undefined':
17901                 break;
17902             
17903             case 'function':
17904                 if ( obj.disabled() ) {
17905                         return;
17906                 }
17907                 break;
17908             
17909             default:
17910                 if (obj.disabled || obj.region == '#disabled') {
17911                         return;
17912                 }
17913                 break;
17914         }
17915                 
17916         this.modules.push(obj);
17917          
17918     },
17919     /**
17920      * convert a string to an object..
17921      * eg. 'AAA.BBB' -> finds AAA.BBB
17922
17923      */
17924     
17925     toObject : function(str)
17926     {
17927         if (!str || typeof(str) == 'object') {
17928             return str;
17929         }
17930         if (str.substring(0,1) == '#') {
17931             return str;
17932         }
17933
17934         var ar = str.split('.');
17935         var rt, o;
17936         rt = ar.shift();
17937             /** eval:var:o */
17938         try {
17939             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17940         } catch (e) {
17941             throw "Module not found : " + str;
17942         }
17943         
17944         if (o === false) {
17945             throw "Module not found : " + str;
17946         }
17947         Roo.each(ar, function(e) {
17948             if (typeof(o[e]) == 'undefined') {
17949                 throw "Module not found : " + str;
17950             }
17951             o = o[e];
17952         });
17953         
17954         return o;
17955         
17956     },
17957     
17958     
17959     /**
17960      * move modules into their correct place in the tree..
17961      * 
17962      */
17963     preBuild : function ()
17964     {
17965         var _t = this;
17966         Roo.each(this.modules , function (obj)
17967         {
17968             Roo.XComponent.event.fireEvent('beforebuild', obj);
17969             
17970             var opar = obj.parent;
17971             try { 
17972                 obj.parent = this.toObject(opar);
17973             } catch(e) {
17974                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17975                 return;
17976             }
17977             
17978             if (!obj.parent) {
17979                 Roo.debug && Roo.log("GOT top level module");
17980                 Roo.debug && Roo.log(obj);
17981                 obj.modules = new Roo.util.MixedCollection(false, 
17982                     function(o) { return o.order + '' }
17983                 );
17984                 this.topModule = obj;
17985                 return;
17986             }
17987                         // parent is a string (usually a dom element name..)
17988             if (typeof(obj.parent) == 'string') {
17989                 this.elmodules.push(obj);
17990                 return;
17991             }
17992             if (obj.parent.constructor != Roo.XComponent) {
17993                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17994             }
17995             if (!obj.parent.modules) {
17996                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17997                     function(o) { return o.order + '' }
17998                 );
17999             }
18000             if (obj.parent.disabled) {
18001                 obj.disabled = true;
18002             }
18003             obj.parent.modules.add(obj);
18004         }, this);
18005     },
18006     
18007      /**
18008      * make a list of modules to build.
18009      * @return {Array} list of modules. 
18010      */ 
18011     
18012     buildOrder : function()
18013     {
18014         var _this = this;
18015         var cmp = function(a,b) {   
18016             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18017         };
18018         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18019             throw "No top level modules to build";
18020         }
18021         
18022         // make a flat list in order of modules to build.
18023         var mods = this.topModule ? [ this.topModule ] : [];
18024                 
18025         
18026         // elmodules (is a list of DOM based modules )
18027         Roo.each(this.elmodules, function(e) {
18028             mods.push(e);
18029             if (!this.topModule &&
18030                 typeof(e.parent) == 'string' &&
18031                 e.parent.substring(0,1) == '#' &&
18032                 Roo.get(e.parent.substr(1))
18033                ) {
18034                 
18035                 _this.topModule = e;
18036             }
18037             
18038         });
18039
18040         
18041         // add modules to their parents..
18042         var addMod = function(m) {
18043             Roo.debug && Roo.log("build Order: add: " + m.name);
18044                 
18045             mods.push(m);
18046             if (m.modules && !m.disabled) {
18047                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18048                 m.modules.keySort('ASC',  cmp );
18049                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18050     
18051                 m.modules.each(addMod);
18052             } else {
18053                 Roo.debug && Roo.log("build Order: no child modules");
18054             }
18055             // not sure if this is used any more..
18056             if (m.finalize) {
18057                 m.finalize.name = m.name + " (clean up) ";
18058                 mods.push(m.finalize);
18059             }
18060             
18061         }
18062         if (this.topModule && this.topModule.modules) { 
18063             this.topModule.modules.keySort('ASC',  cmp );
18064             this.topModule.modules.each(addMod);
18065         } 
18066         return mods;
18067     },
18068     
18069      /**
18070      * Build the registered modules.
18071      * @param {Object} parent element.
18072      * @param {Function} optional method to call after module has been added.
18073      * 
18074      */ 
18075    
18076     build : function(opts) 
18077     {
18078         
18079         if (typeof(opts) != 'undefined') {
18080             Roo.apply(this,opts);
18081         }
18082         
18083         this.preBuild();
18084         var mods = this.buildOrder();
18085       
18086         //this.allmods = mods;
18087         //Roo.debug && Roo.log(mods);
18088         //return;
18089         if (!mods.length) { // should not happen
18090             throw "NO modules!!!";
18091         }
18092         
18093         
18094         var msg = "Building Interface...";
18095         // flash it up as modal - so we store the mask!?
18096         if (!this.hideProgress && Roo.MessageBox) {
18097             Roo.MessageBox.show({ title: 'loading' });
18098             Roo.MessageBox.show({
18099                title: "Please wait...",
18100                msg: msg,
18101                width:450,
18102                progress:true,
18103                buttons : false,
18104                closable:false,
18105                modal: false
18106               
18107             });
18108         }
18109         var total = mods.length;
18110         
18111         var _this = this;
18112         var progressRun = function() {
18113             if (!mods.length) {
18114                 Roo.debug && Roo.log('hide?');
18115                 if (!this.hideProgress && Roo.MessageBox) {
18116                     Roo.MessageBox.hide();
18117                 }
18118                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18119                 
18120                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18121                 
18122                 // THE END...
18123                 return false;   
18124             }
18125             
18126             var m = mods.shift();
18127             
18128             
18129             Roo.debug && Roo.log(m);
18130             // not sure if this is supported any more.. - modules that are are just function
18131             if (typeof(m) == 'function') { 
18132                 m.call(this);
18133                 return progressRun.defer(10, _this);
18134             } 
18135             
18136             
18137             msg = "Building Interface " + (total  - mods.length) + 
18138                     " of " + total + 
18139                     (m.name ? (' - ' + m.name) : '');
18140                         Roo.debug && Roo.log(msg);
18141             if (!_this.hideProgress &&  Roo.MessageBox) { 
18142                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18143             }
18144             
18145          
18146             // is the module disabled?
18147             var disabled = (typeof(m.disabled) == 'function') ?
18148                 m.disabled.call(m.module.disabled) : m.disabled;    
18149             
18150             
18151             if (disabled) {
18152                 return progressRun(); // we do not update the display!
18153             }
18154             
18155             // now build 
18156             
18157                         
18158                         
18159             m.render();
18160             // it's 10 on top level, and 1 on others??? why...
18161             return progressRun.defer(10, _this);
18162              
18163         }
18164         progressRun.defer(1, _this);
18165      
18166         
18167         
18168     },
18169     /**
18170      * Overlay a set of modified strings onto a component
18171      * This is dependant on our builder exporting the strings and 'named strings' elements.
18172      * 
18173      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18174      * @param {Object} associative array of 'named' string and it's new value.
18175      * 
18176      */
18177         overlayStrings : function( component, strings )
18178     {
18179         if (typeof(component['_named_strings']) == 'undefined') {
18180             throw "ERROR: component does not have _named_strings";
18181         }
18182         for ( var k in strings ) {
18183             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18184             if (md !== false) {
18185                 component['_strings'][md] = strings[k];
18186             } else {
18187                 Roo.log('could not find named string: ' + k + ' in');
18188                 Roo.log(component);
18189             }
18190             
18191         }
18192         
18193     },
18194     
18195         
18196         /**
18197          * Event Object.
18198          *
18199          *
18200          */
18201         event: false, 
18202     /**
18203          * wrapper for event.on - aliased later..  
18204          * Typically use to register a event handler for register:
18205          *
18206          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18207          *
18208          */
18209     on : false
18210    
18211     
18212     
18213 });
18214
18215 Roo.XComponent.event = new Roo.util.Observable({
18216                 events : { 
18217                         /**
18218                          * @event register
18219                          * Fires when an Component is registered,
18220                          * set the disable property on the Component to stop registration.
18221                          * @param {Roo.XComponent} c the component being registerd.
18222                          * 
18223                          */
18224                         'register' : true,
18225             /**
18226                          * @event beforebuild
18227                          * Fires before each Component is built
18228                          * can be used to apply permissions.
18229                          * @param {Roo.XComponent} c the component being registerd.
18230                          * 
18231                          */
18232                         'beforebuild' : true,
18233                         /**
18234                          * @event buildcomplete
18235                          * Fires on the top level element when all elements have been built
18236                          * @param {Roo.XComponent} the top level component.
18237                          */
18238                         'buildcomplete' : true
18239                         
18240                 }
18241 });
18242
18243 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18244  //
18245  /**
18246  * marked - a markdown parser
18247  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18248  * https://github.com/chjj/marked
18249  */
18250
18251
18252 /**
18253  *
18254  * Roo.Markdown - is a very crude wrapper around marked..
18255  *
18256  * usage:
18257  * 
18258  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18259  * 
18260  * Note: move the sample code to the bottom of this
18261  * file before uncommenting it.
18262  *
18263  */
18264
18265 Roo.Markdown = {};
18266 Roo.Markdown.toHtml = function(text) {
18267     
18268     var c = new Roo.Markdown.marked.setOptions({
18269             renderer: new Roo.Markdown.marked.Renderer(),
18270             gfm: true,
18271             tables: true,
18272             breaks: false,
18273             pedantic: false,
18274             sanitize: false,
18275             smartLists: true,
18276             smartypants: false
18277           });
18278     // A FEW HACKS!!?
18279     
18280     text = text.replace(/\\\n/g,' ');
18281     return Roo.Markdown.marked(text);
18282 };
18283 //
18284 // converter
18285 //
18286 // Wraps all "globals" so that the only thing
18287 // exposed is makeHtml().
18288 //
18289 (function() {
18290     
18291      /**
18292          * eval:var:escape
18293          * eval:var:unescape
18294          * eval:var:replace
18295          */
18296       
18297     /**
18298      * Helpers
18299      */
18300     
18301     var escape = function (html, encode) {
18302       return html
18303         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18304         .replace(/</g, '&lt;')
18305         .replace(/>/g, '&gt;')
18306         .replace(/"/g, '&quot;')
18307         .replace(/'/g, '&#39;');
18308     }
18309     
18310     var unescape = function (html) {
18311         // explicitly match decimal, hex, and named HTML entities 
18312       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18313         n = n.toLowerCase();
18314         if (n === 'colon') { return ':'; }
18315         if (n.charAt(0) === '#') {
18316           return n.charAt(1) === 'x'
18317             ? String.fromCharCode(parseInt(n.substring(2), 16))
18318             : String.fromCharCode(+n.substring(1));
18319         }
18320         return '';
18321       });
18322     }
18323     
18324     var replace = function (regex, opt) {
18325       regex = regex.source;
18326       opt = opt || '';
18327       return function self(name, val) {
18328         if (!name) { return new RegExp(regex, opt); }
18329         val = val.source || val;
18330         val = val.replace(/(^|[^\[])\^/g, '$1');
18331         regex = regex.replace(name, val);
18332         return self;
18333       };
18334     }
18335
18336
18337          /**
18338          * eval:var:noop
18339     */
18340     var noop = function () {}
18341     noop.exec = noop;
18342     
18343          /**
18344          * eval:var:merge
18345     */
18346     var merge = function (obj) {
18347       var i = 1
18348         , target
18349         , key;
18350     
18351       for (; i < arguments.length; i++) {
18352         target = arguments[i];
18353         for (key in target) {
18354           if (Object.prototype.hasOwnProperty.call(target, key)) {
18355             obj[key] = target[key];
18356           }
18357         }
18358       }
18359     
18360       return obj;
18361     }
18362     
18363     
18364     /**
18365      * Block-Level Grammar
18366      */
18367     
18368     
18369     
18370     
18371     var block = {
18372       newline: /^\n+/,
18373       code: /^( {4}[^\n]+\n*)+/,
18374       fences: noop,
18375       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18376       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18377       nptable: noop,
18378       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18379       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18380       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18381       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18382       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18383       table: noop,
18384       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18385       text: /^[^\n]+/
18386     };
18387     
18388     block.bullet = /(?:[*+-]|\d+\.)/;
18389     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18390     block.item = replace(block.item, 'gm')
18391       (/bull/g, block.bullet)
18392       ();
18393     
18394     block.list = replace(block.list)
18395       (/bull/g, block.bullet)
18396       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18397       ('def', '\\n+(?=' + block.def.source + ')')
18398       ();
18399     
18400     block.blockquote = replace(block.blockquote)
18401       ('def', block.def)
18402       ();
18403     
18404     block._tag = '(?!(?:'
18405       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18406       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18407       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18408     
18409     block.html = replace(block.html)
18410       ('comment', /<!--[\s\S]*?-->/)
18411       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18412       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18413       (/tag/g, block._tag)
18414       ();
18415     
18416     block.paragraph = replace(block.paragraph)
18417       ('hr', block.hr)
18418       ('heading', block.heading)
18419       ('lheading', block.lheading)
18420       ('blockquote', block.blockquote)
18421       ('tag', '<' + block._tag)
18422       ('def', block.def)
18423       ();
18424     
18425     /**
18426      * Normal Block Grammar
18427      */
18428     
18429     block.normal = merge({}, block);
18430     
18431     /**
18432      * GFM Block Grammar
18433      */
18434     
18435     block.gfm = merge({}, block.normal, {
18436       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18437       paragraph: /^/,
18438       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18439     });
18440     
18441     block.gfm.paragraph = replace(block.paragraph)
18442       ('(?!', '(?!'
18443         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18444         + block.list.source.replace('\\1', '\\3') + '|')
18445       ();
18446     
18447     /**
18448      * GFM + Tables Block Grammar
18449      */
18450     
18451     block.tables = merge({}, block.gfm, {
18452       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18453       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18454     });
18455     
18456     /**
18457      * Block Lexer
18458      */
18459     
18460     var Lexer = function (options) {
18461       this.tokens = [];
18462       this.tokens.links = {};
18463       this.options = options || marked.defaults;
18464       this.rules = block.normal;
18465     
18466       if (this.options.gfm) {
18467         if (this.options.tables) {
18468           this.rules = block.tables;
18469         } else {
18470           this.rules = block.gfm;
18471         }
18472       }
18473     }
18474     
18475     /**
18476      * Expose Block Rules
18477      */
18478     
18479     Lexer.rules = block;
18480     
18481     /**
18482      * Static Lex Method
18483      */
18484     
18485     Lexer.lex = function(src, options) {
18486       var lexer = new Lexer(options);
18487       return lexer.lex(src);
18488     };
18489     
18490     /**
18491      * Preprocessing
18492      */
18493     
18494     Lexer.prototype.lex = function(src) {
18495       src = src
18496         .replace(/\r\n|\r/g, '\n')
18497         .replace(/\t/g, '    ')
18498         .replace(/\u00a0/g, ' ')
18499         .replace(/\u2424/g, '\n');
18500     
18501       return this.token(src, true);
18502     };
18503     
18504     /**
18505      * Lexing
18506      */
18507     
18508     Lexer.prototype.token = function(src, top, bq) {
18509       var src = src.replace(/^ +$/gm, '')
18510         , next
18511         , loose
18512         , cap
18513         , bull
18514         , b
18515         , item
18516         , space
18517         , i
18518         , l;
18519     
18520       while (src) {
18521         // newline
18522         if (cap = this.rules.newline.exec(src)) {
18523           src = src.substring(cap[0].length);
18524           if (cap[0].length > 1) {
18525             this.tokens.push({
18526               type: 'space'
18527             });
18528           }
18529         }
18530     
18531         // code
18532         if (cap = this.rules.code.exec(src)) {
18533           src = src.substring(cap[0].length);
18534           cap = cap[0].replace(/^ {4}/gm, '');
18535           this.tokens.push({
18536             type: 'code',
18537             text: !this.options.pedantic
18538               ? cap.replace(/\n+$/, '')
18539               : cap
18540           });
18541           continue;
18542         }
18543     
18544         // fences (gfm)
18545         if (cap = this.rules.fences.exec(src)) {
18546           src = src.substring(cap[0].length);
18547           this.tokens.push({
18548             type: 'code',
18549             lang: cap[2],
18550             text: cap[3] || ''
18551           });
18552           continue;
18553         }
18554     
18555         // heading
18556         if (cap = this.rules.heading.exec(src)) {
18557           src = src.substring(cap[0].length);
18558           this.tokens.push({
18559             type: 'heading',
18560             depth: cap[1].length,
18561             text: cap[2]
18562           });
18563           continue;
18564         }
18565     
18566         // table no leading pipe (gfm)
18567         if (top && (cap = this.rules.nptable.exec(src))) {
18568           src = src.substring(cap[0].length);
18569     
18570           item = {
18571             type: 'table',
18572             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18573             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18574             cells: cap[3].replace(/\n$/, '').split('\n')
18575           };
18576     
18577           for (i = 0; i < item.align.length; i++) {
18578             if (/^ *-+: *$/.test(item.align[i])) {
18579               item.align[i] = 'right';
18580             } else if (/^ *:-+: *$/.test(item.align[i])) {
18581               item.align[i] = 'center';
18582             } else if (/^ *:-+ *$/.test(item.align[i])) {
18583               item.align[i] = 'left';
18584             } else {
18585               item.align[i] = null;
18586             }
18587           }
18588     
18589           for (i = 0; i < item.cells.length; i++) {
18590             item.cells[i] = item.cells[i].split(/ *\| */);
18591           }
18592     
18593           this.tokens.push(item);
18594     
18595           continue;
18596         }
18597     
18598         // lheading
18599         if (cap = this.rules.lheading.exec(src)) {
18600           src = src.substring(cap[0].length);
18601           this.tokens.push({
18602             type: 'heading',
18603             depth: cap[2] === '=' ? 1 : 2,
18604             text: cap[1]
18605           });
18606           continue;
18607         }
18608     
18609         // hr
18610         if (cap = this.rules.hr.exec(src)) {
18611           src = src.substring(cap[0].length);
18612           this.tokens.push({
18613             type: 'hr'
18614           });
18615           continue;
18616         }
18617     
18618         // blockquote
18619         if (cap = this.rules.blockquote.exec(src)) {
18620           src = src.substring(cap[0].length);
18621     
18622           this.tokens.push({
18623             type: 'blockquote_start'
18624           });
18625     
18626           cap = cap[0].replace(/^ *> ?/gm, '');
18627     
18628           // Pass `top` to keep the current
18629           // "toplevel" state. This is exactly
18630           // how markdown.pl works.
18631           this.token(cap, top, true);
18632     
18633           this.tokens.push({
18634             type: 'blockquote_end'
18635           });
18636     
18637           continue;
18638         }
18639     
18640         // list
18641         if (cap = this.rules.list.exec(src)) {
18642           src = src.substring(cap[0].length);
18643           bull = cap[2];
18644     
18645           this.tokens.push({
18646             type: 'list_start',
18647             ordered: bull.length > 1
18648           });
18649     
18650           // Get each top-level item.
18651           cap = cap[0].match(this.rules.item);
18652     
18653           next = false;
18654           l = cap.length;
18655           i = 0;
18656     
18657           for (; i < l; i++) {
18658             item = cap[i];
18659     
18660             // Remove the list item's bullet
18661             // so it is seen as the next token.
18662             space = item.length;
18663             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18664     
18665             // Outdent whatever the
18666             // list item contains. Hacky.
18667             if (~item.indexOf('\n ')) {
18668               space -= item.length;
18669               item = !this.options.pedantic
18670                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18671                 : item.replace(/^ {1,4}/gm, '');
18672             }
18673     
18674             // Determine whether the next list item belongs here.
18675             // Backpedal if it does not belong in this list.
18676             if (this.options.smartLists && i !== l - 1) {
18677               b = block.bullet.exec(cap[i + 1])[0];
18678               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18679                 src = cap.slice(i + 1).join('\n') + src;
18680                 i = l - 1;
18681               }
18682             }
18683     
18684             // Determine whether item is loose or not.
18685             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18686             // for discount behavior.
18687             loose = next || /\n\n(?!\s*$)/.test(item);
18688             if (i !== l - 1) {
18689               next = item.charAt(item.length - 1) === '\n';
18690               if (!loose) { loose = next; }
18691             }
18692     
18693             this.tokens.push({
18694               type: loose
18695                 ? 'loose_item_start'
18696                 : 'list_item_start'
18697             });
18698     
18699             // Recurse.
18700             this.token(item, false, bq);
18701     
18702             this.tokens.push({
18703               type: 'list_item_end'
18704             });
18705           }
18706     
18707           this.tokens.push({
18708             type: 'list_end'
18709           });
18710     
18711           continue;
18712         }
18713     
18714         // html
18715         if (cap = this.rules.html.exec(src)) {
18716           src = src.substring(cap[0].length);
18717           this.tokens.push({
18718             type: this.options.sanitize
18719               ? 'paragraph'
18720               : 'html',
18721             pre: !this.options.sanitizer
18722               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18723             text: cap[0]
18724           });
18725           continue;
18726         }
18727     
18728         // def
18729         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18730           src = src.substring(cap[0].length);
18731           this.tokens.links[cap[1].toLowerCase()] = {
18732             href: cap[2],
18733             title: cap[3]
18734           };
18735           continue;
18736         }
18737     
18738         // table (gfm)
18739         if (top && (cap = this.rules.table.exec(src))) {
18740           src = src.substring(cap[0].length);
18741     
18742           item = {
18743             type: 'table',
18744             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18745             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18746             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18747           };
18748     
18749           for (i = 0; i < item.align.length; i++) {
18750             if (/^ *-+: *$/.test(item.align[i])) {
18751               item.align[i] = 'right';
18752             } else if (/^ *:-+: *$/.test(item.align[i])) {
18753               item.align[i] = 'center';
18754             } else if (/^ *:-+ *$/.test(item.align[i])) {
18755               item.align[i] = 'left';
18756             } else {
18757               item.align[i] = null;
18758             }
18759           }
18760     
18761           for (i = 0; i < item.cells.length; i++) {
18762             item.cells[i] = item.cells[i]
18763               .replace(/^ *\| *| *\| *$/g, '')
18764               .split(/ *\| */);
18765           }
18766     
18767           this.tokens.push(item);
18768     
18769           continue;
18770         }
18771     
18772         // top-level paragraph
18773         if (top && (cap = this.rules.paragraph.exec(src))) {
18774           src = src.substring(cap[0].length);
18775           this.tokens.push({
18776             type: 'paragraph',
18777             text: cap[1].charAt(cap[1].length - 1) === '\n'
18778               ? cap[1].slice(0, -1)
18779               : cap[1]
18780           });
18781           continue;
18782         }
18783     
18784         // text
18785         if (cap = this.rules.text.exec(src)) {
18786           // Top-level should never reach here.
18787           src = src.substring(cap[0].length);
18788           this.tokens.push({
18789             type: 'text',
18790             text: cap[0]
18791           });
18792           continue;
18793         }
18794     
18795         if (src) {
18796           throw new
18797             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18798         }
18799       }
18800     
18801       return this.tokens;
18802     };
18803     
18804     /**
18805      * Inline-Level Grammar
18806      */
18807     
18808     var inline = {
18809       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18810       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18811       url: noop,
18812       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18813       link: /^!?\[(inside)\]\(href\)/,
18814       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18815       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18816       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18817       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18818       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18819       br: /^ {2,}\n(?!\s*$)/,
18820       del: noop,
18821       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18822     };
18823     
18824     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18825     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18826     
18827     inline.link = replace(inline.link)
18828       ('inside', inline._inside)
18829       ('href', inline._href)
18830       ();
18831     
18832     inline.reflink = replace(inline.reflink)
18833       ('inside', inline._inside)
18834       ();
18835     
18836     /**
18837      * Normal Inline Grammar
18838      */
18839     
18840     inline.normal = merge({}, inline);
18841     
18842     /**
18843      * Pedantic Inline Grammar
18844      */
18845     
18846     inline.pedantic = merge({}, inline.normal, {
18847       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18848       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18849     });
18850     
18851     /**
18852      * GFM Inline Grammar
18853      */
18854     
18855     inline.gfm = merge({}, inline.normal, {
18856       escape: replace(inline.escape)('])', '~|])')(),
18857       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18858       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18859       text: replace(inline.text)
18860         (']|', '~]|')
18861         ('|', '|https?://|')
18862         ()
18863     });
18864     
18865     /**
18866      * GFM + Line Breaks Inline Grammar
18867      */
18868     
18869     inline.breaks = merge({}, inline.gfm, {
18870       br: replace(inline.br)('{2,}', '*')(),
18871       text: replace(inline.gfm.text)('{2,}', '*')()
18872     });
18873     
18874     /**
18875      * Inline Lexer & Compiler
18876      */
18877     
18878     var InlineLexer  = function (links, options) {
18879       this.options = options || marked.defaults;
18880       this.links = links;
18881       this.rules = inline.normal;
18882       this.renderer = this.options.renderer || new Renderer;
18883       this.renderer.options = this.options;
18884     
18885       if (!this.links) {
18886         throw new
18887           Error('Tokens array requires a `links` property.');
18888       }
18889     
18890       if (this.options.gfm) {
18891         if (this.options.breaks) {
18892           this.rules = inline.breaks;
18893         } else {
18894           this.rules = inline.gfm;
18895         }
18896       } else if (this.options.pedantic) {
18897         this.rules = inline.pedantic;
18898       }
18899     }
18900     
18901     /**
18902      * Expose Inline Rules
18903      */
18904     
18905     InlineLexer.rules = inline;
18906     
18907     /**
18908      * Static Lexing/Compiling Method
18909      */
18910     
18911     InlineLexer.output = function(src, links, options) {
18912       var inline = new InlineLexer(links, options);
18913       return inline.output(src);
18914     };
18915     
18916     /**
18917      * Lexing/Compiling
18918      */
18919     
18920     InlineLexer.prototype.output = function(src) {
18921       var out = ''
18922         , link
18923         , text
18924         , href
18925         , cap;
18926     
18927       while (src) {
18928         // escape
18929         if (cap = this.rules.escape.exec(src)) {
18930           src = src.substring(cap[0].length);
18931           out += cap[1];
18932           continue;
18933         }
18934     
18935         // autolink
18936         if (cap = this.rules.autolink.exec(src)) {
18937           src = src.substring(cap[0].length);
18938           if (cap[2] === '@') {
18939             text = cap[1].charAt(6) === ':'
18940               ? this.mangle(cap[1].substring(7))
18941               : this.mangle(cap[1]);
18942             href = this.mangle('mailto:') + text;
18943           } else {
18944             text = escape(cap[1]);
18945             href = text;
18946           }
18947           out += this.renderer.link(href, null, text);
18948           continue;
18949         }
18950     
18951         // url (gfm)
18952         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18953           src = src.substring(cap[0].length);
18954           text = escape(cap[1]);
18955           href = text;
18956           out += this.renderer.link(href, null, text);
18957           continue;
18958         }
18959     
18960         // tag
18961         if (cap = this.rules.tag.exec(src)) {
18962           if (!this.inLink && /^<a /i.test(cap[0])) {
18963             this.inLink = true;
18964           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18965             this.inLink = false;
18966           }
18967           src = src.substring(cap[0].length);
18968           out += this.options.sanitize
18969             ? this.options.sanitizer
18970               ? this.options.sanitizer(cap[0])
18971               : escape(cap[0])
18972             : cap[0];
18973           continue;
18974         }
18975     
18976         // link
18977         if (cap = this.rules.link.exec(src)) {
18978           src = src.substring(cap[0].length);
18979           this.inLink = true;
18980           out += this.outputLink(cap, {
18981             href: cap[2],
18982             title: cap[3]
18983           });
18984           this.inLink = false;
18985           continue;
18986         }
18987     
18988         // reflink, nolink
18989         if ((cap = this.rules.reflink.exec(src))
18990             || (cap = this.rules.nolink.exec(src))) {
18991           src = src.substring(cap[0].length);
18992           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18993           link = this.links[link.toLowerCase()];
18994           if (!link || !link.href) {
18995             out += cap[0].charAt(0);
18996             src = cap[0].substring(1) + src;
18997             continue;
18998           }
18999           this.inLink = true;
19000           out += this.outputLink(cap, link);
19001           this.inLink = false;
19002           continue;
19003         }
19004     
19005         // strong
19006         if (cap = this.rules.strong.exec(src)) {
19007           src = src.substring(cap[0].length);
19008           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19009           continue;
19010         }
19011     
19012         // em
19013         if (cap = this.rules.em.exec(src)) {
19014           src = src.substring(cap[0].length);
19015           out += this.renderer.em(this.output(cap[2] || cap[1]));
19016           continue;
19017         }
19018     
19019         // code
19020         if (cap = this.rules.code.exec(src)) {
19021           src = src.substring(cap[0].length);
19022           out += this.renderer.codespan(escape(cap[2], true));
19023           continue;
19024         }
19025     
19026         // br
19027         if (cap = this.rules.br.exec(src)) {
19028           src = src.substring(cap[0].length);
19029           out += this.renderer.br();
19030           continue;
19031         }
19032     
19033         // del (gfm)
19034         if (cap = this.rules.del.exec(src)) {
19035           src = src.substring(cap[0].length);
19036           out += this.renderer.del(this.output(cap[1]));
19037           continue;
19038         }
19039     
19040         // text
19041         if (cap = this.rules.text.exec(src)) {
19042           src = src.substring(cap[0].length);
19043           out += this.renderer.text(escape(this.smartypants(cap[0])));
19044           continue;
19045         }
19046     
19047         if (src) {
19048           throw new
19049             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19050         }
19051       }
19052     
19053       return out;
19054     };
19055     
19056     /**
19057      * Compile Link
19058      */
19059     
19060     InlineLexer.prototype.outputLink = function(cap, link) {
19061       var href = escape(link.href)
19062         , title = link.title ? escape(link.title) : null;
19063     
19064       return cap[0].charAt(0) !== '!'
19065         ? this.renderer.link(href, title, this.output(cap[1]))
19066         : this.renderer.image(href, title, escape(cap[1]));
19067     };
19068     
19069     /**
19070      * Smartypants Transformations
19071      */
19072     
19073     InlineLexer.prototype.smartypants = function(text) {
19074       if (!this.options.smartypants)  { return text; }
19075       return text
19076         // em-dashes
19077         .replace(/---/g, '\u2014')
19078         // en-dashes
19079         .replace(/--/g, '\u2013')
19080         // opening singles
19081         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19082         // closing singles & apostrophes
19083         .replace(/'/g, '\u2019')
19084         // opening doubles
19085         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19086         // closing doubles
19087         .replace(/"/g, '\u201d')
19088         // ellipses
19089         .replace(/\.{3}/g, '\u2026');
19090     };
19091     
19092     /**
19093      * Mangle Links
19094      */
19095     
19096     InlineLexer.prototype.mangle = function(text) {
19097       if (!this.options.mangle) { return text; }
19098       var out = ''
19099         , l = text.length
19100         , i = 0
19101         , ch;
19102     
19103       for (; i < l; i++) {
19104         ch = text.charCodeAt(i);
19105         if (Math.random() > 0.5) {
19106           ch = 'x' + ch.toString(16);
19107         }
19108         out += '&#' + ch + ';';
19109       }
19110     
19111       return out;
19112     };
19113     
19114     /**
19115      * Renderer
19116      */
19117     
19118      /**
19119          * eval:var:Renderer
19120     */
19121     
19122     var Renderer   = function (options) {
19123       this.options = options || {};
19124     }
19125     
19126     Renderer.prototype.code = function(code, lang, escaped) {
19127       if (this.options.highlight) {
19128         var out = this.options.highlight(code, lang);
19129         if (out != null && out !== code) {
19130           escaped = true;
19131           code = out;
19132         }
19133       } else {
19134             // hack!!! - it's already escapeD?
19135             escaped = true;
19136       }
19137     
19138       if (!lang) {
19139         return '<pre><code>'
19140           + (escaped ? code : escape(code, true))
19141           + '\n</code></pre>';
19142       }
19143     
19144       return '<pre><code class="'
19145         + this.options.langPrefix
19146         + escape(lang, true)
19147         + '">'
19148         + (escaped ? code : escape(code, true))
19149         + '\n</code></pre>\n';
19150     };
19151     
19152     Renderer.prototype.blockquote = function(quote) {
19153       return '<blockquote>\n' + quote + '</blockquote>\n';
19154     };
19155     
19156     Renderer.prototype.html = function(html) {
19157       return html;
19158     };
19159     
19160     Renderer.prototype.heading = function(text, level, raw) {
19161       return '<h'
19162         + level
19163         + ' id="'
19164         + this.options.headerPrefix
19165         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19166         + '">'
19167         + text
19168         + '</h'
19169         + level
19170         + '>\n';
19171     };
19172     
19173     Renderer.prototype.hr = function() {
19174       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19175     };
19176     
19177     Renderer.prototype.list = function(body, ordered) {
19178       var type = ordered ? 'ol' : 'ul';
19179       return '<' + type + '>\n' + body + '</' + type + '>\n';
19180     };
19181     
19182     Renderer.prototype.listitem = function(text) {
19183       return '<li>' + text + '</li>\n';
19184     };
19185     
19186     Renderer.prototype.paragraph = function(text) {
19187       return '<p>' + text + '</p>\n';
19188     };
19189     
19190     Renderer.prototype.table = function(header, body) {
19191       return '<table class="table table-striped">\n'
19192         + '<thead>\n'
19193         + header
19194         + '</thead>\n'
19195         + '<tbody>\n'
19196         + body
19197         + '</tbody>\n'
19198         + '</table>\n';
19199     };
19200     
19201     Renderer.prototype.tablerow = function(content) {
19202       return '<tr>\n' + content + '</tr>\n';
19203     };
19204     
19205     Renderer.prototype.tablecell = function(content, flags) {
19206       var type = flags.header ? 'th' : 'td';
19207       var tag = flags.align
19208         ? '<' + type + ' style="text-align:' + flags.align + '">'
19209         : '<' + type + '>';
19210       return tag + content + '</' + type + '>\n';
19211     };
19212     
19213     // span level renderer
19214     Renderer.prototype.strong = function(text) {
19215       return '<strong>' + text + '</strong>';
19216     };
19217     
19218     Renderer.prototype.em = function(text) {
19219       return '<em>' + text + '</em>';
19220     };
19221     
19222     Renderer.prototype.codespan = function(text) {
19223       return '<code>' + text + '</code>';
19224     };
19225     
19226     Renderer.prototype.br = function() {
19227       return this.options.xhtml ? '<br/>' : '<br>';
19228     };
19229     
19230     Renderer.prototype.del = function(text) {
19231       return '<del>' + text + '</del>';
19232     };
19233     
19234     Renderer.prototype.link = function(href, title, text) {
19235       if (this.options.sanitize) {
19236         try {
19237           var prot = decodeURIComponent(unescape(href))
19238             .replace(/[^\w:]/g, '')
19239             .toLowerCase();
19240         } catch (e) {
19241           return '';
19242         }
19243         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19244           return '';
19245         }
19246       }
19247       var out = '<a href="' + href + '"';
19248       if (title) {
19249         out += ' title="' + title + '"';
19250       }
19251       out += '>' + text + '</a>';
19252       return out;
19253     };
19254     
19255     Renderer.prototype.image = function(href, title, text) {
19256       var out = '<img src="' + href + '" alt="' + text + '"';
19257       if (title) {
19258         out += ' title="' + title + '"';
19259       }
19260       out += this.options.xhtml ? '/>' : '>';
19261       return out;
19262     };
19263     
19264     Renderer.prototype.text = function(text) {
19265       return text;
19266     };
19267     
19268     /**
19269      * Parsing & Compiling
19270      */
19271          /**
19272          * eval:var:Parser
19273     */
19274     
19275     var Parser= function (options) {
19276       this.tokens = [];
19277       this.token = null;
19278       this.options = options || marked.defaults;
19279       this.options.renderer = this.options.renderer || new Renderer;
19280       this.renderer = this.options.renderer;
19281       this.renderer.options = this.options;
19282     }
19283     
19284     /**
19285      * Static Parse Method
19286      */
19287     
19288     Parser.parse = function(src, options, renderer) {
19289       var parser = new Parser(options, renderer);
19290       return parser.parse(src);
19291     };
19292     
19293     /**
19294      * Parse Loop
19295      */
19296     
19297     Parser.prototype.parse = function(src) {
19298       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19299       this.tokens = src.reverse();
19300     
19301       var out = '';
19302       while (this.next()) {
19303         out += this.tok();
19304       }
19305     
19306       return out;
19307     };
19308     
19309     /**
19310      * Next Token
19311      */
19312     
19313     Parser.prototype.next = function() {
19314       return this.token = this.tokens.pop();
19315     };
19316     
19317     /**
19318      * Preview Next Token
19319      */
19320     
19321     Parser.prototype.peek = function() {
19322       return this.tokens[this.tokens.length - 1] || 0;
19323     };
19324     
19325     /**
19326      * Parse Text Tokens
19327      */
19328     
19329     Parser.prototype.parseText = function() {
19330       var body = this.token.text;
19331     
19332       while (this.peek().type === 'text') {
19333         body += '\n' + this.next().text;
19334       }
19335     
19336       return this.inline.output(body);
19337     };
19338     
19339     /**
19340      * Parse Current Token
19341      */
19342     
19343     Parser.prototype.tok = function() {
19344       switch (this.token.type) {
19345         case 'space': {
19346           return '';
19347         }
19348         case 'hr': {
19349           return this.renderer.hr();
19350         }
19351         case 'heading': {
19352           return this.renderer.heading(
19353             this.inline.output(this.token.text),
19354             this.token.depth,
19355             this.token.text);
19356         }
19357         case 'code': {
19358           return this.renderer.code(this.token.text,
19359             this.token.lang,
19360             this.token.escaped);
19361         }
19362         case 'table': {
19363           var header = ''
19364             , body = ''
19365             , i
19366             , row
19367             , cell
19368             , flags
19369             , j;
19370     
19371           // header
19372           cell = '';
19373           for (i = 0; i < this.token.header.length; i++) {
19374             flags = { header: true, align: this.token.align[i] };
19375             cell += this.renderer.tablecell(
19376               this.inline.output(this.token.header[i]),
19377               { header: true, align: this.token.align[i] }
19378             );
19379           }
19380           header += this.renderer.tablerow(cell);
19381     
19382           for (i = 0; i < this.token.cells.length; i++) {
19383             row = this.token.cells[i];
19384     
19385             cell = '';
19386             for (j = 0; j < row.length; j++) {
19387               cell += this.renderer.tablecell(
19388                 this.inline.output(row[j]),
19389                 { header: false, align: this.token.align[j] }
19390               );
19391             }
19392     
19393             body += this.renderer.tablerow(cell);
19394           }
19395           return this.renderer.table(header, body);
19396         }
19397         case 'blockquote_start': {
19398           var body = '';
19399     
19400           while (this.next().type !== 'blockquote_end') {
19401             body += this.tok();
19402           }
19403     
19404           return this.renderer.blockquote(body);
19405         }
19406         case 'list_start': {
19407           var body = ''
19408             , ordered = this.token.ordered;
19409     
19410           while (this.next().type !== 'list_end') {
19411             body += this.tok();
19412           }
19413     
19414           return this.renderer.list(body, ordered);
19415         }
19416         case 'list_item_start': {
19417           var body = '';
19418     
19419           while (this.next().type !== 'list_item_end') {
19420             body += this.token.type === 'text'
19421               ? this.parseText()
19422               : this.tok();
19423           }
19424     
19425           return this.renderer.listitem(body);
19426         }
19427         case 'loose_item_start': {
19428           var body = '';
19429     
19430           while (this.next().type !== 'list_item_end') {
19431             body += this.tok();
19432           }
19433     
19434           return this.renderer.listitem(body);
19435         }
19436         case 'html': {
19437           var html = !this.token.pre && !this.options.pedantic
19438             ? this.inline.output(this.token.text)
19439             : this.token.text;
19440           return this.renderer.html(html);
19441         }
19442         case 'paragraph': {
19443           return this.renderer.paragraph(this.inline.output(this.token.text));
19444         }
19445         case 'text': {
19446           return this.renderer.paragraph(this.parseText());
19447         }
19448       }
19449     };
19450   
19451     
19452     /**
19453      * Marked
19454      */
19455          /**
19456          * eval:var:marked
19457     */
19458     var marked = function (src, opt, callback) {
19459       if (callback || typeof opt === 'function') {
19460         if (!callback) {
19461           callback = opt;
19462           opt = null;
19463         }
19464     
19465         opt = merge({}, marked.defaults, opt || {});
19466     
19467         var highlight = opt.highlight
19468           , tokens
19469           , pending
19470           , i = 0;
19471     
19472         try {
19473           tokens = Lexer.lex(src, opt)
19474         } catch (e) {
19475           return callback(e);
19476         }
19477     
19478         pending = tokens.length;
19479          /**
19480          * eval:var:done
19481     */
19482         var done = function(err) {
19483           if (err) {
19484             opt.highlight = highlight;
19485             return callback(err);
19486           }
19487     
19488           var out;
19489     
19490           try {
19491             out = Parser.parse(tokens, opt);
19492           } catch (e) {
19493             err = e;
19494           }
19495     
19496           opt.highlight = highlight;
19497     
19498           return err
19499             ? callback(err)
19500             : callback(null, out);
19501         };
19502     
19503         if (!highlight || highlight.length < 3) {
19504           return done();
19505         }
19506     
19507         delete opt.highlight;
19508     
19509         if (!pending) { return done(); }
19510     
19511         for (; i < tokens.length; i++) {
19512           (function(token) {
19513             if (token.type !== 'code') {
19514               return --pending || done();
19515             }
19516             return highlight(token.text, token.lang, function(err, code) {
19517               if (err) { return done(err); }
19518               if (code == null || code === token.text) {
19519                 return --pending || done();
19520               }
19521               token.text = code;
19522               token.escaped = true;
19523               --pending || done();
19524             });
19525           })(tokens[i]);
19526         }
19527     
19528         return;
19529       }
19530       try {
19531         if (opt) { opt = merge({}, marked.defaults, opt); }
19532         return Parser.parse(Lexer.lex(src, opt), opt);
19533       } catch (e) {
19534         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19535         if ((opt || marked.defaults).silent) {
19536           return '<p>An error occured:</p><pre>'
19537             + escape(e.message + '', true)
19538             + '</pre>';
19539         }
19540         throw e;
19541       }
19542     }
19543     
19544     /**
19545      * Options
19546      */
19547     
19548     marked.options =
19549     marked.setOptions = function(opt) {
19550       merge(marked.defaults, opt);
19551       return marked;
19552     };
19553     
19554     marked.defaults = {
19555       gfm: true,
19556       tables: true,
19557       breaks: false,
19558       pedantic: false,
19559       sanitize: false,
19560       sanitizer: null,
19561       mangle: true,
19562       smartLists: false,
19563       silent: false,
19564       highlight: null,
19565       langPrefix: 'lang-',
19566       smartypants: false,
19567       headerPrefix: '',
19568       renderer: new Renderer,
19569       xhtml: false
19570     };
19571     
19572     /**
19573      * Expose
19574      */
19575     
19576     marked.Parser = Parser;
19577     marked.parser = Parser.parse;
19578     
19579     marked.Renderer = Renderer;
19580     
19581     marked.Lexer = Lexer;
19582     marked.lexer = Lexer.lex;
19583     
19584     marked.InlineLexer = InlineLexer;
19585     marked.inlineLexer = InlineLexer.output;
19586     
19587     marked.parse = marked;
19588     
19589     Roo.Markdown.marked = marked;
19590
19591 })();/*
19592  * Based on:
19593  * Ext JS Library 1.1.1
19594  * Copyright(c) 2006-2007, Ext JS, LLC.
19595  *
19596  * Originally Released Under LGPL - original licence link has changed is not relivant.
19597  *
19598  * Fork - LGPL
19599  * <script type="text/javascript">
19600  */
19601
19602
19603
19604 /*
19605  * These classes are derivatives of the similarly named classes in the YUI Library.
19606  * The original license:
19607  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19608  * Code licensed under the BSD License:
19609  * http://developer.yahoo.net/yui/license.txt
19610  */
19611
19612 (function() {
19613
19614 var Event=Roo.EventManager;
19615 var Dom=Roo.lib.Dom;
19616
19617 /**
19618  * @class Roo.dd.DragDrop
19619  * @extends Roo.util.Observable
19620  * Defines the interface and base operation of items that that can be
19621  * dragged or can be drop targets.  It was designed to be extended, overriding
19622  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19623  * Up to three html elements can be associated with a DragDrop instance:
19624  * <ul>
19625  * <li>linked element: the element that is passed into the constructor.
19626  * This is the element which defines the boundaries for interaction with
19627  * other DragDrop objects.</li>
19628  * <li>handle element(s): The drag operation only occurs if the element that
19629  * was clicked matches a handle element.  By default this is the linked
19630  * element, but there are times that you will want only a portion of the
19631  * linked element to initiate the drag operation, and the setHandleElId()
19632  * method provides a way to define this.</li>
19633  * <li>drag element: this represents the element that would be moved along
19634  * with the cursor during a drag operation.  By default, this is the linked
19635  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19636  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19637  * </li>
19638  * </ul>
19639  * This class should not be instantiated until the onload event to ensure that
19640  * the associated elements are available.
19641  * The following would define a DragDrop obj that would interact with any
19642  * other DragDrop obj in the "group1" group:
19643  * <pre>
19644  *  dd = new Roo.dd.DragDrop("div1", "group1");
19645  * </pre>
19646  * Since none of the event handlers have been implemented, nothing would
19647  * actually happen if you were to run the code above.  Normally you would
19648  * override this class or one of the default implementations, but you can
19649  * also override the methods you want on an instance of the class...
19650  * <pre>
19651  *  dd.onDragDrop = function(e, id) {
19652  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19653  *  }
19654  * </pre>
19655  * @constructor
19656  * @param {String} id of the element that is linked to this instance
19657  * @param {String} sGroup the group of related DragDrop objects
19658  * @param {object} config an object containing configurable attributes
19659  *                Valid properties for DragDrop:
19660  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19661  */
19662 Roo.dd.DragDrop = function(id, sGroup, config) {
19663     if (id) {
19664         this.init(id, sGroup, config);
19665     }
19666     
19667 };
19668
19669 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19670
19671     /**
19672      * The id of the element associated with this object.  This is what we
19673      * refer to as the "linked element" because the size and position of
19674      * this element is used to determine when the drag and drop objects have
19675      * interacted.
19676      * @property id
19677      * @type String
19678      */
19679     id: null,
19680
19681     /**
19682      * Configuration attributes passed into the constructor
19683      * @property config
19684      * @type object
19685      */
19686     config: null,
19687
19688     /**
19689      * The id of the element that will be dragged.  By default this is same
19690      * as the linked element , but could be changed to another element. Ex:
19691      * Roo.dd.DDProxy
19692      * @property dragElId
19693      * @type String
19694      * @private
19695      */
19696     dragElId: null,
19697
19698     /**
19699      * the id of the element that initiates the drag operation.  By default
19700      * this is the linked element, but could be changed to be a child of this
19701      * element.  This lets us do things like only starting the drag when the
19702      * header element within the linked html element is clicked.
19703      * @property handleElId
19704      * @type String
19705      * @private
19706      */
19707     handleElId: null,
19708
19709     /**
19710      * An associative array of HTML tags that will be ignored if clicked.
19711      * @property invalidHandleTypes
19712      * @type {string: string}
19713      */
19714     invalidHandleTypes: null,
19715
19716     /**
19717      * An associative array of ids for elements that will be ignored if clicked
19718      * @property invalidHandleIds
19719      * @type {string: string}
19720      */
19721     invalidHandleIds: null,
19722
19723     /**
19724      * An indexted array of css class names for elements that will be ignored
19725      * if clicked.
19726      * @property invalidHandleClasses
19727      * @type string[]
19728      */
19729     invalidHandleClasses: null,
19730
19731     /**
19732      * The linked element's absolute X position at the time the drag was
19733      * started
19734      * @property startPageX
19735      * @type int
19736      * @private
19737      */
19738     startPageX: 0,
19739
19740     /**
19741      * The linked element's absolute X position at the time the drag was
19742      * started
19743      * @property startPageY
19744      * @type int
19745      * @private
19746      */
19747     startPageY: 0,
19748
19749     /**
19750      * The group defines a logical collection of DragDrop objects that are
19751      * related.  Instances only get events when interacting with other
19752      * DragDrop object in the same group.  This lets us define multiple
19753      * groups using a single DragDrop subclass if we want.
19754      * @property groups
19755      * @type {string: string}
19756      */
19757     groups: null,
19758
19759     /**
19760      * Individual drag/drop instances can be locked.  This will prevent
19761      * onmousedown start drag.
19762      * @property locked
19763      * @type boolean
19764      * @private
19765      */
19766     locked: false,
19767
19768     /**
19769      * Lock this instance
19770      * @method lock
19771      */
19772     lock: function() { this.locked = true; },
19773
19774     /**
19775      * Unlock this instace
19776      * @method unlock
19777      */
19778     unlock: function() { this.locked = false; },
19779
19780     /**
19781      * By default, all insances can be a drop target.  This can be disabled by
19782      * setting isTarget to false.
19783      * @method isTarget
19784      * @type boolean
19785      */
19786     isTarget: true,
19787
19788     /**
19789      * The padding configured for this drag and drop object for calculating
19790      * the drop zone intersection with this object.
19791      * @method padding
19792      * @type int[]
19793      */
19794     padding: null,
19795
19796     /**
19797      * Cached reference to the linked element
19798      * @property _domRef
19799      * @private
19800      */
19801     _domRef: null,
19802
19803     /**
19804      * Internal typeof flag
19805      * @property __ygDragDrop
19806      * @private
19807      */
19808     __ygDragDrop: true,
19809
19810     /**
19811      * Set to true when horizontal contraints are applied
19812      * @property constrainX
19813      * @type boolean
19814      * @private
19815      */
19816     constrainX: false,
19817
19818     /**
19819      * Set to true when vertical contraints are applied
19820      * @property constrainY
19821      * @type boolean
19822      * @private
19823      */
19824     constrainY: false,
19825
19826     /**
19827      * The left constraint
19828      * @property minX
19829      * @type int
19830      * @private
19831      */
19832     minX: 0,
19833
19834     /**
19835      * The right constraint
19836      * @property maxX
19837      * @type int
19838      * @private
19839      */
19840     maxX: 0,
19841
19842     /**
19843      * The up constraint
19844      * @property minY
19845      * @type int
19846      * @type int
19847      * @private
19848      */
19849     minY: 0,
19850
19851     /**
19852      * The down constraint
19853      * @property maxY
19854      * @type int
19855      * @private
19856      */
19857     maxY: 0,
19858
19859     /**
19860      * Maintain offsets when we resetconstraints.  Set to true when you want
19861      * the position of the element relative to its parent to stay the same
19862      * when the page changes
19863      *
19864      * @property maintainOffset
19865      * @type boolean
19866      */
19867     maintainOffset: false,
19868
19869     /**
19870      * Array of pixel locations the element will snap to if we specified a
19871      * horizontal graduation/interval.  This array is generated automatically
19872      * when you define a tick interval.
19873      * @property xTicks
19874      * @type int[]
19875      */
19876     xTicks: null,
19877
19878     /**
19879      * Array of pixel locations the element will snap to if we specified a
19880      * vertical graduation/interval.  This array is generated automatically
19881      * when you define a tick interval.
19882      * @property yTicks
19883      * @type int[]
19884      */
19885     yTicks: null,
19886
19887     /**
19888      * By default the drag and drop instance will only respond to the primary
19889      * button click (left button for a right-handed mouse).  Set to true to
19890      * allow drag and drop to start with any mouse click that is propogated
19891      * by the browser
19892      * @property primaryButtonOnly
19893      * @type boolean
19894      */
19895     primaryButtonOnly: true,
19896
19897     /**
19898      * The availabe property is false until the linked dom element is accessible.
19899      * @property available
19900      * @type boolean
19901      */
19902     available: false,
19903
19904     /**
19905      * By default, drags can only be initiated if the mousedown occurs in the
19906      * region the linked element is.  This is done in part to work around a
19907      * bug in some browsers that mis-report the mousedown if the previous
19908      * mouseup happened outside of the window.  This property is set to true
19909      * if outer handles are defined.
19910      *
19911      * @property hasOuterHandles
19912      * @type boolean
19913      * @default false
19914      */
19915     hasOuterHandles: false,
19916
19917     /**
19918      * Code that executes immediately before the startDrag event
19919      * @method b4StartDrag
19920      * @private
19921      */
19922     b4StartDrag: function(x, y) { },
19923
19924     /**
19925      * Abstract method called after a drag/drop object is clicked
19926      * and the drag or mousedown time thresholds have beeen met.
19927      * @method startDrag
19928      * @param {int} X click location
19929      * @param {int} Y click location
19930      */
19931     startDrag: function(x, y) { /* override this */ },
19932
19933     /**
19934      * Code that executes immediately before the onDrag event
19935      * @method b4Drag
19936      * @private
19937      */
19938     b4Drag: function(e) { },
19939
19940     /**
19941      * Abstract method called during the onMouseMove event while dragging an
19942      * object.
19943      * @method onDrag
19944      * @param {Event} e the mousemove event
19945      */
19946     onDrag: function(e) { /* override this */ },
19947
19948     /**
19949      * Abstract method called when this element fist begins hovering over
19950      * another DragDrop obj
19951      * @method onDragEnter
19952      * @param {Event} e the mousemove event
19953      * @param {String|DragDrop[]} id In POINT mode, the element
19954      * id this is hovering over.  In INTERSECT mode, an array of one or more
19955      * dragdrop items being hovered over.
19956      */
19957     onDragEnter: function(e, id) { /* override this */ },
19958
19959     /**
19960      * Code that executes immediately before the onDragOver event
19961      * @method b4DragOver
19962      * @private
19963      */
19964     b4DragOver: function(e) { },
19965
19966     /**
19967      * Abstract method called when this element is hovering over another
19968      * DragDrop obj
19969      * @method onDragOver
19970      * @param {Event} e the mousemove event
19971      * @param {String|DragDrop[]} id In POINT mode, the element
19972      * id this is hovering over.  In INTERSECT mode, an array of dd items
19973      * being hovered over.
19974      */
19975     onDragOver: function(e, id) { /* override this */ },
19976
19977     /**
19978      * Code that executes immediately before the onDragOut event
19979      * @method b4DragOut
19980      * @private
19981      */
19982     b4DragOut: function(e) { },
19983
19984     /**
19985      * Abstract method called when we are no longer hovering over an element
19986      * @method onDragOut
19987      * @param {Event} e the mousemove event
19988      * @param {String|DragDrop[]} id In POINT mode, the element
19989      * id this was hovering over.  In INTERSECT mode, an array of dd items
19990      * that the mouse is no longer over.
19991      */
19992     onDragOut: function(e, id) { /* override this */ },
19993
19994     /**
19995      * Code that executes immediately before the onDragDrop event
19996      * @method b4DragDrop
19997      * @private
19998      */
19999     b4DragDrop: function(e) { },
20000
20001     /**
20002      * Abstract method called when this item is dropped on another DragDrop
20003      * obj
20004      * @method onDragDrop
20005      * @param {Event} e the mouseup event
20006      * @param {String|DragDrop[]} id In POINT mode, the element
20007      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20008      * was dropped on.
20009      */
20010     onDragDrop: function(e, id) { /* override this */ },
20011
20012     /**
20013      * Abstract method called when this item is dropped on an area with no
20014      * drop target
20015      * @method onInvalidDrop
20016      * @param {Event} e the mouseup event
20017      */
20018     onInvalidDrop: function(e) { /* override this */ },
20019
20020     /**
20021      * Code that executes immediately before the endDrag event
20022      * @method b4EndDrag
20023      * @private
20024      */
20025     b4EndDrag: function(e) { },
20026
20027     /**
20028      * Fired when we are done dragging the object
20029      * @method endDrag
20030      * @param {Event} e the mouseup event
20031      */
20032     endDrag: function(e) { /* override this */ },
20033
20034     /**
20035      * Code executed immediately before the onMouseDown event
20036      * @method b4MouseDown
20037      * @param {Event} e the mousedown event
20038      * @private
20039      */
20040     b4MouseDown: function(e) {  },
20041
20042     /**
20043      * Event handler that fires when a drag/drop obj gets a mousedown
20044      * @method onMouseDown
20045      * @param {Event} e the mousedown event
20046      */
20047     onMouseDown: function(e) { /* override this */ },
20048
20049     /**
20050      * Event handler that fires when a drag/drop obj gets a mouseup
20051      * @method onMouseUp
20052      * @param {Event} e the mouseup event
20053      */
20054     onMouseUp: function(e) { /* override this */ },
20055
20056     /**
20057      * Override the onAvailable method to do what is needed after the initial
20058      * position was determined.
20059      * @method onAvailable
20060      */
20061     onAvailable: function () {
20062     },
20063
20064     /*
20065      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20066      * @type Object
20067      */
20068     defaultPadding : {left:0, right:0, top:0, bottom:0},
20069
20070     /*
20071      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20072  *
20073  * Usage:
20074  <pre><code>
20075  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20076                 { dragElId: "existingProxyDiv" });
20077  dd.startDrag = function(){
20078      this.constrainTo("parent-id");
20079  };
20080  </code></pre>
20081  * Or you can initalize it using the {@link Roo.Element} object:
20082  <pre><code>
20083  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20084      startDrag : function(){
20085          this.constrainTo("parent-id");
20086      }
20087  });
20088  </code></pre>
20089      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20090      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20091      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20092      * an object containing the sides to pad. For example: {right:10, bottom:10}
20093      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20094      */
20095     constrainTo : function(constrainTo, pad, inContent){
20096         if(typeof pad == "number"){
20097             pad = {left: pad, right:pad, top:pad, bottom:pad};
20098         }
20099         pad = pad || this.defaultPadding;
20100         var b = Roo.get(this.getEl()).getBox();
20101         var ce = Roo.get(constrainTo);
20102         var s = ce.getScroll();
20103         var c, cd = ce.dom;
20104         if(cd == document.body){
20105             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20106         }else{
20107             xy = ce.getXY();
20108             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20109         }
20110
20111
20112         var topSpace = b.y - c.y;
20113         var leftSpace = b.x - c.x;
20114
20115         this.resetConstraints();
20116         this.setXConstraint(leftSpace - (pad.left||0), // left
20117                 c.width - leftSpace - b.width - (pad.right||0) //right
20118         );
20119         this.setYConstraint(topSpace - (pad.top||0), //top
20120                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20121         );
20122     },
20123
20124     /**
20125      * Returns a reference to the linked element
20126      * @method getEl
20127      * @return {HTMLElement} the html element
20128      */
20129     getEl: function() {
20130         if (!this._domRef) {
20131             this._domRef = Roo.getDom(this.id);
20132         }
20133
20134         return this._domRef;
20135     },
20136
20137     /**
20138      * Returns a reference to the actual element to drag.  By default this is
20139      * the same as the html element, but it can be assigned to another
20140      * element. An example of this can be found in Roo.dd.DDProxy
20141      * @method getDragEl
20142      * @return {HTMLElement} the html element
20143      */
20144     getDragEl: function() {
20145         return Roo.getDom(this.dragElId);
20146     },
20147
20148     /**
20149      * Sets up the DragDrop object.  Must be called in the constructor of any
20150      * Roo.dd.DragDrop subclass
20151      * @method init
20152      * @param id the id of the linked element
20153      * @param {String} sGroup the group of related items
20154      * @param {object} config configuration attributes
20155      */
20156     init: function(id, sGroup, config) {
20157         this.initTarget(id, sGroup, config);
20158         if (!Roo.isTouch) {
20159             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20160         }
20161         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20162         // Event.on(this.id, "selectstart", Event.preventDefault);
20163     },
20164
20165     /**
20166      * Initializes Targeting functionality only... the object does not
20167      * get a mousedown handler.
20168      * @method initTarget
20169      * @param id the id of the linked element
20170      * @param {String} sGroup the group of related items
20171      * @param {object} config configuration attributes
20172      */
20173     initTarget: function(id, sGroup, config) {
20174
20175         // configuration attributes
20176         this.config = config || {};
20177
20178         // create a local reference to the drag and drop manager
20179         this.DDM = Roo.dd.DDM;
20180         // initialize the groups array
20181         this.groups = {};
20182
20183         // assume that we have an element reference instead of an id if the
20184         // parameter is not a string
20185         if (typeof id !== "string") {
20186             id = Roo.id(id);
20187         }
20188
20189         // set the id
20190         this.id = id;
20191
20192         // add to an interaction group
20193         this.addToGroup((sGroup) ? sGroup : "default");
20194
20195         // We don't want to register this as the handle with the manager
20196         // so we just set the id rather than calling the setter.
20197         this.handleElId = id;
20198
20199         // the linked element is the element that gets dragged by default
20200         this.setDragElId(id);
20201
20202         // by default, clicked anchors will not start drag operations.
20203         this.invalidHandleTypes = { A: "A" };
20204         this.invalidHandleIds = {};
20205         this.invalidHandleClasses = [];
20206
20207         this.applyConfig();
20208
20209         this.handleOnAvailable();
20210     },
20211
20212     /**
20213      * Applies the configuration parameters that were passed into the constructor.
20214      * This is supposed to happen at each level through the inheritance chain.  So
20215      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20216      * DragDrop in order to get all of the parameters that are available in
20217      * each object.
20218      * @method applyConfig
20219      */
20220     applyConfig: function() {
20221
20222         // configurable properties:
20223         //    padding, isTarget, maintainOffset, primaryButtonOnly
20224         this.padding           = this.config.padding || [0, 0, 0, 0];
20225         this.isTarget          = (this.config.isTarget !== false);
20226         this.maintainOffset    = (this.config.maintainOffset);
20227         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20228
20229     },
20230
20231     /**
20232      * Executed when the linked element is available
20233      * @method handleOnAvailable
20234      * @private
20235      */
20236     handleOnAvailable: function() {
20237         this.available = true;
20238         this.resetConstraints();
20239         this.onAvailable();
20240     },
20241
20242      /**
20243      * Configures the padding for the target zone in px.  Effectively expands
20244      * (or reduces) the virtual object size for targeting calculations.
20245      * Supports css-style shorthand; if only one parameter is passed, all sides
20246      * will have that padding, and if only two are passed, the top and bottom
20247      * will have the first param, the left and right the second.
20248      * @method setPadding
20249      * @param {int} iTop    Top pad
20250      * @param {int} iRight  Right pad
20251      * @param {int} iBot    Bot pad
20252      * @param {int} iLeft   Left pad
20253      */
20254     setPadding: function(iTop, iRight, iBot, iLeft) {
20255         // this.padding = [iLeft, iRight, iTop, iBot];
20256         if (!iRight && 0 !== iRight) {
20257             this.padding = [iTop, iTop, iTop, iTop];
20258         } else if (!iBot && 0 !== iBot) {
20259             this.padding = [iTop, iRight, iTop, iRight];
20260         } else {
20261             this.padding = [iTop, iRight, iBot, iLeft];
20262         }
20263     },
20264
20265     /**
20266      * Stores the initial placement of the linked element.
20267      * @method setInitialPosition
20268      * @param {int} diffX   the X offset, default 0
20269      * @param {int} diffY   the Y offset, default 0
20270      */
20271     setInitPosition: function(diffX, diffY) {
20272         var el = this.getEl();
20273
20274         if (!this.DDM.verifyEl(el)) {
20275             return;
20276         }
20277
20278         var dx = diffX || 0;
20279         var dy = diffY || 0;
20280
20281         var p = Dom.getXY( el );
20282
20283         this.initPageX = p[0] - dx;
20284         this.initPageY = p[1] - dy;
20285
20286         this.lastPageX = p[0];
20287         this.lastPageY = p[1];
20288
20289
20290         this.setStartPosition(p);
20291     },
20292
20293     /**
20294      * Sets the start position of the element.  This is set when the obj
20295      * is initialized, the reset when a drag is started.
20296      * @method setStartPosition
20297      * @param pos current position (from previous lookup)
20298      * @private
20299      */
20300     setStartPosition: function(pos) {
20301         var p = pos || Dom.getXY( this.getEl() );
20302         this.deltaSetXY = null;
20303
20304         this.startPageX = p[0];
20305         this.startPageY = p[1];
20306     },
20307
20308     /**
20309      * Add this instance to a group of related drag/drop objects.  All
20310      * instances belong to at least one group, and can belong to as many
20311      * groups as needed.
20312      * @method addToGroup
20313      * @param sGroup {string} the name of the group
20314      */
20315     addToGroup: function(sGroup) {
20316         this.groups[sGroup] = true;
20317         this.DDM.regDragDrop(this, sGroup);
20318     },
20319
20320     /**
20321      * Remove's this instance from the supplied interaction group
20322      * @method removeFromGroup
20323      * @param {string}  sGroup  The group to drop
20324      */
20325     removeFromGroup: function(sGroup) {
20326         if (this.groups[sGroup]) {
20327             delete this.groups[sGroup];
20328         }
20329
20330         this.DDM.removeDDFromGroup(this, sGroup);
20331     },
20332
20333     /**
20334      * Allows you to specify that an element other than the linked element
20335      * will be moved with the cursor during a drag
20336      * @method setDragElId
20337      * @param id {string} the id of the element that will be used to initiate the drag
20338      */
20339     setDragElId: function(id) {
20340         this.dragElId = id;
20341     },
20342
20343     /**
20344      * Allows you to specify a child of the linked element that should be
20345      * used to initiate the drag operation.  An example of this would be if
20346      * you have a content div with text and links.  Clicking anywhere in the
20347      * content area would normally start the drag operation.  Use this method
20348      * to specify that an element inside of the content div is the element
20349      * that starts the drag operation.
20350      * @method setHandleElId
20351      * @param id {string} the id of the element that will be used to
20352      * initiate the drag.
20353      */
20354     setHandleElId: function(id) {
20355         if (typeof id !== "string") {
20356             id = Roo.id(id);
20357         }
20358         this.handleElId = id;
20359         this.DDM.regHandle(this.id, id);
20360     },
20361
20362     /**
20363      * Allows you to set an element outside of the linked element as a drag
20364      * handle
20365      * @method setOuterHandleElId
20366      * @param id the id of the element that will be used to initiate the drag
20367      */
20368     setOuterHandleElId: function(id) {
20369         if (typeof id !== "string") {
20370             id = Roo.id(id);
20371         }
20372         Event.on(id, "mousedown",
20373                 this.handleMouseDown, this);
20374         this.setHandleElId(id);
20375
20376         this.hasOuterHandles = true;
20377     },
20378
20379     /**
20380      * Remove all drag and drop hooks for this element
20381      * @method unreg
20382      */
20383     unreg: function() {
20384         Event.un(this.id, "mousedown",
20385                 this.handleMouseDown);
20386         Event.un(this.id, "touchstart",
20387                 this.handleMouseDown);
20388         this._domRef = null;
20389         this.DDM._remove(this);
20390     },
20391
20392     destroy : function(){
20393         this.unreg();
20394     },
20395
20396     /**
20397      * Returns true if this instance is locked, or the drag drop mgr is locked
20398      * (meaning that all drag/drop is disabled on the page.)
20399      * @method isLocked
20400      * @return {boolean} true if this obj or all drag/drop is locked, else
20401      * false
20402      */
20403     isLocked: function() {
20404         return (this.DDM.isLocked() || this.locked);
20405     },
20406
20407     /**
20408      * Fired when this object is clicked
20409      * @method handleMouseDown
20410      * @param {Event} e
20411      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20412      * @private
20413      */
20414     handleMouseDown: function(e, oDD){
20415      
20416         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20417             //Roo.log('not touch/ button !=0');
20418             return;
20419         }
20420         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20421             return; // double touch..
20422         }
20423         
20424
20425         if (this.isLocked()) {
20426             //Roo.log('locked');
20427             return;
20428         }
20429
20430         this.DDM.refreshCache(this.groups);
20431 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20432         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20433         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20434             //Roo.log('no outer handes or not over target');
20435                 // do nothing.
20436         } else {
20437 //            Roo.log('check validator');
20438             if (this.clickValidator(e)) {
20439 //                Roo.log('validate success');
20440                 // set the initial element position
20441                 this.setStartPosition();
20442
20443
20444                 this.b4MouseDown(e);
20445                 this.onMouseDown(e);
20446
20447                 this.DDM.handleMouseDown(e, this);
20448
20449                 this.DDM.stopEvent(e);
20450             } else {
20451
20452
20453             }
20454         }
20455     },
20456
20457     clickValidator: function(e) {
20458         var target = e.getTarget();
20459         return ( this.isValidHandleChild(target) &&
20460                     (this.id == this.handleElId ||
20461                         this.DDM.handleWasClicked(target, this.id)) );
20462     },
20463
20464     /**
20465      * Allows you to specify a tag name that should not start a drag operation
20466      * when clicked.  This is designed to facilitate embedding links within a
20467      * drag handle that do something other than start the drag.
20468      * @method addInvalidHandleType
20469      * @param {string} tagName the type of element to exclude
20470      */
20471     addInvalidHandleType: function(tagName) {
20472         var type = tagName.toUpperCase();
20473         this.invalidHandleTypes[type] = type;
20474     },
20475
20476     /**
20477      * Lets you to specify an element id for a child of a drag handle
20478      * that should not initiate a drag
20479      * @method addInvalidHandleId
20480      * @param {string} id the element id of the element you wish to ignore
20481      */
20482     addInvalidHandleId: function(id) {
20483         if (typeof id !== "string") {
20484             id = Roo.id(id);
20485         }
20486         this.invalidHandleIds[id] = id;
20487     },
20488
20489     /**
20490      * Lets you specify a css class of elements that will not initiate a drag
20491      * @method addInvalidHandleClass
20492      * @param {string} cssClass the class of the elements you wish to ignore
20493      */
20494     addInvalidHandleClass: function(cssClass) {
20495         this.invalidHandleClasses.push(cssClass);
20496     },
20497
20498     /**
20499      * Unsets an excluded tag name set by addInvalidHandleType
20500      * @method removeInvalidHandleType
20501      * @param {string} tagName the type of element to unexclude
20502      */
20503     removeInvalidHandleType: function(tagName) {
20504         var type = tagName.toUpperCase();
20505         // this.invalidHandleTypes[type] = null;
20506         delete this.invalidHandleTypes[type];
20507     },
20508
20509     /**
20510      * Unsets an invalid handle id
20511      * @method removeInvalidHandleId
20512      * @param {string} id the id of the element to re-enable
20513      */
20514     removeInvalidHandleId: function(id) {
20515         if (typeof id !== "string") {
20516             id = Roo.id(id);
20517         }
20518         delete this.invalidHandleIds[id];
20519     },
20520
20521     /**
20522      * Unsets an invalid css class
20523      * @method removeInvalidHandleClass
20524      * @param {string} cssClass the class of the element(s) you wish to
20525      * re-enable
20526      */
20527     removeInvalidHandleClass: function(cssClass) {
20528         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20529             if (this.invalidHandleClasses[i] == cssClass) {
20530                 delete this.invalidHandleClasses[i];
20531             }
20532         }
20533     },
20534
20535     /**
20536      * Checks the tag exclusion list to see if this click should be ignored
20537      * @method isValidHandleChild
20538      * @param {HTMLElement} node the HTMLElement to evaluate
20539      * @return {boolean} true if this is a valid tag type, false if not
20540      */
20541     isValidHandleChild: function(node) {
20542
20543         var valid = true;
20544         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20545         var nodeName;
20546         try {
20547             nodeName = node.nodeName.toUpperCase();
20548         } catch(e) {
20549             nodeName = node.nodeName;
20550         }
20551         valid = valid && !this.invalidHandleTypes[nodeName];
20552         valid = valid && !this.invalidHandleIds[node.id];
20553
20554         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20555             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20556         }
20557
20558
20559         return valid;
20560
20561     },
20562
20563     /**
20564      * Create the array of horizontal tick marks if an interval was specified
20565      * in setXConstraint().
20566      * @method setXTicks
20567      * @private
20568      */
20569     setXTicks: function(iStartX, iTickSize) {
20570         this.xTicks = [];
20571         this.xTickSize = iTickSize;
20572
20573         var tickMap = {};
20574
20575         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20576             if (!tickMap[i]) {
20577                 this.xTicks[this.xTicks.length] = i;
20578                 tickMap[i] = true;
20579             }
20580         }
20581
20582         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20583             if (!tickMap[i]) {
20584                 this.xTicks[this.xTicks.length] = i;
20585                 tickMap[i] = true;
20586             }
20587         }
20588
20589         this.xTicks.sort(this.DDM.numericSort) ;
20590     },
20591
20592     /**
20593      * Create the array of vertical tick marks if an interval was specified in
20594      * setYConstraint().
20595      * @method setYTicks
20596      * @private
20597      */
20598     setYTicks: function(iStartY, iTickSize) {
20599         this.yTicks = [];
20600         this.yTickSize = iTickSize;
20601
20602         var tickMap = {};
20603
20604         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20605             if (!tickMap[i]) {
20606                 this.yTicks[this.yTicks.length] = i;
20607                 tickMap[i] = true;
20608             }
20609         }
20610
20611         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20612             if (!tickMap[i]) {
20613                 this.yTicks[this.yTicks.length] = i;
20614                 tickMap[i] = true;
20615             }
20616         }
20617
20618         this.yTicks.sort(this.DDM.numericSort) ;
20619     },
20620
20621     /**
20622      * By default, the element can be dragged any place on the screen.  Use
20623      * this method to limit the horizontal travel of the element.  Pass in
20624      * 0,0 for the parameters if you want to lock the drag to the y axis.
20625      * @method setXConstraint
20626      * @param {int} iLeft the number of pixels the element can move to the left
20627      * @param {int} iRight the number of pixels the element can move to the
20628      * right
20629      * @param {int} iTickSize optional parameter for specifying that the
20630      * element
20631      * should move iTickSize pixels at a time.
20632      */
20633     setXConstraint: function(iLeft, iRight, iTickSize) {
20634         this.leftConstraint = iLeft;
20635         this.rightConstraint = iRight;
20636
20637         this.minX = this.initPageX - iLeft;
20638         this.maxX = this.initPageX + iRight;
20639         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20640
20641         this.constrainX = true;
20642     },
20643
20644     /**
20645      * Clears any constraints applied to this instance.  Also clears ticks
20646      * since they can't exist independent of a constraint at this time.
20647      * @method clearConstraints
20648      */
20649     clearConstraints: function() {
20650         this.constrainX = false;
20651         this.constrainY = false;
20652         this.clearTicks();
20653     },
20654
20655     /**
20656      * Clears any tick interval defined for this instance
20657      * @method clearTicks
20658      */
20659     clearTicks: function() {
20660         this.xTicks = null;
20661         this.yTicks = null;
20662         this.xTickSize = 0;
20663         this.yTickSize = 0;
20664     },
20665
20666     /**
20667      * By default, the element can be dragged any place on the screen.  Set
20668      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20669      * parameters if you want to lock the drag to the x axis.
20670      * @method setYConstraint
20671      * @param {int} iUp the number of pixels the element can move up
20672      * @param {int} iDown the number of pixels the element can move down
20673      * @param {int} iTickSize optional parameter for specifying that the
20674      * element should move iTickSize pixels at a time.
20675      */
20676     setYConstraint: function(iUp, iDown, iTickSize) {
20677         this.topConstraint = iUp;
20678         this.bottomConstraint = iDown;
20679
20680         this.minY = this.initPageY - iUp;
20681         this.maxY = this.initPageY + iDown;
20682         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20683
20684         this.constrainY = true;
20685
20686     },
20687
20688     /**
20689      * resetConstraints must be called if you manually reposition a dd element.
20690      * @method resetConstraints
20691      * @param {boolean} maintainOffset
20692      */
20693     resetConstraints: function() {
20694
20695
20696         // Maintain offsets if necessary
20697         if (this.initPageX || this.initPageX === 0) {
20698             // figure out how much this thing has moved
20699             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20700             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20701
20702             this.setInitPosition(dx, dy);
20703
20704         // This is the first time we have detected the element's position
20705         } else {
20706             this.setInitPosition();
20707         }
20708
20709         if (this.constrainX) {
20710             this.setXConstraint( this.leftConstraint,
20711                                  this.rightConstraint,
20712                                  this.xTickSize        );
20713         }
20714
20715         if (this.constrainY) {
20716             this.setYConstraint( this.topConstraint,
20717                                  this.bottomConstraint,
20718                                  this.yTickSize         );
20719         }
20720     },
20721
20722     /**
20723      * Normally the drag element is moved pixel by pixel, but we can specify
20724      * that it move a number of pixels at a time.  This method resolves the
20725      * location when we have it set up like this.
20726      * @method getTick
20727      * @param {int} val where we want to place the object
20728      * @param {int[]} tickArray sorted array of valid points
20729      * @return {int} the closest tick
20730      * @private
20731      */
20732     getTick: function(val, tickArray) {
20733
20734         if (!tickArray) {
20735             // If tick interval is not defined, it is effectively 1 pixel,
20736             // so we return the value passed to us.
20737             return val;
20738         } else if (tickArray[0] >= val) {
20739             // The value is lower than the first tick, so we return the first
20740             // tick.
20741             return tickArray[0];
20742         } else {
20743             for (var i=0, len=tickArray.length; i<len; ++i) {
20744                 var next = i + 1;
20745                 if (tickArray[next] && tickArray[next] >= val) {
20746                     var diff1 = val - tickArray[i];
20747                     var diff2 = tickArray[next] - val;
20748                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20749                 }
20750             }
20751
20752             // The value is larger than the last tick, so we return the last
20753             // tick.
20754             return tickArray[tickArray.length - 1];
20755         }
20756     },
20757
20758     /**
20759      * toString method
20760      * @method toString
20761      * @return {string} string representation of the dd obj
20762      */
20763     toString: function() {
20764         return ("DragDrop " + this.id);
20765     }
20766
20767 });
20768
20769 })();
20770 /*
20771  * Based on:
20772  * Ext JS Library 1.1.1
20773  * Copyright(c) 2006-2007, Ext JS, LLC.
20774  *
20775  * Originally Released Under LGPL - original licence link has changed is not relivant.
20776  *
20777  * Fork - LGPL
20778  * <script type="text/javascript">
20779  */
20780
20781
20782 /**
20783  * The drag and drop utility provides a framework for building drag and drop
20784  * applications.  In addition to enabling drag and drop for specific elements,
20785  * the drag and drop elements are tracked by the manager class, and the
20786  * interactions between the various elements are tracked during the drag and
20787  * the implementing code is notified about these important moments.
20788  */
20789
20790 // Only load the library once.  Rewriting the manager class would orphan
20791 // existing drag and drop instances.
20792 if (!Roo.dd.DragDropMgr) {
20793
20794 /**
20795  * @class Roo.dd.DragDropMgr
20796  * DragDropMgr is a singleton that tracks the element interaction for
20797  * all DragDrop items in the window.  Generally, you will not call
20798  * this class directly, but it does have helper methods that could
20799  * be useful in your DragDrop implementations.
20800  * @static
20801  */
20802 Roo.dd.DragDropMgr = function() {
20803
20804     var Event = Roo.EventManager;
20805
20806     return {
20807
20808         /**
20809          * Two dimensional Array of registered DragDrop objects.  The first
20810          * dimension is the DragDrop item group, the second the DragDrop
20811          * object.
20812          * @property ids
20813          * @type {string: string}
20814          * @private
20815          * @static
20816          */
20817         ids: {},
20818
20819         /**
20820          * Array of element ids defined as drag handles.  Used to determine
20821          * if the element that generated the mousedown event is actually the
20822          * handle and not the html element itself.
20823          * @property handleIds
20824          * @type {string: string}
20825          * @private
20826          * @static
20827          */
20828         handleIds: {},
20829
20830         /**
20831          * the DragDrop object that is currently being dragged
20832          * @property dragCurrent
20833          * @type DragDrop
20834          * @private
20835          * @static
20836          **/
20837         dragCurrent: null,
20838
20839         /**
20840          * the DragDrop object(s) that are being hovered over
20841          * @property dragOvers
20842          * @type Array
20843          * @private
20844          * @static
20845          */
20846         dragOvers: {},
20847
20848         /**
20849          * the X distance between the cursor and the object being dragged
20850          * @property deltaX
20851          * @type int
20852          * @private
20853          * @static
20854          */
20855         deltaX: 0,
20856
20857         /**
20858          * the Y distance between the cursor and the object being dragged
20859          * @property deltaY
20860          * @type int
20861          * @private
20862          * @static
20863          */
20864         deltaY: 0,
20865
20866         /**
20867          * Flag to determine if we should prevent the default behavior of the
20868          * events we define. By default this is true, but this can be set to
20869          * false if you need the default behavior (not recommended)
20870          * @property preventDefault
20871          * @type boolean
20872          * @static
20873          */
20874         preventDefault: true,
20875
20876         /**
20877          * Flag to determine if we should stop the propagation of the events
20878          * we generate. This is true by default but you may want to set it to
20879          * false if the html element contains other features that require the
20880          * mouse click.
20881          * @property stopPropagation
20882          * @type boolean
20883          * @static
20884          */
20885         stopPropagation: true,
20886
20887         /**
20888          * Internal flag that is set to true when drag and drop has been
20889          * intialized
20890          * @property initialized
20891          * @private
20892          * @static
20893          */
20894         initalized: false,
20895
20896         /**
20897          * All drag and drop can be disabled.
20898          * @property locked
20899          * @private
20900          * @static
20901          */
20902         locked: false,
20903
20904         /**
20905          * Called the first time an element is registered.
20906          * @method init
20907          * @private
20908          * @static
20909          */
20910         init: function() {
20911             this.initialized = true;
20912         },
20913
20914         /**
20915          * In point mode, drag and drop interaction is defined by the
20916          * location of the cursor during the drag/drop
20917          * @property POINT
20918          * @type int
20919          * @static
20920          */
20921         POINT: 0,
20922
20923         /**
20924          * In intersect mode, drag and drop interactio nis defined by the
20925          * overlap of two or more drag and drop objects.
20926          * @property INTERSECT
20927          * @type int
20928          * @static
20929          */
20930         INTERSECT: 1,
20931
20932         /**
20933          * The current drag and drop mode.  Default: POINT
20934          * @property mode
20935          * @type int
20936          * @static
20937          */
20938         mode: 0,
20939
20940         /**
20941          * Runs method on all drag and drop objects
20942          * @method _execOnAll
20943          * @private
20944          * @static
20945          */
20946         _execOnAll: function(sMethod, args) {
20947             for (var i in this.ids) {
20948                 for (var j in this.ids[i]) {
20949                     var oDD = this.ids[i][j];
20950                     if (! this.isTypeOfDD(oDD)) {
20951                         continue;
20952                     }
20953                     oDD[sMethod].apply(oDD, args);
20954                 }
20955             }
20956         },
20957
20958         /**
20959          * Drag and drop initialization.  Sets up the global event handlers
20960          * @method _onLoad
20961          * @private
20962          * @static
20963          */
20964         _onLoad: function() {
20965
20966             this.init();
20967
20968             if (!Roo.isTouch) {
20969                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20970                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20971             }
20972             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20973             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20974             
20975             Event.on(window,   "unload",    this._onUnload, this, true);
20976             Event.on(window,   "resize",    this._onResize, this, true);
20977             // Event.on(window,   "mouseout",    this._test);
20978
20979         },
20980
20981         /**
20982          * Reset constraints on all drag and drop objs
20983          * @method _onResize
20984          * @private
20985          * @static
20986          */
20987         _onResize: function(e) {
20988             this._execOnAll("resetConstraints", []);
20989         },
20990
20991         /**
20992          * Lock all drag and drop functionality
20993          * @method lock
20994          * @static
20995          */
20996         lock: function() { this.locked = true; },
20997
20998         /**
20999          * Unlock all drag and drop functionality
21000          * @method unlock
21001          * @static
21002          */
21003         unlock: function() { this.locked = false; },
21004
21005         /**
21006          * Is drag and drop locked?
21007          * @method isLocked
21008          * @return {boolean} True if drag and drop is locked, false otherwise.
21009          * @static
21010          */
21011         isLocked: function() { return this.locked; },
21012
21013         /**
21014          * Location cache that is set for all drag drop objects when a drag is
21015          * initiated, cleared when the drag is finished.
21016          * @property locationCache
21017          * @private
21018          * @static
21019          */
21020         locationCache: {},
21021
21022         /**
21023          * Set useCache to false if you want to force object the lookup of each
21024          * drag and drop linked element constantly during a drag.
21025          * @property useCache
21026          * @type boolean
21027          * @static
21028          */
21029         useCache: true,
21030
21031         /**
21032          * The number of pixels that the mouse needs to move after the
21033          * mousedown before the drag is initiated.  Default=3;
21034          * @property clickPixelThresh
21035          * @type int
21036          * @static
21037          */
21038         clickPixelThresh: 3,
21039
21040         /**
21041          * The number of milliseconds after the mousedown event to initiate the
21042          * drag if we don't get a mouseup event. Default=1000
21043          * @property clickTimeThresh
21044          * @type int
21045          * @static
21046          */
21047         clickTimeThresh: 350,
21048
21049         /**
21050          * Flag that indicates that either the drag pixel threshold or the
21051          * mousdown time threshold has been met
21052          * @property dragThreshMet
21053          * @type boolean
21054          * @private
21055          * @static
21056          */
21057         dragThreshMet: false,
21058
21059         /**
21060          * Timeout used for the click time threshold
21061          * @property clickTimeout
21062          * @type Object
21063          * @private
21064          * @static
21065          */
21066         clickTimeout: null,
21067
21068         /**
21069          * The X position of the mousedown event stored for later use when a
21070          * drag threshold is met.
21071          * @property startX
21072          * @type int
21073          * @private
21074          * @static
21075          */
21076         startX: 0,
21077
21078         /**
21079          * The Y position of the mousedown event stored for later use when a
21080          * drag threshold is met.
21081          * @property startY
21082          * @type int
21083          * @private
21084          * @static
21085          */
21086         startY: 0,
21087
21088         /**
21089          * Each DragDrop instance must be registered with the DragDropMgr.
21090          * This is executed in DragDrop.init()
21091          * @method regDragDrop
21092          * @param {DragDrop} oDD the DragDrop object to register
21093          * @param {String} sGroup the name of the group this element belongs to
21094          * @static
21095          */
21096         regDragDrop: function(oDD, sGroup) {
21097             if (!this.initialized) { this.init(); }
21098
21099             if (!this.ids[sGroup]) {
21100                 this.ids[sGroup] = {};
21101             }
21102             this.ids[sGroup][oDD.id] = oDD;
21103         },
21104
21105         /**
21106          * Removes the supplied dd instance from the supplied group. Executed
21107          * by DragDrop.removeFromGroup, so don't call this function directly.
21108          * @method removeDDFromGroup
21109          * @private
21110          * @static
21111          */
21112         removeDDFromGroup: function(oDD, sGroup) {
21113             if (!this.ids[sGroup]) {
21114                 this.ids[sGroup] = {};
21115             }
21116
21117             var obj = this.ids[sGroup];
21118             if (obj && obj[oDD.id]) {
21119                 delete obj[oDD.id];
21120             }
21121         },
21122
21123         /**
21124          * Unregisters a drag and drop item.  This is executed in
21125          * DragDrop.unreg, use that method instead of calling this directly.
21126          * @method _remove
21127          * @private
21128          * @static
21129          */
21130         _remove: function(oDD) {
21131             for (var g in oDD.groups) {
21132                 if (g && this.ids[g][oDD.id]) {
21133                     delete this.ids[g][oDD.id];
21134                 }
21135             }
21136             delete this.handleIds[oDD.id];
21137         },
21138
21139         /**
21140          * Each DragDrop handle element must be registered.  This is done
21141          * automatically when executing DragDrop.setHandleElId()
21142          * @method regHandle
21143          * @param {String} sDDId the DragDrop id this element is a handle for
21144          * @param {String} sHandleId the id of the element that is the drag
21145          * handle
21146          * @static
21147          */
21148         regHandle: function(sDDId, sHandleId) {
21149             if (!this.handleIds[sDDId]) {
21150                 this.handleIds[sDDId] = {};
21151             }
21152             this.handleIds[sDDId][sHandleId] = sHandleId;
21153         },
21154
21155         /**
21156          * Utility function to determine if a given element has been
21157          * registered as a drag drop item.
21158          * @method isDragDrop
21159          * @param {String} id the element id to check
21160          * @return {boolean} true if this element is a DragDrop item,
21161          * false otherwise
21162          * @static
21163          */
21164         isDragDrop: function(id) {
21165             return ( this.getDDById(id) ) ? true : false;
21166         },
21167
21168         /**
21169          * Returns the drag and drop instances that are in all groups the
21170          * passed in instance belongs to.
21171          * @method getRelated
21172          * @param {DragDrop} p_oDD the obj to get related data for
21173          * @param {boolean} bTargetsOnly if true, only return targetable objs
21174          * @return {DragDrop[]} the related instances
21175          * @static
21176          */
21177         getRelated: function(p_oDD, bTargetsOnly) {
21178             var oDDs = [];
21179             for (var i in p_oDD.groups) {
21180                 for (j in this.ids[i]) {
21181                     var dd = this.ids[i][j];
21182                     if (! this.isTypeOfDD(dd)) {
21183                         continue;
21184                     }
21185                     if (!bTargetsOnly || dd.isTarget) {
21186                         oDDs[oDDs.length] = dd;
21187                     }
21188                 }
21189             }
21190
21191             return oDDs;
21192         },
21193
21194         /**
21195          * Returns true if the specified dd target is a legal target for
21196          * the specifice drag obj
21197          * @method isLegalTarget
21198          * @param {DragDrop} the drag obj
21199          * @param {DragDrop} the target
21200          * @return {boolean} true if the target is a legal target for the
21201          * dd obj
21202          * @static
21203          */
21204         isLegalTarget: function (oDD, oTargetDD) {
21205             var targets = this.getRelated(oDD, true);
21206             for (var i=0, len=targets.length;i<len;++i) {
21207                 if (targets[i].id == oTargetDD.id) {
21208                     return true;
21209                 }
21210             }
21211
21212             return false;
21213         },
21214
21215         /**
21216          * My goal is to be able to transparently determine if an object is
21217          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21218          * returns "object", oDD.constructor.toString() always returns
21219          * "DragDrop" and not the name of the subclass.  So for now it just
21220          * evaluates a well-known variable in DragDrop.
21221          * @method isTypeOfDD
21222          * @param {Object} the object to evaluate
21223          * @return {boolean} true if typeof oDD = DragDrop
21224          * @static
21225          */
21226         isTypeOfDD: function (oDD) {
21227             return (oDD && oDD.__ygDragDrop);
21228         },
21229
21230         /**
21231          * Utility function to determine if a given element has been
21232          * registered as a drag drop handle for the given Drag Drop object.
21233          * @method isHandle
21234          * @param {String} id the element id to check
21235          * @return {boolean} true if this element is a DragDrop handle, false
21236          * otherwise
21237          * @static
21238          */
21239         isHandle: function(sDDId, sHandleId) {
21240             return ( this.handleIds[sDDId] &&
21241                             this.handleIds[sDDId][sHandleId] );
21242         },
21243
21244         /**
21245          * Returns the DragDrop instance for a given id
21246          * @method getDDById
21247          * @param {String} id the id of the DragDrop object
21248          * @return {DragDrop} the drag drop object, null if it is not found
21249          * @static
21250          */
21251         getDDById: function(id) {
21252             for (var i in this.ids) {
21253                 if (this.ids[i][id]) {
21254                     return this.ids[i][id];
21255                 }
21256             }
21257             return null;
21258         },
21259
21260         /**
21261          * Fired after a registered DragDrop object gets the mousedown event.
21262          * Sets up the events required to track the object being dragged
21263          * @method handleMouseDown
21264          * @param {Event} e the event
21265          * @param oDD the DragDrop object being dragged
21266          * @private
21267          * @static
21268          */
21269         handleMouseDown: function(e, oDD) {
21270             if(Roo.QuickTips){
21271                 Roo.QuickTips.disable();
21272             }
21273             this.currentTarget = e.getTarget();
21274
21275             this.dragCurrent = oDD;
21276
21277             var el = oDD.getEl();
21278
21279             // track start position
21280             this.startX = e.getPageX();
21281             this.startY = e.getPageY();
21282
21283             this.deltaX = this.startX - el.offsetLeft;
21284             this.deltaY = this.startY - el.offsetTop;
21285
21286             this.dragThreshMet = false;
21287
21288             this.clickTimeout = setTimeout(
21289                     function() {
21290                         var DDM = Roo.dd.DDM;
21291                         DDM.startDrag(DDM.startX, DDM.startY);
21292                     },
21293                     this.clickTimeThresh );
21294         },
21295
21296         /**
21297          * Fired when either the drag pixel threshol or the mousedown hold
21298          * time threshold has been met.
21299          * @method startDrag
21300          * @param x {int} the X position of the original mousedown
21301          * @param y {int} the Y position of the original mousedown
21302          * @static
21303          */
21304         startDrag: function(x, y) {
21305             clearTimeout(this.clickTimeout);
21306             if (this.dragCurrent) {
21307                 this.dragCurrent.b4StartDrag(x, y);
21308                 this.dragCurrent.startDrag(x, y);
21309             }
21310             this.dragThreshMet = true;
21311         },
21312
21313         /**
21314          * Internal function to handle the mouseup event.  Will be invoked
21315          * from the context of the document.
21316          * @method handleMouseUp
21317          * @param {Event} e the event
21318          * @private
21319          * @static
21320          */
21321         handleMouseUp: function(e) {
21322
21323             if(Roo.QuickTips){
21324                 Roo.QuickTips.enable();
21325             }
21326             if (! this.dragCurrent) {
21327                 return;
21328             }
21329
21330             clearTimeout(this.clickTimeout);
21331
21332             if (this.dragThreshMet) {
21333                 this.fireEvents(e, true);
21334             } else {
21335             }
21336
21337             this.stopDrag(e);
21338
21339             this.stopEvent(e);
21340         },
21341
21342         /**
21343          * Utility to stop event propagation and event default, if these
21344          * features are turned on.
21345          * @method stopEvent
21346          * @param {Event} e the event as returned by this.getEvent()
21347          * @static
21348          */
21349         stopEvent: function(e){
21350             if(this.stopPropagation) {
21351                 e.stopPropagation();
21352             }
21353
21354             if (this.preventDefault) {
21355                 e.preventDefault();
21356             }
21357         },
21358
21359         /**
21360          * Internal function to clean up event handlers after the drag
21361          * operation is complete
21362          * @method stopDrag
21363          * @param {Event} e the event
21364          * @private
21365          * @static
21366          */
21367         stopDrag: function(e) {
21368             // Fire the drag end event for the item that was dragged
21369             if (this.dragCurrent) {
21370                 if (this.dragThreshMet) {
21371                     this.dragCurrent.b4EndDrag(e);
21372                     this.dragCurrent.endDrag(e);
21373                 }
21374
21375                 this.dragCurrent.onMouseUp(e);
21376             }
21377
21378             this.dragCurrent = null;
21379             this.dragOvers = {};
21380         },
21381
21382         /**
21383          * Internal function to handle the mousemove event.  Will be invoked
21384          * from the context of the html element.
21385          *
21386          * @TODO figure out what we can do about mouse events lost when the
21387          * user drags objects beyond the window boundary.  Currently we can
21388          * detect this in internet explorer by verifying that the mouse is
21389          * down during the mousemove event.  Firefox doesn't give us the
21390          * button state on the mousemove event.
21391          * @method handleMouseMove
21392          * @param {Event} e the event
21393          * @private
21394          * @static
21395          */
21396         handleMouseMove: function(e) {
21397             if (! this.dragCurrent) {
21398                 return true;
21399             }
21400
21401             // var button = e.which || e.button;
21402
21403             // check for IE mouseup outside of page boundary
21404             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21405                 this.stopEvent(e);
21406                 return this.handleMouseUp(e);
21407             }
21408
21409             if (!this.dragThreshMet) {
21410                 var diffX = Math.abs(this.startX - e.getPageX());
21411                 var diffY = Math.abs(this.startY - e.getPageY());
21412                 if (diffX > this.clickPixelThresh ||
21413                             diffY > this.clickPixelThresh) {
21414                     this.startDrag(this.startX, this.startY);
21415                 }
21416             }
21417
21418             if (this.dragThreshMet) {
21419                 this.dragCurrent.b4Drag(e);
21420                 this.dragCurrent.onDrag(e);
21421                 if(!this.dragCurrent.moveOnly){
21422                     this.fireEvents(e, false);
21423                 }
21424             }
21425
21426             this.stopEvent(e);
21427
21428             return true;
21429         },
21430
21431         /**
21432          * Iterates over all of the DragDrop elements to find ones we are
21433          * hovering over or dropping on
21434          * @method fireEvents
21435          * @param {Event} e the event
21436          * @param {boolean} isDrop is this a drop op or a mouseover op?
21437          * @private
21438          * @static
21439          */
21440         fireEvents: function(e, isDrop) {
21441             var dc = this.dragCurrent;
21442
21443             // If the user did the mouse up outside of the window, we could
21444             // get here even though we have ended the drag.
21445             if (!dc || dc.isLocked()) {
21446                 return;
21447             }
21448
21449             var pt = e.getPoint();
21450
21451             // cache the previous dragOver array
21452             var oldOvers = [];
21453
21454             var outEvts   = [];
21455             var overEvts  = [];
21456             var dropEvts  = [];
21457             var enterEvts = [];
21458
21459             // Check to see if the object(s) we were hovering over is no longer
21460             // being hovered over so we can fire the onDragOut event
21461             for (var i in this.dragOvers) {
21462
21463                 var ddo = this.dragOvers[i];
21464
21465                 if (! this.isTypeOfDD(ddo)) {
21466                     continue;
21467                 }
21468
21469                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21470                     outEvts.push( ddo );
21471                 }
21472
21473                 oldOvers[i] = true;
21474                 delete this.dragOvers[i];
21475             }
21476
21477             for (var sGroup in dc.groups) {
21478
21479                 if ("string" != typeof sGroup) {
21480                     continue;
21481                 }
21482
21483                 for (i in this.ids[sGroup]) {
21484                     var oDD = this.ids[sGroup][i];
21485                     if (! this.isTypeOfDD(oDD)) {
21486                         continue;
21487                     }
21488
21489                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21490                         if (this.isOverTarget(pt, oDD, this.mode)) {
21491                             // look for drop interactions
21492                             if (isDrop) {
21493                                 dropEvts.push( oDD );
21494                             // look for drag enter and drag over interactions
21495                             } else {
21496
21497                                 // initial drag over: dragEnter fires
21498                                 if (!oldOvers[oDD.id]) {
21499                                     enterEvts.push( oDD );
21500                                 // subsequent drag overs: dragOver fires
21501                                 } else {
21502                                     overEvts.push( oDD );
21503                                 }
21504
21505                                 this.dragOvers[oDD.id] = oDD;
21506                             }
21507                         }
21508                     }
21509                 }
21510             }
21511
21512             if (this.mode) {
21513                 if (outEvts.length) {
21514                     dc.b4DragOut(e, outEvts);
21515                     dc.onDragOut(e, outEvts);
21516                 }
21517
21518                 if (enterEvts.length) {
21519                     dc.onDragEnter(e, enterEvts);
21520                 }
21521
21522                 if (overEvts.length) {
21523                     dc.b4DragOver(e, overEvts);
21524                     dc.onDragOver(e, overEvts);
21525                 }
21526
21527                 if (dropEvts.length) {
21528                     dc.b4DragDrop(e, dropEvts);
21529                     dc.onDragDrop(e, dropEvts);
21530                 }
21531
21532             } else {
21533                 // fire dragout events
21534                 var len = 0;
21535                 for (i=0, len=outEvts.length; i<len; ++i) {
21536                     dc.b4DragOut(e, outEvts[i].id);
21537                     dc.onDragOut(e, outEvts[i].id);
21538                 }
21539
21540                 // fire enter events
21541                 for (i=0,len=enterEvts.length; i<len; ++i) {
21542                     // dc.b4DragEnter(e, oDD.id);
21543                     dc.onDragEnter(e, enterEvts[i].id);
21544                 }
21545
21546                 // fire over events
21547                 for (i=0,len=overEvts.length; i<len; ++i) {
21548                     dc.b4DragOver(e, overEvts[i].id);
21549                     dc.onDragOver(e, overEvts[i].id);
21550                 }
21551
21552                 // fire drop events
21553                 for (i=0, len=dropEvts.length; i<len; ++i) {
21554                     dc.b4DragDrop(e, dropEvts[i].id);
21555                     dc.onDragDrop(e, dropEvts[i].id);
21556                 }
21557
21558             }
21559
21560             // notify about a drop that did not find a target
21561             if (isDrop && !dropEvts.length) {
21562                 dc.onInvalidDrop(e);
21563             }
21564
21565         },
21566
21567         /**
21568          * Helper function for getting the best match from the list of drag
21569          * and drop objects returned by the drag and drop events when we are
21570          * in INTERSECT mode.  It returns either the first object that the
21571          * cursor is over, or the object that has the greatest overlap with
21572          * the dragged element.
21573          * @method getBestMatch
21574          * @param  {DragDrop[]} dds The array of drag and drop objects
21575          * targeted
21576          * @return {DragDrop}       The best single match
21577          * @static
21578          */
21579         getBestMatch: function(dds) {
21580             var winner = null;
21581             // Return null if the input is not what we expect
21582             //if (!dds || !dds.length || dds.length == 0) {
21583                // winner = null;
21584             // If there is only one item, it wins
21585             //} else if (dds.length == 1) {
21586
21587             var len = dds.length;
21588
21589             if (len == 1) {
21590                 winner = dds[0];
21591             } else {
21592                 // Loop through the targeted items
21593                 for (var i=0; i<len; ++i) {
21594                     var dd = dds[i];
21595                     // If the cursor is over the object, it wins.  If the
21596                     // cursor is over multiple matches, the first one we come
21597                     // to wins.
21598                     if (dd.cursorIsOver) {
21599                         winner = dd;
21600                         break;
21601                     // Otherwise the object with the most overlap wins
21602                     } else {
21603                         if (!winner ||
21604                             winner.overlap.getArea() < dd.overlap.getArea()) {
21605                             winner = dd;
21606                         }
21607                     }
21608                 }
21609             }
21610
21611             return winner;
21612         },
21613
21614         /**
21615          * Refreshes the cache of the top-left and bottom-right points of the
21616          * drag and drop objects in the specified group(s).  This is in the
21617          * format that is stored in the drag and drop instance, so typical
21618          * usage is:
21619          * <code>
21620          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21621          * </code>
21622          * Alternatively:
21623          * <code>
21624          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21625          * </code>
21626          * @TODO this really should be an indexed array.  Alternatively this
21627          * method could accept both.
21628          * @method refreshCache
21629          * @param {Object} groups an associative array of groups to refresh
21630          * @static
21631          */
21632         refreshCache: function(groups) {
21633             for (var sGroup in groups) {
21634                 if ("string" != typeof sGroup) {
21635                     continue;
21636                 }
21637                 for (var i in this.ids[sGroup]) {
21638                     var oDD = this.ids[sGroup][i];
21639
21640                     if (this.isTypeOfDD(oDD)) {
21641                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21642                         var loc = this.getLocation(oDD);
21643                         if (loc) {
21644                             this.locationCache[oDD.id] = loc;
21645                         } else {
21646                             delete this.locationCache[oDD.id];
21647                             // this will unregister the drag and drop object if
21648                             // the element is not in a usable state
21649                             // oDD.unreg();
21650                         }
21651                     }
21652                 }
21653             }
21654         },
21655
21656         /**
21657          * This checks to make sure an element exists and is in the DOM.  The
21658          * main purpose is to handle cases where innerHTML is used to remove
21659          * drag and drop objects from the DOM.  IE provides an 'unspecified
21660          * error' when trying to access the offsetParent of such an element
21661          * @method verifyEl
21662          * @param {HTMLElement} el the element to check
21663          * @return {boolean} true if the element looks usable
21664          * @static
21665          */
21666         verifyEl: function(el) {
21667             if (el) {
21668                 var parent;
21669                 if(Roo.isIE){
21670                     try{
21671                         parent = el.offsetParent;
21672                     }catch(e){}
21673                 }else{
21674                     parent = el.offsetParent;
21675                 }
21676                 if (parent) {
21677                     return true;
21678                 }
21679             }
21680
21681             return false;
21682         },
21683
21684         /**
21685          * Returns a Region object containing the drag and drop element's position
21686          * and size, including the padding configured for it
21687          * @method getLocation
21688          * @param {DragDrop} oDD the drag and drop object to get the
21689          *                       location for
21690          * @return {Roo.lib.Region} a Region object representing the total area
21691          *                             the element occupies, including any padding
21692          *                             the instance is configured for.
21693          * @static
21694          */
21695         getLocation: function(oDD) {
21696             if (! this.isTypeOfDD(oDD)) {
21697                 return null;
21698             }
21699
21700             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21701
21702             try {
21703                 pos= Roo.lib.Dom.getXY(el);
21704             } catch (e) { }
21705
21706             if (!pos) {
21707                 return null;
21708             }
21709
21710             x1 = pos[0];
21711             x2 = x1 + el.offsetWidth;
21712             y1 = pos[1];
21713             y2 = y1 + el.offsetHeight;
21714
21715             t = y1 - oDD.padding[0];
21716             r = x2 + oDD.padding[1];
21717             b = y2 + oDD.padding[2];
21718             l = x1 - oDD.padding[3];
21719
21720             return new Roo.lib.Region( t, r, b, l );
21721         },
21722
21723         /**
21724          * Checks the cursor location to see if it over the target
21725          * @method isOverTarget
21726          * @param {Roo.lib.Point} pt The point to evaluate
21727          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21728          * @return {boolean} true if the mouse is over the target
21729          * @private
21730          * @static
21731          */
21732         isOverTarget: function(pt, oTarget, intersect) {
21733             // use cache if available
21734             var loc = this.locationCache[oTarget.id];
21735             if (!loc || !this.useCache) {
21736                 loc = this.getLocation(oTarget);
21737                 this.locationCache[oTarget.id] = loc;
21738
21739             }
21740
21741             if (!loc) {
21742                 return false;
21743             }
21744
21745             oTarget.cursorIsOver = loc.contains( pt );
21746
21747             // DragDrop is using this as a sanity check for the initial mousedown
21748             // in this case we are done.  In POINT mode, if the drag obj has no
21749             // contraints, we are also done. Otherwise we need to evaluate the
21750             // location of the target as related to the actual location of the
21751             // dragged element.
21752             var dc = this.dragCurrent;
21753             if (!dc || !dc.getTargetCoord ||
21754                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21755                 return oTarget.cursorIsOver;
21756             }
21757
21758             oTarget.overlap = null;
21759
21760             // Get the current location of the drag element, this is the
21761             // location of the mouse event less the delta that represents
21762             // where the original mousedown happened on the element.  We
21763             // need to consider constraints and ticks as well.
21764             var pos = dc.getTargetCoord(pt.x, pt.y);
21765
21766             var el = dc.getDragEl();
21767             var curRegion = new Roo.lib.Region( pos.y,
21768                                                    pos.x + el.offsetWidth,
21769                                                    pos.y + el.offsetHeight,
21770                                                    pos.x );
21771
21772             var overlap = curRegion.intersect(loc);
21773
21774             if (overlap) {
21775                 oTarget.overlap = overlap;
21776                 return (intersect) ? true : oTarget.cursorIsOver;
21777             } else {
21778                 return false;
21779             }
21780         },
21781
21782         /**
21783          * unload event handler
21784          * @method _onUnload
21785          * @private
21786          * @static
21787          */
21788         _onUnload: function(e, me) {
21789             Roo.dd.DragDropMgr.unregAll();
21790         },
21791
21792         /**
21793          * Cleans up the drag and drop events and objects.
21794          * @method unregAll
21795          * @private
21796          * @static
21797          */
21798         unregAll: function() {
21799
21800             if (this.dragCurrent) {
21801                 this.stopDrag();
21802                 this.dragCurrent = null;
21803             }
21804
21805             this._execOnAll("unreg", []);
21806
21807             for (i in this.elementCache) {
21808                 delete this.elementCache[i];
21809             }
21810
21811             this.elementCache = {};
21812             this.ids = {};
21813         },
21814
21815         /**
21816          * A cache of DOM elements
21817          * @property elementCache
21818          * @private
21819          * @static
21820          */
21821         elementCache: {},
21822
21823         /**
21824          * Get the wrapper for the DOM element specified
21825          * @method getElWrapper
21826          * @param {String} id the id of the element to get
21827          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21828          * @private
21829          * @deprecated This wrapper isn't that useful
21830          * @static
21831          */
21832         getElWrapper: function(id) {
21833             var oWrapper = this.elementCache[id];
21834             if (!oWrapper || !oWrapper.el) {
21835                 oWrapper = this.elementCache[id] =
21836                     new this.ElementWrapper(Roo.getDom(id));
21837             }
21838             return oWrapper;
21839         },
21840
21841         /**
21842          * Returns the actual DOM element
21843          * @method getElement
21844          * @param {String} id the id of the elment to get
21845          * @return {Object} The element
21846          * @deprecated use Roo.getDom instead
21847          * @static
21848          */
21849         getElement: function(id) {
21850             return Roo.getDom(id);
21851         },
21852
21853         /**
21854          * Returns the style property for the DOM element (i.e.,
21855          * document.getElById(id).style)
21856          * @method getCss
21857          * @param {String} id the id of the elment to get
21858          * @return {Object} The style property of the element
21859          * @deprecated use Roo.getDom instead
21860          * @static
21861          */
21862         getCss: function(id) {
21863             var el = Roo.getDom(id);
21864             return (el) ? el.style : null;
21865         },
21866
21867         /**
21868          * Inner class for cached elements
21869          * @class DragDropMgr.ElementWrapper
21870          * @for DragDropMgr
21871          * @private
21872          * @deprecated
21873          */
21874         ElementWrapper: function(el) {
21875                 /**
21876                  * The element
21877                  * @property el
21878                  */
21879                 this.el = el || null;
21880                 /**
21881                  * The element id
21882                  * @property id
21883                  */
21884                 this.id = this.el && el.id;
21885                 /**
21886                  * A reference to the style property
21887                  * @property css
21888                  */
21889                 this.css = this.el && el.style;
21890             },
21891
21892         /**
21893          * Returns the X position of an html element
21894          * @method getPosX
21895          * @param el the element for which to get the position
21896          * @return {int} the X coordinate
21897          * @for DragDropMgr
21898          * @deprecated use Roo.lib.Dom.getX instead
21899          * @static
21900          */
21901         getPosX: function(el) {
21902             return Roo.lib.Dom.getX(el);
21903         },
21904
21905         /**
21906          * Returns the Y position of an html element
21907          * @method getPosY
21908          * @param el the element for which to get the position
21909          * @return {int} the Y coordinate
21910          * @deprecated use Roo.lib.Dom.getY instead
21911          * @static
21912          */
21913         getPosY: function(el) {
21914             return Roo.lib.Dom.getY(el);
21915         },
21916
21917         /**
21918          * Swap two nodes.  In IE, we use the native method, for others we
21919          * emulate the IE behavior
21920          * @method swapNode
21921          * @param n1 the first node to swap
21922          * @param n2 the other node to swap
21923          * @static
21924          */
21925         swapNode: function(n1, n2) {
21926             if (n1.swapNode) {
21927                 n1.swapNode(n2);
21928             } else {
21929                 var p = n2.parentNode;
21930                 var s = n2.nextSibling;
21931
21932                 if (s == n1) {
21933                     p.insertBefore(n1, n2);
21934                 } else if (n2 == n1.nextSibling) {
21935                     p.insertBefore(n2, n1);
21936                 } else {
21937                     n1.parentNode.replaceChild(n2, n1);
21938                     p.insertBefore(n1, s);
21939                 }
21940             }
21941         },
21942
21943         /**
21944          * Returns the current scroll position
21945          * @method getScroll
21946          * @private
21947          * @static
21948          */
21949         getScroll: function () {
21950             var t, l, dde=document.documentElement, db=document.body;
21951             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21952                 t = dde.scrollTop;
21953                 l = dde.scrollLeft;
21954             } else if (db) {
21955                 t = db.scrollTop;
21956                 l = db.scrollLeft;
21957             } else {
21958
21959             }
21960             return { top: t, left: l };
21961         },
21962
21963         /**
21964          * Returns the specified element style property
21965          * @method getStyle
21966          * @param {HTMLElement} el          the element
21967          * @param {string}      styleProp   the style property
21968          * @return {string} The value of the style property
21969          * @deprecated use Roo.lib.Dom.getStyle
21970          * @static
21971          */
21972         getStyle: function(el, styleProp) {
21973             return Roo.fly(el).getStyle(styleProp);
21974         },
21975
21976         /**
21977          * Gets the scrollTop
21978          * @method getScrollTop
21979          * @return {int} the document's scrollTop
21980          * @static
21981          */
21982         getScrollTop: function () { return this.getScroll().top; },
21983
21984         /**
21985          * Gets the scrollLeft
21986          * @method getScrollLeft
21987          * @return {int} the document's scrollTop
21988          * @static
21989          */
21990         getScrollLeft: function () { return this.getScroll().left; },
21991
21992         /**
21993          * Sets the x/y position of an element to the location of the
21994          * target element.
21995          * @method moveToEl
21996          * @param {HTMLElement} moveEl      The element to move
21997          * @param {HTMLElement} targetEl    The position reference element
21998          * @static
21999          */
22000         moveToEl: function (moveEl, targetEl) {
22001             var aCoord = Roo.lib.Dom.getXY(targetEl);
22002             Roo.lib.Dom.setXY(moveEl, aCoord);
22003         },
22004
22005         /**
22006          * Numeric array sort function
22007          * @method numericSort
22008          * @static
22009          */
22010         numericSort: function(a, b) { return (a - b); },
22011
22012         /**
22013          * Internal counter
22014          * @property _timeoutCount
22015          * @private
22016          * @static
22017          */
22018         _timeoutCount: 0,
22019
22020         /**
22021          * Trying to make the load order less important.  Without this we get
22022          * an error if this file is loaded before the Event Utility.
22023          * @method _addListeners
22024          * @private
22025          * @static
22026          */
22027         _addListeners: function() {
22028             var DDM = Roo.dd.DDM;
22029             if ( Roo.lib.Event && document ) {
22030                 DDM._onLoad();
22031             } else {
22032                 if (DDM._timeoutCount > 2000) {
22033                 } else {
22034                     setTimeout(DDM._addListeners, 10);
22035                     if (document && document.body) {
22036                         DDM._timeoutCount += 1;
22037                     }
22038                 }
22039             }
22040         },
22041
22042         /**
22043          * Recursively searches the immediate parent and all child nodes for
22044          * the handle element in order to determine wheter or not it was
22045          * clicked.
22046          * @method handleWasClicked
22047          * @param node the html element to inspect
22048          * @static
22049          */
22050         handleWasClicked: function(node, id) {
22051             if (this.isHandle(id, node.id)) {
22052                 return true;
22053             } else {
22054                 // check to see if this is a text node child of the one we want
22055                 var p = node.parentNode;
22056
22057                 while (p) {
22058                     if (this.isHandle(id, p.id)) {
22059                         return true;
22060                     } else {
22061                         p = p.parentNode;
22062                     }
22063                 }
22064             }
22065
22066             return false;
22067         }
22068
22069     };
22070
22071 }();
22072
22073 // shorter alias, save a few bytes
22074 Roo.dd.DDM = Roo.dd.DragDropMgr;
22075 Roo.dd.DDM._addListeners();
22076
22077 }/*
22078  * Based on:
22079  * Ext JS Library 1.1.1
22080  * Copyright(c) 2006-2007, Ext JS, LLC.
22081  *
22082  * Originally Released Under LGPL - original licence link has changed is not relivant.
22083  *
22084  * Fork - LGPL
22085  * <script type="text/javascript">
22086  */
22087
22088 /**
22089  * @class Roo.dd.DD
22090  * A DragDrop implementation where the linked element follows the
22091  * mouse cursor during a drag.
22092  * @extends Roo.dd.DragDrop
22093  * @constructor
22094  * @param {String} id the id of the linked element
22095  * @param {String} sGroup the group of related DragDrop items
22096  * @param {object} config an object containing configurable attributes
22097  *                Valid properties for DD:
22098  *                    scroll
22099  */
22100 Roo.dd.DD = function(id, sGroup, config) {
22101     if (id) {
22102         this.init(id, sGroup, config);
22103     }
22104 };
22105
22106 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22107
22108     /**
22109      * When set to true, the utility automatically tries to scroll the browser
22110      * window wehn a drag and drop element is dragged near the viewport boundary.
22111      * Defaults to true.
22112      * @property scroll
22113      * @type boolean
22114      */
22115     scroll: true,
22116
22117     /**
22118      * Sets the pointer offset to the distance between the linked element's top
22119      * left corner and the location the element was clicked
22120      * @method autoOffset
22121      * @param {int} iPageX the X coordinate of the click
22122      * @param {int} iPageY the Y coordinate of the click
22123      */
22124     autoOffset: function(iPageX, iPageY) {
22125         var x = iPageX - this.startPageX;
22126         var y = iPageY - this.startPageY;
22127         this.setDelta(x, y);
22128     },
22129
22130     /**
22131      * Sets the pointer offset.  You can call this directly to force the
22132      * offset to be in a particular location (e.g., pass in 0,0 to set it
22133      * to the center of the object)
22134      * @method setDelta
22135      * @param {int} iDeltaX the distance from the left
22136      * @param {int} iDeltaY the distance from the top
22137      */
22138     setDelta: function(iDeltaX, iDeltaY) {
22139         this.deltaX = iDeltaX;
22140         this.deltaY = iDeltaY;
22141     },
22142
22143     /**
22144      * Sets the drag element to the location of the mousedown or click event,
22145      * maintaining the cursor location relative to the location on the element
22146      * that was clicked.  Override this if you want to place the element in a
22147      * location other than where the cursor is.
22148      * @method setDragElPos
22149      * @param {int} iPageX the X coordinate of the mousedown or drag event
22150      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22151      */
22152     setDragElPos: function(iPageX, iPageY) {
22153         // the first time we do this, we are going to check to make sure
22154         // the element has css positioning
22155
22156         var el = this.getDragEl();
22157         this.alignElWithMouse(el, iPageX, iPageY);
22158     },
22159
22160     /**
22161      * Sets the element to the location of the mousedown or click event,
22162      * maintaining the cursor location relative to the location on the element
22163      * that was clicked.  Override this if you want to place the element in a
22164      * location other than where the cursor is.
22165      * @method alignElWithMouse
22166      * @param {HTMLElement} el the element to move
22167      * @param {int} iPageX the X coordinate of the mousedown or drag event
22168      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22169      */
22170     alignElWithMouse: function(el, iPageX, iPageY) {
22171         var oCoord = this.getTargetCoord(iPageX, iPageY);
22172         var fly = el.dom ? el : Roo.fly(el);
22173         if (!this.deltaSetXY) {
22174             var aCoord = [oCoord.x, oCoord.y];
22175             fly.setXY(aCoord);
22176             var newLeft = fly.getLeft(true);
22177             var newTop  = fly.getTop(true);
22178             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22179         } else {
22180             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22181         }
22182
22183         this.cachePosition(oCoord.x, oCoord.y);
22184         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22185         return oCoord;
22186     },
22187
22188     /**
22189      * Saves the most recent position so that we can reset the constraints and
22190      * tick marks on-demand.  We need to know this so that we can calculate the
22191      * number of pixels the element is offset from its original position.
22192      * @method cachePosition
22193      * @param iPageX the current x position (optional, this just makes it so we
22194      * don't have to look it up again)
22195      * @param iPageY the current y position (optional, this just makes it so we
22196      * don't have to look it up again)
22197      */
22198     cachePosition: function(iPageX, iPageY) {
22199         if (iPageX) {
22200             this.lastPageX = iPageX;
22201             this.lastPageY = iPageY;
22202         } else {
22203             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22204             this.lastPageX = aCoord[0];
22205             this.lastPageY = aCoord[1];
22206         }
22207     },
22208
22209     /**
22210      * Auto-scroll the window if the dragged object has been moved beyond the
22211      * visible window boundary.
22212      * @method autoScroll
22213      * @param {int} x the drag element's x position
22214      * @param {int} y the drag element's y position
22215      * @param {int} h the height of the drag element
22216      * @param {int} w the width of the drag element
22217      * @private
22218      */
22219     autoScroll: function(x, y, h, w) {
22220
22221         if (this.scroll) {
22222             // The client height
22223             var clientH = Roo.lib.Dom.getViewWidth();
22224
22225             // The client width
22226             var clientW = Roo.lib.Dom.getViewHeight();
22227
22228             // The amt scrolled down
22229             var st = this.DDM.getScrollTop();
22230
22231             // The amt scrolled right
22232             var sl = this.DDM.getScrollLeft();
22233
22234             // Location of the bottom of the element
22235             var bot = h + y;
22236
22237             // Location of the right of the element
22238             var right = w + x;
22239
22240             // The distance from the cursor to the bottom of the visible area,
22241             // adjusted so that we don't scroll if the cursor is beyond the
22242             // element drag constraints
22243             var toBot = (clientH + st - y - this.deltaY);
22244
22245             // The distance from the cursor to the right of the visible area
22246             var toRight = (clientW + sl - x - this.deltaX);
22247
22248
22249             // How close to the edge the cursor must be before we scroll
22250             // var thresh = (document.all) ? 100 : 40;
22251             var thresh = 40;
22252
22253             // How many pixels to scroll per autoscroll op.  This helps to reduce
22254             // clunky scrolling. IE is more sensitive about this ... it needs this
22255             // value to be higher.
22256             var scrAmt = (document.all) ? 80 : 30;
22257
22258             // Scroll down if we are near the bottom of the visible page and the
22259             // obj extends below the crease
22260             if ( bot > clientH && toBot < thresh ) {
22261                 window.scrollTo(sl, st + scrAmt);
22262             }
22263
22264             // Scroll up if the window is scrolled down and the top of the object
22265             // goes above the top border
22266             if ( y < st && st > 0 && y - st < thresh ) {
22267                 window.scrollTo(sl, st - scrAmt);
22268             }
22269
22270             // Scroll right if the obj is beyond the right border and the cursor is
22271             // near the border.
22272             if ( right > clientW && toRight < thresh ) {
22273                 window.scrollTo(sl + scrAmt, st);
22274             }
22275
22276             // Scroll left if the window has been scrolled to the right and the obj
22277             // extends past the left border
22278             if ( x < sl && sl > 0 && x - sl < thresh ) {
22279                 window.scrollTo(sl - scrAmt, st);
22280             }
22281         }
22282     },
22283
22284     /**
22285      * Finds the location the element should be placed if we want to move
22286      * it to where the mouse location less the click offset would place us.
22287      * @method getTargetCoord
22288      * @param {int} iPageX the X coordinate of the click
22289      * @param {int} iPageY the Y coordinate of the click
22290      * @return an object that contains the coordinates (Object.x and Object.y)
22291      * @private
22292      */
22293     getTargetCoord: function(iPageX, iPageY) {
22294
22295
22296         var x = iPageX - this.deltaX;
22297         var y = iPageY - this.deltaY;
22298
22299         if (this.constrainX) {
22300             if (x < this.minX) { x = this.minX; }
22301             if (x > this.maxX) { x = this.maxX; }
22302         }
22303
22304         if (this.constrainY) {
22305             if (y < this.minY) { y = this.minY; }
22306             if (y > this.maxY) { y = this.maxY; }
22307         }
22308
22309         x = this.getTick(x, this.xTicks);
22310         y = this.getTick(y, this.yTicks);
22311
22312
22313         return {x:x, y:y};
22314     },
22315
22316     /*
22317      * Sets up config options specific to this class. Overrides
22318      * Roo.dd.DragDrop, but all versions of this method through the
22319      * inheritance chain are called
22320      */
22321     applyConfig: function() {
22322         Roo.dd.DD.superclass.applyConfig.call(this);
22323         this.scroll = (this.config.scroll !== false);
22324     },
22325
22326     /*
22327      * Event that fires prior to the onMouseDown event.  Overrides
22328      * Roo.dd.DragDrop.
22329      */
22330     b4MouseDown: function(e) {
22331         // this.resetConstraints();
22332         this.autoOffset(e.getPageX(),
22333                             e.getPageY());
22334     },
22335
22336     /*
22337      * Event that fires prior to the onDrag event.  Overrides
22338      * Roo.dd.DragDrop.
22339      */
22340     b4Drag: function(e) {
22341         this.setDragElPos(e.getPageX(),
22342                             e.getPageY());
22343     },
22344
22345     toString: function() {
22346         return ("DD " + this.id);
22347     }
22348
22349     //////////////////////////////////////////////////////////////////////////
22350     // Debugging ygDragDrop events that can be overridden
22351     //////////////////////////////////////////////////////////////////////////
22352     /*
22353     startDrag: function(x, y) {
22354     },
22355
22356     onDrag: function(e) {
22357     },
22358
22359     onDragEnter: function(e, id) {
22360     },
22361
22362     onDragOver: function(e, id) {
22363     },
22364
22365     onDragOut: function(e, id) {
22366     },
22367
22368     onDragDrop: function(e, id) {
22369     },
22370
22371     endDrag: function(e) {
22372     }
22373
22374     */
22375
22376 });/*
22377  * Based on:
22378  * Ext JS Library 1.1.1
22379  * Copyright(c) 2006-2007, Ext JS, LLC.
22380  *
22381  * Originally Released Under LGPL - original licence link has changed is not relivant.
22382  *
22383  * Fork - LGPL
22384  * <script type="text/javascript">
22385  */
22386
22387 /**
22388  * @class Roo.dd.DDProxy
22389  * A DragDrop implementation that inserts an empty, bordered div into
22390  * the document that follows the cursor during drag operations.  At the time of
22391  * the click, the frame div is resized to the dimensions of the linked html
22392  * element, and moved to the exact location of the linked element.
22393  *
22394  * References to the "frame" element refer to the single proxy element that
22395  * was created to be dragged in place of all DDProxy elements on the
22396  * page.
22397  *
22398  * @extends Roo.dd.DD
22399  * @constructor
22400  * @param {String} id the id of the linked html element
22401  * @param {String} sGroup the group of related DragDrop objects
22402  * @param {object} config an object containing configurable attributes
22403  *                Valid properties for DDProxy in addition to those in DragDrop:
22404  *                   resizeFrame, centerFrame, dragElId
22405  */
22406 Roo.dd.DDProxy = function(id, sGroup, config) {
22407     if (id) {
22408         this.init(id, sGroup, config);
22409         this.initFrame();
22410     }
22411 };
22412
22413 /**
22414  * The default drag frame div id
22415  * @property Roo.dd.DDProxy.dragElId
22416  * @type String
22417  * @static
22418  */
22419 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22420
22421 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22422
22423     /**
22424      * By default we resize the drag frame to be the same size as the element
22425      * we want to drag (this is to get the frame effect).  We can turn it off
22426      * if we want a different behavior.
22427      * @property resizeFrame
22428      * @type boolean
22429      */
22430     resizeFrame: true,
22431
22432     /**
22433      * By default the frame is positioned exactly where the drag element is, so
22434      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22435      * you do not have constraints on the obj is to have the drag frame centered
22436      * around the cursor.  Set centerFrame to true for this effect.
22437      * @property centerFrame
22438      * @type boolean
22439      */
22440     centerFrame: false,
22441
22442     /**
22443      * Creates the proxy element if it does not yet exist
22444      * @method createFrame
22445      */
22446     createFrame: function() {
22447         var self = this;
22448         var body = document.body;
22449
22450         if (!body || !body.firstChild) {
22451             setTimeout( function() { self.createFrame(); }, 50 );
22452             return;
22453         }
22454
22455         var div = this.getDragEl();
22456
22457         if (!div) {
22458             div    = document.createElement("div");
22459             div.id = this.dragElId;
22460             var s  = div.style;
22461
22462             s.position   = "absolute";
22463             s.visibility = "hidden";
22464             s.cursor     = "move";
22465             s.border     = "2px solid #aaa";
22466             s.zIndex     = 999;
22467
22468             // appendChild can blow up IE if invoked prior to the window load event
22469             // while rendering a table.  It is possible there are other scenarios
22470             // that would cause this to happen as well.
22471             body.insertBefore(div, body.firstChild);
22472         }
22473     },
22474
22475     /**
22476      * Initialization for the drag frame element.  Must be called in the
22477      * constructor of all subclasses
22478      * @method initFrame
22479      */
22480     initFrame: function() {
22481         this.createFrame();
22482     },
22483
22484     applyConfig: function() {
22485         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22486
22487         this.resizeFrame = (this.config.resizeFrame !== false);
22488         this.centerFrame = (this.config.centerFrame);
22489         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22490     },
22491
22492     /**
22493      * Resizes the drag frame to the dimensions of the clicked object, positions
22494      * it over the object, and finally displays it
22495      * @method showFrame
22496      * @param {int} iPageX X click position
22497      * @param {int} iPageY Y click position
22498      * @private
22499      */
22500     showFrame: function(iPageX, iPageY) {
22501         var el = this.getEl();
22502         var dragEl = this.getDragEl();
22503         var s = dragEl.style;
22504
22505         this._resizeProxy();
22506
22507         if (this.centerFrame) {
22508             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22509                            Math.round(parseInt(s.height, 10)/2) );
22510         }
22511
22512         this.setDragElPos(iPageX, iPageY);
22513
22514         Roo.fly(dragEl).show();
22515     },
22516
22517     /**
22518      * The proxy is automatically resized to the dimensions of the linked
22519      * element when a drag is initiated, unless resizeFrame is set to false
22520      * @method _resizeProxy
22521      * @private
22522      */
22523     _resizeProxy: function() {
22524         if (this.resizeFrame) {
22525             var el = this.getEl();
22526             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22527         }
22528     },
22529
22530     // overrides Roo.dd.DragDrop
22531     b4MouseDown: function(e) {
22532         var x = e.getPageX();
22533         var y = e.getPageY();
22534         this.autoOffset(x, y);
22535         this.setDragElPos(x, y);
22536     },
22537
22538     // overrides Roo.dd.DragDrop
22539     b4StartDrag: function(x, y) {
22540         // show the drag frame
22541         this.showFrame(x, y);
22542     },
22543
22544     // overrides Roo.dd.DragDrop
22545     b4EndDrag: function(e) {
22546         Roo.fly(this.getDragEl()).hide();
22547     },
22548
22549     // overrides Roo.dd.DragDrop
22550     // By default we try to move the element to the last location of the frame.
22551     // This is so that the default behavior mirrors that of Roo.dd.DD.
22552     endDrag: function(e) {
22553
22554         var lel = this.getEl();
22555         var del = this.getDragEl();
22556
22557         // Show the drag frame briefly so we can get its position
22558         del.style.visibility = "";
22559
22560         this.beforeMove();
22561         // Hide the linked element before the move to get around a Safari
22562         // rendering bug.
22563         lel.style.visibility = "hidden";
22564         Roo.dd.DDM.moveToEl(lel, del);
22565         del.style.visibility = "hidden";
22566         lel.style.visibility = "";
22567
22568         this.afterDrag();
22569     },
22570
22571     beforeMove : function(){
22572
22573     },
22574
22575     afterDrag : function(){
22576
22577     },
22578
22579     toString: function() {
22580         return ("DDProxy " + this.id);
22581     }
22582
22583 });
22584 /*
22585  * Based on:
22586  * Ext JS Library 1.1.1
22587  * Copyright(c) 2006-2007, Ext JS, LLC.
22588  *
22589  * Originally Released Under LGPL - original licence link has changed is not relivant.
22590  *
22591  * Fork - LGPL
22592  * <script type="text/javascript">
22593  */
22594
22595  /**
22596  * @class Roo.dd.DDTarget
22597  * A DragDrop implementation that does not move, but can be a drop
22598  * target.  You would get the same result by simply omitting implementation
22599  * for the event callbacks, but this way we reduce the processing cost of the
22600  * event listener and the callbacks.
22601  * @extends Roo.dd.DragDrop
22602  * @constructor
22603  * @param {String} id the id of the element that is a drop target
22604  * @param {String} sGroup the group of related DragDrop objects
22605  * @param {object} config an object containing configurable attributes
22606  *                 Valid properties for DDTarget in addition to those in
22607  *                 DragDrop:
22608  *                    none
22609  */
22610 Roo.dd.DDTarget = function(id, sGroup, config) {
22611     if (id) {
22612         this.initTarget(id, sGroup, config);
22613     }
22614     if (config && (config.listeners || config.events)) { 
22615         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22616             listeners : config.listeners || {}, 
22617             events : config.events || {} 
22618         });    
22619     }
22620 };
22621
22622 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22623 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22624     toString: function() {
22625         return ("DDTarget " + this.id);
22626     }
22627 });
22628 /*
22629  * Based on:
22630  * Ext JS Library 1.1.1
22631  * Copyright(c) 2006-2007, Ext JS, LLC.
22632  *
22633  * Originally Released Under LGPL - original licence link has changed is not relivant.
22634  *
22635  * Fork - LGPL
22636  * <script type="text/javascript">
22637  */
22638  
22639
22640 /**
22641  * @class Roo.dd.ScrollManager
22642  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22643  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22644  * @static
22645  */
22646 Roo.dd.ScrollManager = function(){
22647     var ddm = Roo.dd.DragDropMgr;
22648     var els = {};
22649     var dragEl = null;
22650     var proc = {};
22651     
22652     
22653     
22654     var onStop = function(e){
22655         dragEl = null;
22656         clearProc();
22657     };
22658     
22659     var triggerRefresh = function(){
22660         if(ddm.dragCurrent){
22661              ddm.refreshCache(ddm.dragCurrent.groups);
22662         }
22663     };
22664     
22665     var doScroll = function(){
22666         if(ddm.dragCurrent){
22667             var dds = Roo.dd.ScrollManager;
22668             if(!dds.animate){
22669                 if(proc.el.scroll(proc.dir, dds.increment)){
22670                     triggerRefresh();
22671                 }
22672             }else{
22673                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22674             }
22675         }
22676     };
22677     
22678     var clearProc = function(){
22679         if(proc.id){
22680             clearInterval(proc.id);
22681         }
22682         proc.id = 0;
22683         proc.el = null;
22684         proc.dir = "";
22685     };
22686     
22687     var startProc = function(el, dir){
22688          Roo.log('scroll startproc');
22689         clearProc();
22690         proc.el = el;
22691         proc.dir = dir;
22692         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22693     };
22694     
22695     var onFire = function(e, isDrop){
22696        
22697         if(isDrop || !ddm.dragCurrent){ return; }
22698         var dds = Roo.dd.ScrollManager;
22699         if(!dragEl || dragEl != ddm.dragCurrent){
22700             dragEl = ddm.dragCurrent;
22701             // refresh regions on drag start
22702             dds.refreshCache();
22703         }
22704         
22705         var xy = Roo.lib.Event.getXY(e);
22706         var pt = new Roo.lib.Point(xy[0], xy[1]);
22707         for(var id in els){
22708             var el = els[id], r = el._region;
22709             if(r && r.contains(pt) && el.isScrollable()){
22710                 if(r.bottom - pt.y <= dds.thresh){
22711                     if(proc.el != el){
22712                         startProc(el, "down");
22713                     }
22714                     return;
22715                 }else if(r.right - pt.x <= dds.thresh){
22716                     if(proc.el != el){
22717                         startProc(el, "left");
22718                     }
22719                     return;
22720                 }else if(pt.y - r.top <= dds.thresh){
22721                     if(proc.el != el){
22722                         startProc(el, "up");
22723                     }
22724                     return;
22725                 }else if(pt.x - r.left <= dds.thresh){
22726                     if(proc.el != el){
22727                         startProc(el, "right");
22728                     }
22729                     return;
22730                 }
22731             }
22732         }
22733         clearProc();
22734     };
22735     
22736     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22737     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22738     
22739     return {
22740         /**
22741          * Registers new overflow element(s) to auto scroll
22742          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22743          */
22744         register : function(el){
22745             if(el instanceof Array){
22746                 for(var i = 0, len = el.length; i < len; i++) {
22747                         this.register(el[i]);
22748                 }
22749             }else{
22750                 el = Roo.get(el);
22751                 els[el.id] = el;
22752             }
22753             Roo.dd.ScrollManager.els = els;
22754         },
22755         
22756         /**
22757          * Unregisters overflow element(s) so they are no longer scrolled
22758          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22759          */
22760         unregister : function(el){
22761             if(el instanceof Array){
22762                 for(var i = 0, len = el.length; i < len; i++) {
22763                         this.unregister(el[i]);
22764                 }
22765             }else{
22766                 el = Roo.get(el);
22767                 delete els[el.id];
22768             }
22769         },
22770         
22771         /**
22772          * The number of pixels from the edge of a container the pointer needs to be to 
22773          * trigger scrolling (defaults to 25)
22774          * @type Number
22775          */
22776         thresh : 25,
22777         
22778         /**
22779          * The number of pixels to scroll in each scroll increment (defaults to 50)
22780          * @type Number
22781          */
22782         increment : 100,
22783         
22784         /**
22785          * The frequency of scrolls in milliseconds (defaults to 500)
22786          * @type Number
22787          */
22788         frequency : 500,
22789         
22790         /**
22791          * True to animate the scroll (defaults to true)
22792          * @type Boolean
22793          */
22794         animate: true,
22795         
22796         /**
22797          * The animation duration in seconds - 
22798          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22799          * @type Number
22800          */
22801         animDuration: .4,
22802         
22803         /**
22804          * Manually trigger a cache refresh.
22805          */
22806         refreshCache : function(){
22807             for(var id in els){
22808                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22809                     els[id]._region = els[id].getRegion();
22810                 }
22811             }
22812         }
22813     };
22814 }();/*
22815  * Based on:
22816  * Ext JS Library 1.1.1
22817  * Copyright(c) 2006-2007, Ext JS, LLC.
22818  *
22819  * Originally Released Under LGPL - original licence link has changed is not relivant.
22820  *
22821  * Fork - LGPL
22822  * <script type="text/javascript">
22823  */
22824  
22825
22826 /**
22827  * @class Roo.dd.Registry
22828  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22829  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22830  * @static
22831  */
22832 Roo.dd.Registry = function(){
22833     var elements = {}; 
22834     var handles = {}; 
22835     var autoIdSeed = 0;
22836
22837     var getId = function(el, autogen){
22838         if(typeof el == "string"){
22839             return el;
22840         }
22841         var id = el.id;
22842         if(!id && autogen !== false){
22843             id = "roodd-" + (++autoIdSeed);
22844             el.id = id;
22845         }
22846         return id;
22847     };
22848     
22849     return {
22850     /**
22851      * Register a drag drop element
22852      * @param {String|HTMLElement} element The id or DOM node to register
22853      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22854      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22855      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22856      * populated in the data object (if applicable):
22857      * <pre>
22858 Value      Description<br />
22859 ---------  ------------------------------------------<br />
22860 handles    Array of DOM nodes that trigger dragging<br />
22861            for the element being registered<br />
22862 isHandle   True if the element passed in triggers<br />
22863            dragging itself, else false
22864 </pre>
22865      */
22866         register : function(el, data){
22867             data = data || {};
22868             if(typeof el == "string"){
22869                 el = document.getElementById(el);
22870             }
22871             data.ddel = el;
22872             elements[getId(el)] = data;
22873             if(data.isHandle !== false){
22874                 handles[data.ddel.id] = data;
22875             }
22876             if(data.handles){
22877                 var hs = data.handles;
22878                 for(var i = 0, len = hs.length; i < len; i++){
22879                         handles[getId(hs[i])] = data;
22880                 }
22881             }
22882         },
22883
22884     /**
22885      * Unregister a drag drop element
22886      * @param {String|HTMLElement}  element The id or DOM node to unregister
22887      */
22888         unregister : function(el){
22889             var id = getId(el, false);
22890             var data = elements[id];
22891             if(data){
22892                 delete elements[id];
22893                 if(data.handles){
22894                     var hs = data.handles;
22895                     for(var i = 0, len = hs.length; i < len; i++){
22896                         delete handles[getId(hs[i], false)];
22897                     }
22898                 }
22899             }
22900         },
22901
22902     /**
22903      * Returns the handle registered for a DOM Node by id
22904      * @param {String|HTMLElement} id The DOM node or id to look up
22905      * @return {Object} handle The custom handle data
22906      */
22907         getHandle : function(id){
22908             if(typeof id != "string"){ // must be element?
22909                 id = id.id;
22910             }
22911             return handles[id];
22912         },
22913
22914     /**
22915      * Returns the handle that is registered for the DOM node that is the target of the event
22916      * @param {Event} e The event
22917      * @return {Object} handle The custom handle data
22918      */
22919         getHandleFromEvent : function(e){
22920             var t = Roo.lib.Event.getTarget(e);
22921             return t ? handles[t.id] : null;
22922         },
22923
22924     /**
22925      * Returns a custom data object that is registered for a DOM node by id
22926      * @param {String|HTMLElement} id The DOM node or id to look up
22927      * @return {Object} data The custom data
22928      */
22929         getTarget : function(id){
22930             if(typeof id != "string"){ // must be element?
22931                 id = id.id;
22932             }
22933             return elements[id];
22934         },
22935
22936     /**
22937      * Returns a custom data object that is registered for the DOM node that is the target of the event
22938      * @param {Event} e The event
22939      * @return {Object} data The custom data
22940      */
22941         getTargetFromEvent : function(e){
22942             var t = Roo.lib.Event.getTarget(e);
22943             return t ? elements[t.id] || handles[t.id] : null;
22944         }
22945     };
22946 }();/*
22947  * Based on:
22948  * Ext JS Library 1.1.1
22949  * Copyright(c) 2006-2007, Ext JS, LLC.
22950  *
22951  * Originally Released Under LGPL - original licence link has changed is not relivant.
22952  *
22953  * Fork - LGPL
22954  * <script type="text/javascript">
22955  */
22956  
22957
22958 /**
22959  * @class Roo.dd.StatusProxy
22960  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22961  * default drag proxy used by all Roo.dd components.
22962  * @constructor
22963  * @param {Object} config
22964  */
22965 Roo.dd.StatusProxy = function(config){
22966     Roo.apply(this, config);
22967     this.id = this.id || Roo.id();
22968     this.el = new Roo.Layer({
22969         dh: {
22970             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22971                 {tag: "div", cls: "x-dd-drop-icon"},
22972                 {tag: "div", cls: "x-dd-drag-ghost"}
22973             ]
22974         }, 
22975         shadow: !config || config.shadow !== false
22976     });
22977     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22978     this.dropStatus = this.dropNotAllowed;
22979 };
22980
22981 Roo.dd.StatusProxy.prototype = {
22982     /**
22983      * @cfg {String} dropAllowed
22984      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22985      */
22986     dropAllowed : "x-dd-drop-ok",
22987     /**
22988      * @cfg {String} dropNotAllowed
22989      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22990      */
22991     dropNotAllowed : "x-dd-drop-nodrop",
22992
22993     /**
22994      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22995      * over the current target element.
22996      * @param {String} cssClass The css class for the new drop status indicator image
22997      */
22998     setStatus : function(cssClass){
22999         cssClass = cssClass || this.dropNotAllowed;
23000         if(this.dropStatus != cssClass){
23001             this.el.replaceClass(this.dropStatus, cssClass);
23002             this.dropStatus = cssClass;
23003         }
23004     },
23005
23006     /**
23007      * Resets the status indicator to the default dropNotAllowed value
23008      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23009      */
23010     reset : function(clearGhost){
23011         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23012         this.dropStatus = this.dropNotAllowed;
23013         if(clearGhost){
23014             this.ghost.update("");
23015         }
23016     },
23017
23018     /**
23019      * Updates the contents of the ghost element
23020      * @param {String} html The html that will replace the current innerHTML of the ghost element
23021      */
23022     update : function(html){
23023         if(typeof html == "string"){
23024             this.ghost.update(html);
23025         }else{
23026             this.ghost.update("");
23027             html.style.margin = "0";
23028             this.ghost.dom.appendChild(html);
23029         }
23030         // ensure float = none set?? cant remember why though.
23031         var el = this.ghost.dom.firstChild;
23032                 if(el){
23033                         Roo.fly(el).setStyle('float', 'none');
23034                 }
23035     },
23036     
23037     /**
23038      * Returns the underlying proxy {@link Roo.Layer}
23039      * @return {Roo.Layer} el
23040     */
23041     getEl : function(){
23042         return this.el;
23043     },
23044
23045     /**
23046      * Returns the ghost element
23047      * @return {Roo.Element} el
23048      */
23049     getGhost : function(){
23050         return this.ghost;
23051     },
23052
23053     /**
23054      * Hides the proxy
23055      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23056      */
23057     hide : function(clear){
23058         this.el.hide();
23059         if(clear){
23060             this.reset(true);
23061         }
23062     },
23063
23064     /**
23065      * Stops the repair animation if it's currently running
23066      */
23067     stop : function(){
23068         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23069             this.anim.stop();
23070         }
23071     },
23072
23073     /**
23074      * Displays this proxy
23075      */
23076     show : function(){
23077         this.el.show();
23078     },
23079
23080     /**
23081      * Force the Layer to sync its shadow and shim positions to the element
23082      */
23083     sync : function(){
23084         this.el.sync();
23085     },
23086
23087     /**
23088      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23089      * invalid drop operation by the item being dragged.
23090      * @param {Array} xy The XY position of the element ([x, y])
23091      * @param {Function} callback The function to call after the repair is complete
23092      * @param {Object} scope The scope in which to execute the callback
23093      */
23094     repair : function(xy, callback, scope){
23095         this.callback = callback;
23096         this.scope = scope;
23097         if(xy && this.animRepair !== false){
23098             this.el.addClass("x-dd-drag-repair");
23099             this.el.hideUnders(true);
23100             this.anim = this.el.shift({
23101                 duration: this.repairDuration || .5,
23102                 easing: 'easeOut',
23103                 xy: xy,
23104                 stopFx: true,
23105                 callback: this.afterRepair,
23106                 scope: this
23107             });
23108         }else{
23109             this.afterRepair();
23110         }
23111     },
23112
23113     // private
23114     afterRepair : function(){
23115         this.hide(true);
23116         if(typeof this.callback == "function"){
23117             this.callback.call(this.scope || this);
23118         }
23119         this.callback = null;
23120         this.scope = null;
23121     }
23122 };/*
23123  * Based on:
23124  * Ext JS Library 1.1.1
23125  * Copyright(c) 2006-2007, Ext JS, LLC.
23126  *
23127  * Originally Released Under LGPL - original licence link has changed is not relivant.
23128  *
23129  * Fork - LGPL
23130  * <script type="text/javascript">
23131  */
23132
23133 /**
23134  * @class Roo.dd.DragSource
23135  * @extends Roo.dd.DDProxy
23136  * A simple class that provides the basic implementation needed to make any element draggable.
23137  * @constructor
23138  * @param {String/HTMLElement/Element} el The container element
23139  * @param {Object} config
23140  */
23141 Roo.dd.DragSource = function(el, config){
23142     this.el = Roo.get(el);
23143     this.dragData = {};
23144     
23145     Roo.apply(this, config);
23146     
23147     if(!this.proxy){
23148         this.proxy = new Roo.dd.StatusProxy();
23149     }
23150
23151     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23152           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23153     
23154     this.dragging = false;
23155 };
23156
23157 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23158     /**
23159      * @cfg {String} dropAllowed
23160      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23161      */
23162     dropAllowed : "x-dd-drop-ok",
23163     /**
23164      * @cfg {String} dropNotAllowed
23165      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23166      */
23167     dropNotAllowed : "x-dd-drop-nodrop",
23168
23169     /**
23170      * Returns the data object associated with this drag source
23171      * @return {Object} data An object containing arbitrary data
23172      */
23173     getDragData : function(e){
23174         return this.dragData;
23175     },
23176
23177     // private
23178     onDragEnter : function(e, id){
23179         var target = Roo.dd.DragDropMgr.getDDById(id);
23180         this.cachedTarget = target;
23181         if(this.beforeDragEnter(target, e, id) !== false){
23182             if(target.isNotifyTarget){
23183                 var status = target.notifyEnter(this, e, this.dragData);
23184                 this.proxy.setStatus(status);
23185             }else{
23186                 this.proxy.setStatus(this.dropAllowed);
23187             }
23188             
23189             if(this.afterDragEnter){
23190                 /**
23191                  * An empty function by default, but provided so that you can perform a custom action
23192                  * when the dragged item enters the drop target by providing an implementation.
23193                  * @param {Roo.dd.DragDrop} target The drop target
23194                  * @param {Event} e The event object
23195                  * @param {String} id The id of the dragged element
23196                  * @method afterDragEnter
23197                  */
23198                 this.afterDragEnter(target, e, id);
23199             }
23200         }
23201     },
23202
23203     /**
23204      * An empty function by default, but provided so that you can perform a custom action
23205      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23206      * @param {Roo.dd.DragDrop} target The drop target
23207      * @param {Event} e The event object
23208      * @param {String} id The id of the dragged element
23209      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23210      */
23211     beforeDragEnter : function(target, e, id){
23212         return true;
23213     },
23214
23215     // private
23216     alignElWithMouse: function() {
23217         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23218         this.proxy.sync();
23219     },
23220
23221     // private
23222     onDragOver : function(e, id){
23223         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23224         if(this.beforeDragOver(target, e, id) !== false){
23225             if(target.isNotifyTarget){
23226                 var status = target.notifyOver(this, e, this.dragData);
23227                 this.proxy.setStatus(status);
23228             }
23229
23230             if(this.afterDragOver){
23231                 /**
23232                  * An empty function by default, but provided so that you can perform a custom action
23233                  * while the dragged item is over the drop target by providing an implementation.
23234                  * @param {Roo.dd.DragDrop} target The drop target
23235                  * @param {Event} e The event object
23236                  * @param {String} id The id of the dragged element
23237                  * @method afterDragOver
23238                  */
23239                 this.afterDragOver(target, e, id);
23240             }
23241         }
23242     },
23243
23244     /**
23245      * An empty function by default, but provided so that you can perform a custom action
23246      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23247      * @param {Roo.dd.DragDrop} target The drop target
23248      * @param {Event} e The event object
23249      * @param {String} id The id of the dragged element
23250      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23251      */
23252     beforeDragOver : function(target, e, id){
23253         return true;
23254     },
23255
23256     // private
23257     onDragOut : function(e, id){
23258         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23259         if(this.beforeDragOut(target, e, id) !== false){
23260             if(target.isNotifyTarget){
23261                 target.notifyOut(this, e, this.dragData);
23262             }
23263             this.proxy.reset();
23264             if(this.afterDragOut){
23265                 /**
23266                  * An empty function by default, but provided so that you can perform a custom action
23267                  * after the dragged item is dragged out of the target without dropping.
23268                  * @param {Roo.dd.DragDrop} target The drop target
23269                  * @param {Event} e The event object
23270                  * @param {String} id The id of the dragged element
23271                  * @method afterDragOut
23272                  */
23273                 this.afterDragOut(target, e, id);
23274             }
23275         }
23276         this.cachedTarget = null;
23277     },
23278
23279     /**
23280      * An empty function by default, but provided so that you can perform a custom action before the dragged
23281      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23282      * @param {Roo.dd.DragDrop} target The drop target
23283      * @param {Event} e The event object
23284      * @param {String} id The id of the dragged element
23285      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23286      */
23287     beforeDragOut : function(target, e, id){
23288         return true;
23289     },
23290     
23291     // private
23292     onDragDrop : function(e, id){
23293         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23294         if(this.beforeDragDrop(target, e, id) !== false){
23295             if(target.isNotifyTarget){
23296                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23297                     this.onValidDrop(target, e, id);
23298                 }else{
23299                     this.onInvalidDrop(target, e, id);
23300                 }
23301             }else{
23302                 this.onValidDrop(target, e, id);
23303             }
23304             
23305             if(this.afterDragDrop){
23306                 /**
23307                  * An empty function by default, but provided so that you can perform a custom action
23308                  * after a valid drag drop has occurred by providing an implementation.
23309                  * @param {Roo.dd.DragDrop} target The drop target
23310                  * @param {Event} e The event object
23311                  * @param {String} id The id of the dropped element
23312                  * @method afterDragDrop
23313                  */
23314                 this.afterDragDrop(target, e, id);
23315             }
23316         }
23317         delete this.cachedTarget;
23318     },
23319
23320     /**
23321      * An empty function by default, but provided so that you can perform a custom action before the dragged
23322      * item is dropped onto the target and optionally cancel the onDragDrop.
23323      * @param {Roo.dd.DragDrop} target The drop target
23324      * @param {Event} e The event object
23325      * @param {String} id The id of the dragged element
23326      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23327      */
23328     beforeDragDrop : function(target, e, id){
23329         return true;
23330     },
23331
23332     // private
23333     onValidDrop : function(target, e, id){
23334         this.hideProxy();
23335         if(this.afterValidDrop){
23336             /**
23337              * An empty function by default, but provided so that you can perform a custom action
23338              * after a valid drop has occurred by providing an implementation.
23339              * @param {Object} target The target DD 
23340              * @param {Event} e The event object
23341              * @param {String} id The id of the dropped element
23342              * @method afterInvalidDrop
23343              */
23344             this.afterValidDrop(target, e, id);
23345         }
23346     },
23347
23348     // private
23349     getRepairXY : function(e, data){
23350         return this.el.getXY();  
23351     },
23352
23353     // private
23354     onInvalidDrop : function(target, e, id){
23355         this.beforeInvalidDrop(target, e, id);
23356         if(this.cachedTarget){
23357             if(this.cachedTarget.isNotifyTarget){
23358                 this.cachedTarget.notifyOut(this, e, this.dragData);
23359             }
23360             this.cacheTarget = null;
23361         }
23362         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23363
23364         if(this.afterInvalidDrop){
23365             /**
23366              * An empty function by default, but provided so that you can perform a custom action
23367              * after an invalid drop has occurred by providing an implementation.
23368              * @param {Event} e The event object
23369              * @param {String} id The id of the dropped element
23370              * @method afterInvalidDrop
23371              */
23372             this.afterInvalidDrop(e, id);
23373         }
23374     },
23375
23376     // private
23377     afterRepair : function(){
23378         if(Roo.enableFx){
23379             this.el.highlight(this.hlColor || "c3daf9");
23380         }
23381         this.dragging = false;
23382     },
23383
23384     /**
23385      * An empty function by default, but provided so that you can perform a custom action after an invalid
23386      * drop has occurred.
23387      * @param {Roo.dd.DragDrop} target The drop target
23388      * @param {Event} e The event object
23389      * @param {String} id The id of the dragged element
23390      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23391      */
23392     beforeInvalidDrop : function(target, e, id){
23393         return true;
23394     },
23395
23396     // private
23397     handleMouseDown : function(e){
23398         if(this.dragging) {
23399             return;
23400         }
23401         var data = this.getDragData(e);
23402         if(data && this.onBeforeDrag(data, e) !== false){
23403             this.dragData = data;
23404             this.proxy.stop();
23405             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23406         } 
23407     },
23408
23409     /**
23410      * An empty function by default, but provided so that you can perform a custom action before the initial
23411      * drag event begins and optionally cancel it.
23412      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23413      * @param {Event} e The event object
23414      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23415      */
23416     onBeforeDrag : function(data, e){
23417         return true;
23418     },
23419
23420     /**
23421      * An empty function by default, but provided so that you can perform a custom action once the initial
23422      * drag event has begun.  The drag cannot be canceled from this function.
23423      * @param {Number} x The x position of the click on the dragged object
23424      * @param {Number} y The y position of the click on the dragged object
23425      */
23426     onStartDrag : Roo.emptyFn,
23427
23428     // private - YUI override
23429     startDrag : function(x, y){
23430         this.proxy.reset();
23431         this.dragging = true;
23432         this.proxy.update("");
23433         this.onInitDrag(x, y);
23434         this.proxy.show();
23435     },
23436
23437     // private
23438     onInitDrag : function(x, y){
23439         var clone = this.el.dom.cloneNode(true);
23440         clone.id = Roo.id(); // prevent duplicate ids
23441         this.proxy.update(clone);
23442         this.onStartDrag(x, y);
23443         return true;
23444     },
23445
23446     /**
23447      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23448      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23449      */
23450     getProxy : function(){
23451         return this.proxy;  
23452     },
23453
23454     /**
23455      * Hides the drag source's {@link Roo.dd.StatusProxy}
23456      */
23457     hideProxy : function(){
23458         this.proxy.hide();  
23459         this.proxy.reset(true);
23460         this.dragging = false;
23461     },
23462
23463     // private
23464     triggerCacheRefresh : function(){
23465         Roo.dd.DDM.refreshCache(this.groups);
23466     },
23467
23468     // private - override to prevent hiding
23469     b4EndDrag: function(e) {
23470     },
23471
23472     // private - override to prevent moving
23473     endDrag : function(e){
23474         this.onEndDrag(this.dragData, e);
23475     },
23476
23477     // private
23478     onEndDrag : function(data, e){
23479     },
23480     
23481     // private - pin to cursor
23482     autoOffset : function(x, y) {
23483         this.setDelta(-12, -20);
23484     }    
23485 });/*
23486  * Based on:
23487  * Ext JS Library 1.1.1
23488  * Copyright(c) 2006-2007, Ext JS, LLC.
23489  *
23490  * Originally Released Under LGPL - original licence link has changed is not relivant.
23491  *
23492  * Fork - LGPL
23493  * <script type="text/javascript">
23494  */
23495
23496
23497 /**
23498  * @class Roo.dd.DropTarget
23499  * @extends Roo.dd.DDTarget
23500  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23501  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23502  * @constructor
23503  * @param {String/HTMLElement/Element} el The container element
23504  * @param {Object} config
23505  */
23506 Roo.dd.DropTarget = function(el, config){
23507     this.el = Roo.get(el);
23508     
23509     var listeners = false; ;
23510     if (config && config.listeners) {
23511         listeners= config.listeners;
23512         delete config.listeners;
23513     }
23514     Roo.apply(this, config);
23515     
23516     if(this.containerScroll){
23517         Roo.dd.ScrollManager.register(this.el);
23518     }
23519     this.addEvents( {
23520          /**
23521          * @scope Roo.dd.DropTarget
23522          */
23523          
23524          /**
23525          * @event enter
23526          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23527          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23528          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23529          * 
23530          * IMPORTANT : it should set  this.valid to true|false
23531          * 
23532          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23533          * @param {Event} e The event
23534          * @param {Object} data An object containing arbitrary data supplied by the drag source
23535          */
23536         "enter" : true,
23537         
23538          /**
23539          * @event over
23540          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23541          * This method will be called on every mouse movement while the drag source is over the drop target.
23542          * This default implementation simply returns the dropAllowed config value.
23543          * 
23544          * IMPORTANT : it should set  this.valid to true|false
23545          * 
23546          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23547          * @param {Event} e The event
23548          * @param {Object} data An object containing arbitrary data supplied by the drag source
23549          
23550          */
23551         "over" : true,
23552         /**
23553          * @event out
23554          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23555          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23556          * overClass (if any) from the drop element.
23557          * 
23558          * 
23559          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23560          * @param {Event} e The event
23561          * @param {Object} data An object containing arbitrary data supplied by the drag source
23562          */
23563          "out" : true,
23564          
23565         /**
23566          * @event drop
23567          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23568          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23569          * implementation that does something to process the drop event and returns true so that the drag source's
23570          * repair action does not run.
23571          * 
23572          * IMPORTANT : it should set this.success
23573          * 
23574          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23575          * @param {Event} e The event
23576          * @param {Object} data An object containing arbitrary data supplied by the drag source
23577         */
23578          "drop" : true
23579     });
23580             
23581      
23582     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23583         this.el.dom, 
23584         this.ddGroup || this.group,
23585         {
23586             isTarget: true,
23587             listeners : listeners || {} 
23588            
23589         
23590         }
23591     );
23592
23593 };
23594
23595 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23596     /**
23597      * @cfg {String} overClass
23598      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23599      */
23600      /**
23601      * @cfg {String} ddGroup
23602      * The drag drop group to handle drop events for
23603      */
23604      
23605     /**
23606      * @cfg {String} dropAllowed
23607      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23608      */
23609     dropAllowed : "x-dd-drop-ok",
23610     /**
23611      * @cfg {String} dropNotAllowed
23612      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23613      */
23614     dropNotAllowed : "x-dd-drop-nodrop",
23615     /**
23616      * @cfg {boolean} success
23617      * set this after drop listener.. 
23618      */
23619     success : false,
23620     /**
23621      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23622      * if the drop point is valid for over/enter..
23623      */
23624     valid : false,
23625     // private
23626     isTarget : true,
23627
23628     // private
23629     isNotifyTarget : true,
23630     
23631     /**
23632      * @hide
23633      */
23634     notifyEnter : function(dd, e, data)
23635     {
23636         this.valid = true;
23637         this.fireEvent('enter', dd, e, data);
23638         if(this.overClass){
23639             this.el.addClass(this.overClass);
23640         }
23641         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23642             this.valid ? this.dropAllowed : this.dropNotAllowed
23643         );
23644     },
23645
23646     /**
23647      * @hide
23648      */
23649     notifyOver : function(dd, e, data)
23650     {
23651         this.valid = true;
23652         this.fireEvent('over', dd, e, data);
23653         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23654             this.valid ? this.dropAllowed : this.dropNotAllowed
23655         );
23656     },
23657
23658     /**
23659      * @hide
23660      */
23661     notifyOut : function(dd, e, data)
23662     {
23663         this.fireEvent('out', dd, e, data);
23664         if(this.overClass){
23665             this.el.removeClass(this.overClass);
23666         }
23667     },
23668
23669     /**
23670      * @hide
23671      */
23672     notifyDrop : function(dd, e, data)
23673     {
23674         this.success = false;
23675         this.fireEvent('drop', dd, e, data);
23676         return this.success;
23677     }
23678 });/*
23679  * Based on:
23680  * Ext JS Library 1.1.1
23681  * Copyright(c) 2006-2007, Ext JS, LLC.
23682  *
23683  * Originally Released Under LGPL - original licence link has changed is not relivant.
23684  *
23685  * Fork - LGPL
23686  * <script type="text/javascript">
23687  */
23688
23689
23690 /**
23691  * @class Roo.dd.DragZone
23692  * @extends Roo.dd.DragSource
23693  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23694  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23695  * @constructor
23696  * @param {String/HTMLElement/Element} el The container element
23697  * @param {Object} config
23698  */
23699 Roo.dd.DragZone = function(el, config){
23700     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23701     if(this.containerScroll){
23702         Roo.dd.ScrollManager.register(this.el);
23703     }
23704 };
23705
23706 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23707     /**
23708      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23709      * for auto scrolling during drag operations.
23710      */
23711     /**
23712      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23713      * method after a failed drop (defaults to "c3daf9" - light blue)
23714      */
23715
23716     /**
23717      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23718      * for a valid target to drag based on the mouse down. Override this method
23719      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23720      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23721      * @param {EventObject} e The mouse down event
23722      * @return {Object} The dragData
23723      */
23724     getDragData : function(e){
23725         return Roo.dd.Registry.getHandleFromEvent(e);
23726     },
23727     
23728     /**
23729      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23730      * this.dragData.ddel
23731      * @param {Number} x The x position of the click on the dragged object
23732      * @param {Number} y The y position of the click on the dragged object
23733      * @return {Boolean} true to continue the drag, false to cancel
23734      */
23735     onInitDrag : function(x, y){
23736         this.proxy.update(this.dragData.ddel.cloneNode(true));
23737         this.onStartDrag(x, y);
23738         return true;
23739     },
23740     
23741     /**
23742      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23743      */
23744     afterRepair : function(){
23745         if(Roo.enableFx){
23746             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23747         }
23748         this.dragging = false;
23749     },
23750
23751     /**
23752      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23753      * the XY of this.dragData.ddel
23754      * @param {EventObject} e The mouse up event
23755      * @return {Array} The xy location (e.g. [100, 200])
23756      */
23757     getRepairXY : function(e){
23758         return Roo.Element.fly(this.dragData.ddel).getXY();  
23759     }
23760 });/*
23761  * Based on:
23762  * Ext JS Library 1.1.1
23763  * Copyright(c) 2006-2007, Ext JS, LLC.
23764  *
23765  * Originally Released Under LGPL - original licence link has changed is not relivant.
23766  *
23767  * Fork - LGPL
23768  * <script type="text/javascript">
23769  */
23770 /**
23771  * @class Roo.dd.DropZone
23772  * @extends Roo.dd.DropTarget
23773  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23774  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23775  * @constructor
23776  * @param {String/HTMLElement/Element} el The container element
23777  * @param {Object} config
23778  */
23779 Roo.dd.DropZone = function(el, config){
23780     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23781 };
23782
23783 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23784     /**
23785      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23786      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23787      * provide your own custom lookup.
23788      * @param {Event} e The event
23789      * @return {Object} data The custom data
23790      */
23791     getTargetFromEvent : function(e){
23792         return Roo.dd.Registry.getTargetFromEvent(e);
23793     },
23794
23795     /**
23796      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23797      * that it has registered.  This method has no default implementation and should be overridden to provide
23798      * node-specific processing if necessary.
23799      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23800      * {@link #getTargetFromEvent} for this node)
23801      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23802      * @param {Event} e The event
23803      * @param {Object} data An object containing arbitrary data supplied by the drag source
23804      */
23805     onNodeEnter : function(n, dd, e, data){
23806         
23807     },
23808
23809     /**
23810      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23811      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23812      * overridden to provide the proper feedback.
23813      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23814      * {@link #getTargetFromEvent} for this node)
23815      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23816      * @param {Event} e The event
23817      * @param {Object} data An object containing arbitrary data supplied by the drag source
23818      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23819      * underlying {@link Roo.dd.StatusProxy} can be updated
23820      */
23821     onNodeOver : function(n, dd, e, data){
23822         return this.dropAllowed;
23823     },
23824
23825     /**
23826      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23827      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23828      * node-specific processing if necessary.
23829      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23830      * {@link #getTargetFromEvent} for this node)
23831      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23832      * @param {Event} e The event
23833      * @param {Object} data An object containing arbitrary data supplied by the drag source
23834      */
23835     onNodeOut : function(n, dd, e, data){
23836         
23837     },
23838
23839     /**
23840      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23841      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23842      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23843      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23844      * {@link #getTargetFromEvent} for this node)
23845      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23846      * @param {Event} e The event
23847      * @param {Object} data An object containing arbitrary data supplied by the drag source
23848      * @return {Boolean} True if the drop was valid, else false
23849      */
23850     onNodeDrop : function(n, dd, e, data){
23851         return false;
23852     },
23853
23854     /**
23855      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23856      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23857      * it should be overridden to provide the proper feedback if necessary.
23858      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23859      * @param {Event} e The event
23860      * @param {Object} data An object containing arbitrary data supplied by the drag source
23861      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23862      * underlying {@link Roo.dd.StatusProxy} can be updated
23863      */
23864     onContainerOver : function(dd, e, data){
23865         return this.dropNotAllowed;
23866     },
23867
23868     /**
23869      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23870      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23871      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23872      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23873      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23874      * @param {Event} e The event
23875      * @param {Object} data An object containing arbitrary data supplied by the drag source
23876      * @return {Boolean} True if the drop was valid, else false
23877      */
23878     onContainerDrop : function(dd, e, data){
23879         return false;
23880     },
23881
23882     /**
23883      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23884      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23885      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23886      * you should override this method and provide a custom implementation.
23887      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23888      * @param {Event} e The event
23889      * @param {Object} data An object containing arbitrary data supplied by the drag source
23890      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23891      * underlying {@link Roo.dd.StatusProxy} can be updated
23892      */
23893     notifyEnter : function(dd, e, data){
23894         return this.dropNotAllowed;
23895     },
23896
23897     /**
23898      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23899      * This method will be called on every mouse movement while the drag source is over the drop zone.
23900      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23901      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23902      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23903      * registered node, it will call {@link #onContainerOver}.
23904      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23905      * @param {Event} e The event
23906      * @param {Object} data An object containing arbitrary data supplied by the drag source
23907      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23908      * underlying {@link Roo.dd.StatusProxy} can be updated
23909      */
23910     notifyOver : function(dd, e, data){
23911         var n = this.getTargetFromEvent(e);
23912         if(!n){ // not over valid drop target
23913             if(this.lastOverNode){
23914                 this.onNodeOut(this.lastOverNode, dd, e, data);
23915                 this.lastOverNode = null;
23916             }
23917             return this.onContainerOver(dd, e, data);
23918         }
23919         if(this.lastOverNode != n){
23920             if(this.lastOverNode){
23921                 this.onNodeOut(this.lastOverNode, dd, e, data);
23922             }
23923             this.onNodeEnter(n, dd, e, data);
23924             this.lastOverNode = n;
23925         }
23926         return this.onNodeOver(n, dd, e, data);
23927     },
23928
23929     /**
23930      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23931      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23932      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23933      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23934      * @param {Event} e The event
23935      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23936      */
23937     notifyOut : function(dd, e, data){
23938         if(this.lastOverNode){
23939             this.onNodeOut(this.lastOverNode, dd, e, data);
23940             this.lastOverNode = null;
23941         }
23942     },
23943
23944     /**
23945      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23946      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23947      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23948      * otherwise it will call {@link #onContainerDrop}.
23949      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23950      * @param {Event} e The event
23951      * @param {Object} data An object containing arbitrary data supplied by the drag source
23952      * @return {Boolean} True if the drop was valid, else false
23953      */
23954     notifyDrop : function(dd, e, data){
23955         if(this.lastOverNode){
23956             this.onNodeOut(this.lastOverNode, dd, e, data);
23957             this.lastOverNode = null;
23958         }
23959         var n = this.getTargetFromEvent(e);
23960         return n ?
23961             this.onNodeDrop(n, dd, e, data) :
23962             this.onContainerDrop(dd, e, data);
23963     },
23964
23965     // private
23966     triggerCacheRefresh : function(){
23967         Roo.dd.DDM.refreshCache(this.groups);
23968     }  
23969 });/*
23970  * Based on:
23971  * Ext JS Library 1.1.1
23972  * Copyright(c) 2006-2007, Ext JS, LLC.
23973  *
23974  * Originally Released Under LGPL - original licence link has changed is not relivant.
23975  *
23976  * Fork - LGPL
23977  * <script type="text/javascript">
23978  */
23979
23980
23981 /**
23982  * @class Roo.data.SortTypes
23983  * @static
23984  * Defines the default sorting (casting?) comparison functions used when sorting data.
23985  */
23986 Roo.data.SortTypes = {
23987     /**
23988      * Default sort that does nothing
23989      * @param {Mixed} s The value being converted
23990      * @return {Mixed} The comparison value
23991      */
23992     none : function(s){
23993         return s;
23994     },
23995     
23996     /**
23997      * The regular expression used to strip tags
23998      * @type {RegExp}
23999      * @property
24000      */
24001     stripTagsRE : /<\/?[^>]+>/gi,
24002     
24003     /**
24004      * Strips all HTML tags to sort on text only
24005      * @param {Mixed} s The value being converted
24006      * @return {String} The comparison value
24007      */
24008     asText : function(s){
24009         return String(s).replace(this.stripTagsRE, "");
24010     },
24011     
24012     /**
24013      * Strips all HTML tags to sort on text only - Case insensitive
24014      * @param {Mixed} s The value being converted
24015      * @return {String} The comparison value
24016      */
24017     asUCText : function(s){
24018         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24019     },
24020     
24021     /**
24022      * Case insensitive string
24023      * @param {Mixed} s The value being converted
24024      * @return {String} The comparison value
24025      */
24026     asUCString : function(s) {
24027         return String(s).toUpperCase();
24028     },
24029     
24030     /**
24031      * Date sorting
24032      * @param {Mixed} s The value being converted
24033      * @return {Number} The comparison value
24034      */
24035     asDate : function(s) {
24036         if(!s){
24037             return 0;
24038         }
24039         if(s instanceof Date){
24040             return s.getTime();
24041         }
24042         return Date.parse(String(s));
24043     },
24044     
24045     /**
24046      * Float sorting
24047      * @param {Mixed} s The value being converted
24048      * @return {Float} The comparison value
24049      */
24050     asFloat : function(s) {
24051         var val = parseFloat(String(s).replace(/,/g, ""));
24052         if(isNaN(val)) {
24053             val = 0;
24054         }
24055         return val;
24056     },
24057     
24058     /**
24059      * Integer sorting
24060      * @param {Mixed} s The value being converted
24061      * @return {Number} The comparison value
24062      */
24063     asInt : function(s) {
24064         var val = parseInt(String(s).replace(/,/g, ""));
24065         if(isNaN(val)) {
24066             val = 0;
24067         }
24068         return val;
24069     }
24070 };/*
24071  * Based on:
24072  * Ext JS Library 1.1.1
24073  * Copyright(c) 2006-2007, Ext JS, LLC.
24074  *
24075  * Originally Released Under LGPL - original licence link has changed is not relivant.
24076  *
24077  * Fork - LGPL
24078  * <script type="text/javascript">
24079  */
24080
24081 /**
24082 * @class Roo.data.Record
24083  * Instances of this class encapsulate both record <em>definition</em> information, and record
24084  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24085  * to access Records cached in an {@link Roo.data.Store} object.<br>
24086  * <p>
24087  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24088  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24089  * objects.<br>
24090  * <p>
24091  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24092  * @constructor
24093  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24094  * {@link #create}. The parameters are the same.
24095  * @param {Array} data An associative Array of data values keyed by the field name.
24096  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24097  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24098  * not specified an integer id is generated.
24099  */
24100 Roo.data.Record = function(data, id){
24101     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24102     this.data = data;
24103 };
24104
24105 /**
24106  * Generate a constructor for a specific record layout.
24107  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24108  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24109  * Each field definition object may contain the following properties: <ul>
24110  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
24111  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24112  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24113  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24114  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24115  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24116  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24117  * this may be omitted.</p></li>
24118  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24119  * <ul><li>auto (Default, implies no conversion)</li>
24120  * <li>string</li>
24121  * <li>int</li>
24122  * <li>float</li>
24123  * <li>boolean</li>
24124  * <li>date</li></ul></p></li>
24125  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24126  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24127  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24128  * by the Reader into an object that will be stored in the Record. It is passed the
24129  * following parameters:<ul>
24130  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24131  * </ul></p></li>
24132  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24133  * </ul>
24134  * <br>usage:<br><pre><code>
24135 var TopicRecord = Roo.data.Record.create(
24136     {name: 'title', mapping: 'topic_title'},
24137     {name: 'author', mapping: 'username'},
24138     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24139     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24140     {name: 'lastPoster', mapping: 'user2'},
24141     {name: 'excerpt', mapping: 'post_text'}
24142 );
24143
24144 var myNewRecord = new TopicRecord({
24145     title: 'Do my job please',
24146     author: 'noobie',
24147     totalPosts: 1,
24148     lastPost: new Date(),
24149     lastPoster: 'Animal',
24150     excerpt: 'No way dude!'
24151 });
24152 myStore.add(myNewRecord);
24153 </code></pre>
24154  * @method create
24155  * @static
24156  */
24157 Roo.data.Record.create = function(o){
24158     var f = function(){
24159         f.superclass.constructor.apply(this, arguments);
24160     };
24161     Roo.extend(f, Roo.data.Record);
24162     var p = f.prototype;
24163     p.fields = new Roo.util.MixedCollection(false, function(field){
24164         return field.name;
24165     });
24166     for(var i = 0, len = o.length; i < len; i++){
24167         p.fields.add(new Roo.data.Field(o[i]));
24168     }
24169     f.getField = function(name){
24170         return p.fields.get(name);  
24171     };
24172     return f;
24173 };
24174
24175 Roo.data.Record.AUTO_ID = 1000;
24176 Roo.data.Record.EDIT = 'edit';
24177 Roo.data.Record.REJECT = 'reject';
24178 Roo.data.Record.COMMIT = 'commit';
24179
24180 Roo.data.Record.prototype = {
24181     /**
24182      * Readonly flag - true if this record has been modified.
24183      * @type Boolean
24184      */
24185     dirty : false,
24186     editing : false,
24187     error: null,
24188     modified: null,
24189
24190     // private
24191     join : function(store){
24192         this.store = store;
24193     },
24194
24195     /**
24196      * Set the named field to the specified value.
24197      * @param {String} name The name of the field to set.
24198      * @param {Object} value The value to set the field to.
24199      */
24200     set : function(name, value){
24201         if(this.data[name] == value){
24202             return;
24203         }
24204         this.dirty = true;
24205         if(!this.modified){
24206             this.modified = {};
24207         }
24208         if(typeof this.modified[name] == 'undefined'){
24209             this.modified[name] = this.data[name];
24210         }
24211         this.data[name] = value;
24212         if(!this.editing && this.store){
24213             this.store.afterEdit(this);
24214         }       
24215     },
24216
24217     /**
24218      * Get the value of the named field.
24219      * @param {String} name The name of the field to get the value of.
24220      * @return {Object} The value of the field.
24221      */
24222     get : function(name){
24223         return this.data[name]; 
24224     },
24225
24226     // private
24227     beginEdit : function(){
24228         this.editing = true;
24229         this.modified = {}; 
24230     },
24231
24232     // private
24233     cancelEdit : function(){
24234         this.editing = false;
24235         delete this.modified;
24236     },
24237
24238     // private
24239     endEdit : function(){
24240         this.editing = false;
24241         if(this.dirty && this.store){
24242             this.store.afterEdit(this);
24243         }
24244     },
24245
24246     /**
24247      * Usually called by the {@link Roo.data.Store} which owns the Record.
24248      * Rejects all changes made to the Record since either creation, or the last commit operation.
24249      * Modified fields are reverted to their original values.
24250      * <p>
24251      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24252      * of reject operations.
24253      */
24254     reject : function(){
24255         var m = this.modified;
24256         for(var n in m){
24257             if(typeof m[n] != "function"){
24258                 this.data[n] = m[n];
24259             }
24260         }
24261         this.dirty = false;
24262         delete this.modified;
24263         this.editing = false;
24264         if(this.store){
24265             this.store.afterReject(this);
24266         }
24267     },
24268
24269     /**
24270      * Usually called by the {@link Roo.data.Store} which owns the Record.
24271      * Commits all changes made to the Record since either creation, or the last commit operation.
24272      * <p>
24273      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24274      * of commit operations.
24275      */
24276     commit : function(){
24277         this.dirty = false;
24278         delete this.modified;
24279         this.editing = false;
24280         if(this.store){
24281             this.store.afterCommit(this);
24282         }
24283     },
24284
24285     // private
24286     hasError : function(){
24287         return this.error != null;
24288     },
24289
24290     // private
24291     clearError : function(){
24292         this.error = null;
24293     },
24294
24295     /**
24296      * Creates a copy of this record.
24297      * @param {String} id (optional) A new record id if you don't want to use this record's id
24298      * @return {Record}
24299      */
24300     copy : function(newId) {
24301         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24302     }
24303 };/*
24304  * Based on:
24305  * Ext JS Library 1.1.1
24306  * Copyright(c) 2006-2007, Ext JS, LLC.
24307  *
24308  * Originally Released Under LGPL - original licence link has changed is not relivant.
24309  *
24310  * Fork - LGPL
24311  * <script type="text/javascript">
24312  */
24313
24314
24315
24316 /**
24317  * @class Roo.data.Store
24318  * @extends Roo.util.Observable
24319  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24320  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24321  * <p>
24322  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
24323  * has no knowledge of the format of the data returned by the Proxy.<br>
24324  * <p>
24325  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24326  * instances from the data object. These records are cached and made available through accessor functions.
24327  * @constructor
24328  * Creates a new Store.
24329  * @param {Object} config A config object containing the objects needed for the Store to access data,
24330  * and read the data into Records.
24331  */
24332 Roo.data.Store = function(config){
24333     this.data = new Roo.util.MixedCollection(false);
24334     this.data.getKey = function(o){
24335         return o.id;
24336     };
24337     this.baseParams = {};
24338     // private
24339     this.paramNames = {
24340         "start" : "start",
24341         "limit" : "limit",
24342         "sort" : "sort",
24343         "dir" : "dir",
24344         "multisort" : "_multisort"
24345     };
24346
24347     if(config && config.data){
24348         this.inlineData = config.data;
24349         delete config.data;
24350     }
24351
24352     Roo.apply(this, config);
24353     
24354     if(this.reader){ // reader passed
24355         this.reader = Roo.factory(this.reader, Roo.data);
24356         this.reader.xmodule = this.xmodule || false;
24357         if(!this.recordType){
24358             this.recordType = this.reader.recordType;
24359         }
24360         if(this.reader.onMetaChange){
24361             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24362         }
24363     }
24364
24365     if(this.recordType){
24366         this.fields = this.recordType.prototype.fields;
24367     }
24368     this.modified = [];
24369
24370     this.addEvents({
24371         /**
24372          * @event datachanged
24373          * Fires when the data cache has changed, and a widget which is using this Store
24374          * as a Record cache should refresh its view.
24375          * @param {Store} this
24376          */
24377         datachanged : true,
24378         /**
24379          * @event metachange
24380          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24381          * @param {Store} this
24382          * @param {Object} meta The JSON metadata
24383          */
24384         metachange : true,
24385         /**
24386          * @event add
24387          * Fires when Records have been added to the Store
24388          * @param {Store} this
24389          * @param {Roo.data.Record[]} records The array of Records added
24390          * @param {Number} index The index at which the record(s) were added
24391          */
24392         add : true,
24393         /**
24394          * @event remove
24395          * Fires when a Record has been removed from the Store
24396          * @param {Store} this
24397          * @param {Roo.data.Record} record The Record that was removed
24398          * @param {Number} index The index at which the record was removed
24399          */
24400         remove : true,
24401         /**
24402          * @event update
24403          * Fires when a Record has been updated
24404          * @param {Store} this
24405          * @param {Roo.data.Record} record The Record that was updated
24406          * @param {String} operation The update operation being performed.  Value may be one of:
24407          * <pre><code>
24408  Roo.data.Record.EDIT
24409  Roo.data.Record.REJECT
24410  Roo.data.Record.COMMIT
24411          * </code></pre>
24412          */
24413         update : true,
24414         /**
24415          * @event clear
24416          * Fires when the data cache has been cleared.
24417          * @param {Store} this
24418          */
24419         clear : true,
24420         /**
24421          * @event beforeload
24422          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24423          * the load action will be canceled.
24424          * @param {Store} this
24425          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24426          */
24427         beforeload : true,
24428         /**
24429          * @event beforeloadadd
24430          * Fires after a new set of Records has been loaded.
24431          * @param {Store} this
24432          * @param {Roo.data.Record[]} records The Records that were loaded
24433          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24434          */
24435         beforeloadadd : true,
24436         /**
24437          * @event load
24438          * Fires after a new set of Records has been loaded, before they are added to the store.
24439          * @param {Store} this
24440          * @param {Roo.data.Record[]} records The Records that were loaded
24441          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24442          * @params {Object} return from reader
24443          */
24444         load : true,
24445         /**
24446          * @event loadexception
24447          * Fires if an exception occurs in the Proxy during loading.
24448          * Called with the signature of the Proxy's "loadexception" event.
24449          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24450          * 
24451          * @param {Proxy} 
24452          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24453          * @param {Object} load options 
24454          * @param {Object} jsonData from your request (normally this contains the Exception)
24455          */
24456         loadexception : true
24457     });
24458     
24459     if(this.proxy){
24460         this.proxy = Roo.factory(this.proxy, Roo.data);
24461         this.proxy.xmodule = this.xmodule || false;
24462         this.relayEvents(this.proxy,  ["loadexception"]);
24463     }
24464     this.sortToggle = {};
24465     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24466
24467     Roo.data.Store.superclass.constructor.call(this);
24468
24469     if(this.inlineData){
24470         this.loadData(this.inlineData);
24471         delete this.inlineData;
24472     }
24473 };
24474
24475 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24476      /**
24477     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24478     * without a remote query - used by combo/forms at present.
24479     */
24480     
24481     /**
24482     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24483     */
24484     /**
24485     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24486     */
24487     /**
24488     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24489     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24490     */
24491     /**
24492     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24493     * on any HTTP request
24494     */
24495     /**
24496     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24497     */
24498     /**
24499     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24500     */
24501     multiSort: false,
24502     /**
24503     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24504     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24505     */
24506     remoteSort : false,
24507
24508     /**
24509     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24510      * loaded or when a record is removed. (defaults to false).
24511     */
24512     pruneModifiedRecords : false,
24513
24514     // private
24515     lastOptions : null,
24516
24517     /**
24518      * Add Records to the Store and fires the add event.
24519      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24520      */
24521     add : function(records){
24522         records = [].concat(records);
24523         for(var i = 0, len = records.length; i < len; i++){
24524             records[i].join(this);
24525         }
24526         var index = this.data.length;
24527         this.data.addAll(records);
24528         this.fireEvent("add", this, records, index);
24529     },
24530
24531     /**
24532      * Remove a Record from the Store and fires the remove event.
24533      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24534      */
24535     remove : function(record){
24536         var index = this.data.indexOf(record);
24537         this.data.removeAt(index);
24538  
24539         if(this.pruneModifiedRecords){
24540             this.modified.remove(record);
24541         }
24542         this.fireEvent("remove", this, record, index);
24543     },
24544
24545     /**
24546      * Remove all Records from the Store and fires the clear event.
24547      */
24548     removeAll : function(){
24549         this.data.clear();
24550         if(this.pruneModifiedRecords){
24551             this.modified = [];
24552         }
24553         this.fireEvent("clear", this);
24554     },
24555
24556     /**
24557      * Inserts Records to the Store at the given index and fires the add event.
24558      * @param {Number} index The start index at which to insert the passed Records.
24559      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24560      */
24561     insert : function(index, records){
24562         records = [].concat(records);
24563         for(var i = 0, len = records.length; i < len; i++){
24564             this.data.insert(index, records[i]);
24565             records[i].join(this);
24566         }
24567         this.fireEvent("add", this, records, index);
24568     },
24569
24570     /**
24571      * Get the index within the cache of the passed Record.
24572      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24573      * @return {Number} The index of the passed Record. Returns -1 if not found.
24574      */
24575     indexOf : function(record){
24576         return this.data.indexOf(record);
24577     },
24578
24579     /**
24580      * Get the index within the cache of the Record with the passed id.
24581      * @param {String} id The id of the Record to find.
24582      * @return {Number} The index of the Record. Returns -1 if not found.
24583      */
24584     indexOfId : function(id){
24585         return this.data.indexOfKey(id);
24586     },
24587
24588     /**
24589      * Get the Record with the specified id.
24590      * @param {String} id The id of the Record to find.
24591      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24592      */
24593     getById : function(id){
24594         return this.data.key(id);
24595     },
24596
24597     /**
24598      * Get the Record at the specified index.
24599      * @param {Number} index The index of the Record to find.
24600      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24601      */
24602     getAt : function(index){
24603         return this.data.itemAt(index);
24604     },
24605
24606     /**
24607      * Returns a range of Records between specified indices.
24608      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24609      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24610      * @return {Roo.data.Record[]} An array of Records
24611      */
24612     getRange : function(start, end){
24613         return this.data.getRange(start, end);
24614     },
24615
24616     // private
24617     storeOptions : function(o){
24618         o = Roo.apply({}, o);
24619         delete o.callback;
24620         delete o.scope;
24621         this.lastOptions = o;
24622     },
24623
24624     /**
24625      * Loads the Record cache from the configured Proxy using the configured Reader.
24626      * <p>
24627      * If using remote paging, then the first load call must specify the <em>start</em>
24628      * and <em>limit</em> properties in the options.params property to establish the initial
24629      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24630      * <p>
24631      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24632      * and this call will return before the new data has been loaded. Perform any post-processing
24633      * in a callback function, or in a "load" event handler.</strong>
24634      * <p>
24635      * @param {Object} options An object containing properties which control loading options:<ul>
24636      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24637      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24638      * passed the following arguments:<ul>
24639      * <li>r : Roo.data.Record[]</li>
24640      * <li>options: Options object from the load call</li>
24641      * <li>success: Boolean success indicator</li></ul></li>
24642      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24643      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24644      * </ul>
24645      */
24646     load : function(options){
24647         options = options || {};
24648         if(this.fireEvent("beforeload", this, options) !== false){
24649             this.storeOptions(options);
24650             var p = Roo.apply(options.params || {}, this.baseParams);
24651             // if meta was not loaded from remote source.. try requesting it.
24652             if (!this.reader.metaFromRemote) {
24653                 p._requestMeta = 1;
24654             }
24655             if(this.sortInfo && this.remoteSort){
24656                 var pn = this.paramNames;
24657                 p[pn["sort"]] = this.sortInfo.field;
24658                 p[pn["dir"]] = this.sortInfo.direction;
24659             }
24660             if (this.multiSort) {
24661                 var pn = this.paramNames;
24662                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24663             }
24664             
24665             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24666         }
24667     },
24668
24669     /**
24670      * Reloads the Record cache from the configured Proxy using the configured Reader and
24671      * the options from the last load operation performed.
24672      * @param {Object} options (optional) An object containing properties which may override the options
24673      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24674      * the most recently used options are reused).
24675      */
24676     reload : function(options){
24677         this.load(Roo.applyIf(options||{}, this.lastOptions));
24678     },
24679
24680     // private
24681     // Called as a callback by the Reader during a load operation.
24682     loadRecords : function(o, options, success){
24683          
24684         if(!o){
24685             if(success !== false){
24686                 this.fireEvent("load", this, [], options, o);
24687             }
24688             if(options.callback){
24689                 options.callback.call(options.scope || this, [], options, false);
24690             }
24691             return;
24692         }
24693         // if data returned failure - throw an exception.
24694         if (o.success === false) {
24695             // show a message if no listener is registered.
24696             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24697                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24698             }
24699             // loadmask wil be hooked into this..
24700             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24701             return;
24702         }
24703         var r = o.records, t = o.totalRecords || r.length;
24704         
24705         this.fireEvent("beforeloadadd", this, r, options, o);
24706         
24707         if(!options || options.add !== true){
24708             if(this.pruneModifiedRecords){
24709                 this.modified = [];
24710             }
24711             for(var i = 0, len = r.length; i < len; i++){
24712                 r[i].join(this);
24713             }
24714             if(this.snapshot){
24715                 this.data = this.snapshot;
24716                 delete this.snapshot;
24717             }
24718             this.data.clear();
24719             this.data.addAll(r);
24720             this.totalLength = t;
24721             this.applySort();
24722             this.fireEvent("datachanged", this);
24723         }else{
24724             this.totalLength = Math.max(t, this.data.length+r.length);
24725             this.add(r);
24726         }
24727         
24728         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24729                 
24730             var e = new Roo.data.Record({});
24731
24732             e.set(this.parent.displayField, this.parent.emptyTitle);
24733             e.set(this.parent.valueField, '');
24734
24735             this.insert(0, e);
24736         }
24737             
24738         this.fireEvent("load", this, r, options, o);
24739         if(options.callback){
24740             options.callback.call(options.scope || this, r, options, true);
24741         }
24742     },
24743
24744
24745     /**
24746      * Loads data from a passed data block. A Reader which understands the format of the data
24747      * must have been configured in the constructor.
24748      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24749      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24750      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24751      */
24752     loadData : function(o, append){
24753         var r = this.reader.readRecords(o);
24754         this.loadRecords(r, {add: append}, true);
24755     },
24756     
24757      /**
24758      * using 'cn' the nested child reader read the child array into it's child stores.
24759      * @param {Object} rec The record with a 'children array
24760      */
24761     loadDataFromChildren : function(rec)
24762     {
24763         this.loadData(this.reader.toLoadData(rec));
24764     },
24765     
24766
24767     /**
24768      * Gets the number of cached records.
24769      * <p>
24770      * <em>If using paging, this may not be the total size of the dataset. If the data object
24771      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24772      * the data set size</em>
24773      */
24774     getCount : function(){
24775         return this.data.length || 0;
24776     },
24777
24778     /**
24779      * Gets the total number of records in the dataset as returned by the server.
24780      * <p>
24781      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24782      * the dataset size</em>
24783      */
24784     getTotalCount : function(){
24785         return this.totalLength || 0;
24786     },
24787
24788     /**
24789      * Returns the sort state of the Store as an object with two properties:
24790      * <pre><code>
24791  field {String} The name of the field by which the Records are sorted
24792  direction {String} The sort order, "ASC" or "DESC"
24793      * </code></pre>
24794      */
24795     getSortState : function(){
24796         return this.sortInfo;
24797     },
24798
24799     // private
24800     applySort : function(){
24801         if(this.sortInfo && !this.remoteSort){
24802             var s = this.sortInfo, f = s.field;
24803             var st = this.fields.get(f).sortType;
24804             var fn = function(r1, r2){
24805                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24806                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24807             };
24808             this.data.sort(s.direction, fn);
24809             if(this.snapshot && this.snapshot != this.data){
24810                 this.snapshot.sort(s.direction, fn);
24811             }
24812         }
24813     },
24814
24815     /**
24816      * Sets the default sort column and order to be used by the next load operation.
24817      * @param {String} fieldName The name of the field to sort by.
24818      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24819      */
24820     setDefaultSort : function(field, dir){
24821         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24822     },
24823
24824     /**
24825      * Sort the Records.
24826      * If remote sorting is used, the sort is performed on the server, and the cache is
24827      * reloaded. If local sorting is used, the cache is sorted internally.
24828      * @param {String} fieldName The name of the field to sort by.
24829      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24830      */
24831     sort : function(fieldName, dir){
24832         var f = this.fields.get(fieldName);
24833         if(!dir){
24834             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24835             
24836             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24837                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24838             }else{
24839                 dir = f.sortDir;
24840             }
24841         }
24842         this.sortToggle[f.name] = dir;
24843         this.sortInfo = {field: f.name, direction: dir};
24844         if(!this.remoteSort){
24845             this.applySort();
24846             this.fireEvent("datachanged", this);
24847         }else{
24848             this.load(this.lastOptions);
24849         }
24850     },
24851
24852     /**
24853      * Calls the specified function for each of the Records in the cache.
24854      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24855      * Returning <em>false</em> aborts and exits the iteration.
24856      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24857      */
24858     each : function(fn, scope){
24859         this.data.each(fn, scope);
24860     },
24861
24862     /**
24863      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24864      * (e.g., during paging).
24865      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24866      */
24867     getModifiedRecords : function(){
24868         return this.modified;
24869     },
24870
24871     // private
24872     createFilterFn : function(property, value, anyMatch){
24873         if(!value.exec){ // not a regex
24874             value = String(value);
24875             if(value.length == 0){
24876                 return false;
24877             }
24878             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24879         }
24880         return function(r){
24881             return value.test(r.data[property]);
24882         };
24883     },
24884
24885     /**
24886      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24887      * @param {String} property A field on your records
24888      * @param {Number} start The record index to start at (defaults to 0)
24889      * @param {Number} end The last record index to include (defaults to length - 1)
24890      * @return {Number} The sum
24891      */
24892     sum : function(property, start, end){
24893         var rs = this.data.items, v = 0;
24894         start = start || 0;
24895         end = (end || end === 0) ? end : rs.length-1;
24896
24897         for(var i = start; i <= end; i++){
24898             v += (rs[i].data[property] || 0);
24899         }
24900         return v;
24901     },
24902
24903     /**
24904      * Filter the records by a specified property.
24905      * @param {String} field A field on your records
24906      * @param {String/RegExp} value Either a string that the field
24907      * should start with or a RegExp to test against the field
24908      * @param {Boolean} anyMatch True to match any part not just the beginning
24909      */
24910     filter : function(property, value, anyMatch){
24911         var fn = this.createFilterFn(property, value, anyMatch);
24912         return fn ? this.filterBy(fn) : this.clearFilter();
24913     },
24914
24915     /**
24916      * Filter by a function. The specified function will be called with each
24917      * record in this data source. If the function returns true the record is included,
24918      * otherwise it is filtered.
24919      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24920      * @param {Object} scope (optional) The scope of the function (defaults to this)
24921      */
24922     filterBy : function(fn, scope){
24923         this.snapshot = this.snapshot || this.data;
24924         this.data = this.queryBy(fn, scope||this);
24925         this.fireEvent("datachanged", this);
24926     },
24927
24928     /**
24929      * Query the records by a specified property.
24930      * @param {String} field A field on your records
24931      * @param {String/RegExp} value Either a string that the field
24932      * should start with or a RegExp to test against the field
24933      * @param {Boolean} anyMatch True to match any part not just the beginning
24934      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24935      */
24936     query : function(property, value, anyMatch){
24937         var fn = this.createFilterFn(property, value, anyMatch);
24938         return fn ? this.queryBy(fn) : this.data.clone();
24939     },
24940
24941     /**
24942      * Query by a function. The specified function will be called with each
24943      * record in this data source. If the function returns true the record is included
24944      * in the results.
24945      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24946      * @param {Object} scope (optional) The scope of the function (defaults to this)
24947       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24948      **/
24949     queryBy : function(fn, scope){
24950         var data = this.snapshot || this.data;
24951         return data.filterBy(fn, scope||this);
24952     },
24953
24954     /**
24955      * Collects unique values for a particular dataIndex from this store.
24956      * @param {String} dataIndex The property to collect
24957      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24958      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24959      * @return {Array} An array of the unique values
24960      **/
24961     collect : function(dataIndex, allowNull, bypassFilter){
24962         var d = (bypassFilter === true && this.snapshot) ?
24963                 this.snapshot.items : this.data.items;
24964         var v, sv, r = [], l = {};
24965         for(var i = 0, len = d.length; i < len; i++){
24966             v = d[i].data[dataIndex];
24967             sv = String(v);
24968             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24969                 l[sv] = true;
24970                 r[r.length] = v;
24971             }
24972         }
24973         return r;
24974     },
24975
24976     /**
24977      * Revert to a view of the Record cache with no filtering applied.
24978      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24979      */
24980     clearFilter : function(suppressEvent){
24981         if(this.snapshot && this.snapshot != this.data){
24982             this.data = this.snapshot;
24983             delete this.snapshot;
24984             if(suppressEvent !== true){
24985                 this.fireEvent("datachanged", this);
24986             }
24987         }
24988     },
24989
24990     // private
24991     afterEdit : function(record){
24992         if(this.modified.indexOf(record) == -1){
24993             this.modified.push(record);
24994         }
24995         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24996     },
24997     
24998     // private
24999     afterReject : function(record){
25000         this.modified.remove(record);
25001         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25002     },
25003
25004     // private
25005     afterCommit : function(record){
25006         this.modified.remove(record);
25007         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25008     },
25009
25010     /**
25011      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25012      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25013      */
25014     commitChanges : function(){
25015         var m = this.modified.slice(0);
25016         this.modified = [];
25017         for(var i = 0, len = m.length; i < len; i++){
25018             m[i].commit();
25019         }
25020     },
25021
25022     /**
25023      * Cancel outstanding changes on all changed records.
25024      */
25025     rejectChanges : function(){
25026         var m = this.modified.slice(0);
25027         this.modified = [];
25028         for(var i = 0, len = m.length; i < len; i++){
25029             m[i].reject();
25030         }
25031     },
25032
25033     onMetaChange : function(meta, rtype, o){
25034         this.recordType = rtype;
25035         this.fields = rtype.prototype.fields;
25036         delete this.snapshot;
25037         this.sortInfo = meta.sortInfo || this.sortInfo;
25038         this.modified = [];
25039         this.fireEvent('metachange', this, this.reader.meta);
25040     },
25041     
25042     moveIndex : function(data, type)
25043     {
25044         var index = this.indexOf(data);
25045         
25046         var newIndex = index + type;
25047         
25048         this.remove(data);
25049         
25050         this.insert(newIndex, data);
25051         
25052     }
25053 });/*
25054  * Based on:
25055  * Ext JS Library 1.1.1
25056  * Copyright(c) 2006-2007, Ext JS, LLC.
25057  *
25058  * Originally Released Under LGPL - original licence link has changed is not relivant.
25059  *
25060  * Fork - LGPL
25061  * <script type="text/javascript">
25062  */
25063
25064 /**
25065  * @class Roo.data.SimpleStore
25066  * @extends Roo.data.Store
25067  * Small helper class to make creating Stores from Array data easier.
25068  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25069  * @cfg {Array} fields An array of field definition objects, or field name strings.
25070  * @cfg {Object} an existing reader (eg. copied from another store)
25071  * @cfg {Array} data The multi-dimensional array of data
25072  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25073  * @cfg {Roo.data.Reader} reader  [not-required] 
25074  * @constructor
25075  * @param {Object} config
25076  */
25077 Roo.data.SimpleStore = function(config)
25078 {
25079     Roo.data.SimpleStore.superclass.constructor.call(this, {
25080         isLocal : true,
25081         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25082                 id: config.id
25083             },
25084             Roo.data.Record.create(config.fields)
25085         ),
25086         proxy : new Roo.data.MemoryProxy(config.data)
25087     });
25088     this.load();
25089 };
25090 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25091  * Based on:
25092  * Ext JS Library 1.1.1
25093  * Copyright(c) 2006-2007, Ext JS, LLC.
25094  *
25095  * Originally Released Under LGPL - original licence link has changed is not relivant.
25096  *
25097  * Fork - LGPL
25098  * <script type="text/javascript">
25099  */
25100
25101 /**
25102 /**
25103  * @extends Roo.data.Store
25104  * @class Roo.data.JsonStore
25105  * Small helper class to make creating Stores for JSON data easier. <br/>
25106 <pre><code>
25107 var store = new Roo.data.JsonStore({
25108     url: 'get-images.php',
25109     root: 'images',
25110     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25111 });
25112 </code></pre>
25113  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25114  * JsonReader and HttpProxy (unless inline data is provided).</b>
25115  * @cfg {Array} fields An array of field definition objects, or field name strings.
25116  * @constructor
25117  * @param {Object} config
25118  */
25119 Roo.data.JsonStore = function(c){
25120     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25121         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25122         reader: new Roo.data.JsonReader(c, c.fields)
25123     }));
25124 };
25125 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25126  * Based on:
25127  * Ext JS Library 1.1.1
25128  * Copyright(c) 2006-2007, Ext JS, LLC.
25129  *
25130  * Originally Released Under LGPL - original licence link has changed is not relivant.
25131  *
25132  * Fork - LGPL
25133  * <script type="text/javascript">
25134  */
25135
25136  
25137 Roo.data.Field = function(config){
25138     if(typeof config == "string"){
25139         config = {name: config};
25140     }
25141     Roo.apply(this, config);
25142     
25143     if(!this.type){
25144         this.type = "auto";
25145     }
25146     
25147     var st = Roo.data.SortTypes;
25148     // named sortTypes are supported, here we look them up
25149     if(typeof this.sortType == "string"){
25150         this.sortType = st[this.sortType];
25151     }
25152     
25153     // set default sortType for strings and dates
25154     if(!this.sortType){
25155         switch(this.type){
25156             case "string":
25157                 this.sortType = st.asUCString;
25158                 break;
25159             case "date":
25160                 this.sortType = st.asDate;
25161                 break;
25162             default:
25163                 this.sortType = st.none;
25164         }
25165     }
25166
25167     // define once
25168     var stripRe = /[\$,%]/g;
25169
25170     // prebuilt conversion function for this field, instead of
25171     // switching every time we're reading a value
25172     if(!this.convert){
25173         var cv, dateFormat = this.dateFormat;
25174         switch(this.type){
25175             case "":
25176             case "auto":
25177             case undefined:
25178                 cv = function(v){ return v; };
25179                 break;
25180             case "string":
25181                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25182                 break;
25183             case "int":
25184                 cv = function(v){
25185                     return v !== undefined && v !== null && v !== '' ?
25186                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25187                     };
25188                 break;
25189             case "float":
25190                 cv = function(v){
25191                     return v !== undefined && v !== null && v !== '' ?
25192                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25193                     };
25194                 break;
25195             case "bool":
25196             case "boolean":
25197                 cv = function(v){ return v === true || v === "true" || v == 1; };
25198                 break;
25199             case "date":
25200                 cv = function(v){
25201                     if(!v){
25202                         return '';
25203                     }
25204                     if(v instanceof Date){
25205                         return v;
25206                     }
25207                     if(dateFormat){
25208                         if(dateFormat == "timestamp"){
25209                             return new Date(v*1000);
25210                         }
25211                         return Date.parseDate(v, dateFormat);
25212                     }
25213                     var parsed = Date.parse(v);
25214                     return parsed ? new Date(parsed) : null;
25215                 };
25216              break;
25217             
25218         }
25219         this.convert = cv;
25220     }
25221 };
25222
25223 Roo.data.Field.prototype = {
25224     dateFormat: null,
25225     defaultValue: "",
25226     mapping: null,
25227     sortType : null,
25228     sortDir : "ASC"
25229 };/*
25230  * Based on:
25231  * Ext JS Library 1.1.1
25232  * Copyright(c) 2006-2007, Ext JS, LLC.
25233  *
25234  * Originally Released Under LGPL - original licence link has changed is not relivant.
25235  *
25236  * Fork - LGPL
25237  * <script type="text/javascript">
25238  */
25239  
25240 // Base class for reading structured data from a data source.  This class is intended to be
25241 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25242
25243 /**
25244  * @class Roo.data.DataReader
25245  * @abstract
25246  * Base class for reading structured data from a data source.  This class is intended to be
25247  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25248  */
25249
25250 Roo.data.DataReader = function(meta, recordType){
25251     
25252     this.meta = meta;
25253     
25254     this.recordType = recordType instanceof Array ? 
25255         Roo.data.Record.create(recordType) : recordType;
25256 };
25257
25258 Roo.data.DataReader.prototype = {
25259     
25260     
25261     readerType : 'Data',
25262      /**
25263      * Create an empty record
25264      * @param {Object} data (optional) - overlay some values
25265      * @return {Roo.data.Record} record created.
25266      */
25267     newRow :  function(d) {
25268         var da =  {};
25269         this.recordType.prototype.fields.each(function(c) {
25270             switch( c.type) {
25271                 case 'int' : da[c.name] = 0; break;
25272                 case 'date' : da[c.name] = new Date(); break;
25273                 case 'float' : da[c.name] = 0.0; break;
25274                 case 'boolean' : da[c.name] = false; break;
25275                 default : da[c.name] = ""; break;
25276             }
25277             
25278         });
25279         return new this.recordType(Roo.apply(da, d));
25280     }
25281     
25282     
25283 };/*
25284  * Based on:
25285  * Ext JS Library 1.1.1
25286  * Copyright(c) 2006-2007, Ext JS, LLC.
25287  *
25288  * Originally Released Under LGPL - original licence link has changed is not relivant.
25289  *
25290  * Fork - LGPL
25291  * <script type="text/javascript">
25292  */
25293
25294 /**
25295  * @class Roo.data.DataProxy
25296  * @extends Roo.util.Observable
25297  * @abstract
25298  * This class is an abstract base class for implementations which provide retrieval of
25299  * unformatted data objects.<br>
25300  * <p>
25301  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25302  * (of the appropriate type which knows how to parse the data object) to provide a block of
25303  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25304  * <p>
25305  * Custom implementations must implement the load method as described in
25306  * {@link Roo.data.HttpProxy#load}.
25307  */
25308 Roo.data.DataProxy = function(){
25309     this.addEvents({
25310         /**
25311          * @event beforeload
25312          * Fires before a network request is made to retrieve a data object.
25313          * @param {Object} This DataProxy object.
25314          * @param {Object} params The params parameter to the load function.
25315          */
25316         beforeload : true,
25317         /**
25318          * @event load
25319          * Fires before the load method's callback is called.
25320          * @param {Object} This DataProxy object.
25321          * @param {Object} o The data object.
25322          * @param {Object} arg The callback argument object passed to the load function.
25323          */
25324         load : true,
25325         /**
25326          * @event loadexception
25327          * Fires if an Exception occurs during data retrieval.
25328          * @param {Object} This DataProxy object.
25329          * @param {Object} o The data object.
25330          * @param {Object} arg The callback argument object passed to the load function.
25331          * @param {Object} e The Exception.
25332          */
25333         loadexception : true
25334     });
25335     Roo.data.DataProxy.superclass.constructor.call(this);
25336 };
25337
25338 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25339
25340     /**
25341      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25342      */
25343 /*
25344  * Based on:
25345  * Ext JS Library 1.1.1
25346  * Copyright(c) 2006-2007, Ext JS, LLC.
25347  *
25348  * Originally Released Under LGPL - original licence link has changed is not relivant.
25349  *
25350  * Fork - LGPL
25351  * <script type="text/javascript">
25352  */
25353 /**
25354  * @class Roo.data.MemoryProxy
25355  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25356  * to the Reader when its load method is called.
25357  * @constructor
25358  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25359  */
25360 Roo.data.MemoryProxy = function(data){
25361     if (data.data) {
25362         data = data.data;
25363     }
25364     Roo.data.MemoryProxy.superclass.constructor.call(this);
25365     this.data = data;
25366 };
25367
25368 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25369     
25370     /**
25371      * Load data from the requested source (in this case an in-memory
25372      * data object passed to the constructor), read the data object into
25373      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25374      * process that block using the passed callback.
25375      * @param {Object} params This parameter is not used by the MemoryProxy class.
25376      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25377      * object into a block of Roo.data.Records.
25378      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25379      * The function must be passed <ul>
25380      * <li>The Record block object</li>
25381      * <li>The "arg" argument from the load function</li>
25382      * <li>A boolean success indicator</li>
25383      * </ul>
25384      * @param {Object} scope The scope in which to call the callback
25385      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25386      */
25387     load : function(params, reader, callback, scope, arg){
25388         params = params || {};
25389         var result;
25390         try {
25391             result = reader.readRecords(params.data ? params.data :this.data);
25392         }catch(e){
25393             this.fireEvent("loadexception", this, arg, null, e);
25394             callback.call(scope, null, arg, false);
25395             return;
25396         }
25397         callback.call(scope, result, arg, true);
25398     },
25399     
25400     // private
25401     update : function(params, records){
25402         
25403     }
25404 });/*
25405  * Based on:
25406  * Ext JS Library 1.1.1
25407  * Copyright(c) 2006-2007, Ext JS, LLC.
25408  *
25409  * Originally Released Under LGPL - original licence link has changed is not relivant.
25410  *
25411  * Fork - LGPL
25412  * <script type="text/javascript">
25413  */
25414 /**
25415  * @class Roo.data.HttpProxy
25416  * @extends Roo.data.DataProxy
25417  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25418  * configured to reference a certain URL.<br><br>
25419  * <p>
25420  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25421  * from which the running page was served.<br><br>
25422  * <p>
25423  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25424  * <p>
25425  * Be aware that to enable the browser to parse an XML document, the server must set
25426  * the Content-Type header in the HTTP response to "text/xml".
25427  * @constructor
25428  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25429  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25430  * will be used to make the request.
25431  */
25432 Roo.data.HttpProxy = function(conn){
25433     Roo.data.HttpProxy.superclass.constructor.call(this);
25434     // is conn a conn config or a real conn?
25435     this.conn = conn;
25436     this.useAjax = !conn || !conn.events;
25437   
25438 };
25439
25440 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25441     // thse are take from connection...
25442     
25443     /**
25444      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25445      */
25446     /**
25447      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25448      * extra parameters to each request made by this object. (defaults to undefined)
25449      */
25450     /**
25451      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25452      *  to each request made by this object. (defaults to undefined)
25453      */
25454     /**
25455      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
25456      */
25457     /**
25458      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25459      */
25460      /**
25461      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25462      * @type Boolean
25463      */
25464   
25465
25466     /**
25467      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25468      * @type Boolean
25469      */
25470     /**
25471      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25472      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25473      * a finer-grained basis than the DataProxy events.
25474      */
25475     getConnection : function(){
25476         return this.useAjax ? Roo.Ajax : this.conn;
25477     },
25478
25479     /**
25480      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25481      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25482      * process that block using the passed callback.
25483      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25484      * for the request to the remote server.
25485      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25486      * object into a block of Roo.data.Records.
25487      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25488      * The function must be passed <ul>
25489      * <li>The Record block object</li>
25490      * <li>The "arg" argument from the load function</li>
25491      * <li>A boolean success indicator</li>
25492      * </ul>
25493      * @param {Object} scope The scope in which to call the callback
25494      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25495      */
25496     load : function(params, reader, callback, scope, arg){
25497         if(this.fireEvent("beforeload", this, params) !== false){
25498             var  o = {
25499                 params : params || {},
25500                 request: {
25501                     callback : callback,
25502                     scope : scope,
25503                     arg : arg
25504                 },
25505                 reader: reader,
25506                 callback : this.loadResponse,
25507                 scope: this
25508             };
25509             if(this.useAjax){
25510                 Roo.applyIf(o, this.conn);
25511                 if(this.activeRequest){
25512                     Roo.Ajax.abort(this.activeRequest);
25513                 }
25514                 this.activeRequest = Roo.Ajax.request(o);
25515             }else{
25516                 this.conn.request(o);
25517             }
25518         }else{
25519             callback.call(scope||this, null, arg, false);
25520         }
25521     },
25522
25523     // private
25524     loadResponse : function(o, success, response){
25525         delete this.activeRequest;
25526         if(!success){
25527             this.fireEvent("loadexception", this, o, response);
25528             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25529             return;
25530         }
25531         var result;
25532         try {
25533             result = o.reader.read(response);
25534         }catch(e){
25535             o.success = false;
25536             o.raw = { errorMsg : response.responseText };
25537             this.fireEvent("loadexception", this, o, response, e);
25538             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25539             return;
25540         }
25541         
25542         this.fireEvent("load", this, o, o.request.arg);
25543         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25544     },
25545
25546     // private
25547     update : function(dataSet){
25548
25549     },
25550
25551     // private
25552     updateResponse : function(dataSet){
25553
25554     }
25555 });/*
25556  * Based on:
25557  * Ext JS Library 1.1.1
25558  * Copyright(c) 2006-2007, Ext JS, LLC.
25559  *
25560  * Originally Released Under LGPL - original licence link has changed is not relivant.
25561  *
25562  * Fork - LGPL
25563  * <script type="text/javascript">
25564  */
25565
25566 /**
25567  * @class Roo.data.ScriptTagProxy
25568  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25569  * other than the originating domain of the running page.<br><br>
25570  * <p>
25571  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
25572  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25573  * <p>
25574  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25575  * source code that is used as the source inside a &lt;script> tag.<br><br>
25576  * <p>
25577  * In order for the browser to process the returned data, the server must wrap the data object
25578  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25579  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25580  * depending on whether the callback name was passed:
25581  * <p>
25582  * <pre><code>
25583 boolean scriptTag = false;
25584 String cb = request.getParameter("callback");
25585 if (cb != null) {
25586     scriptTag = true;
25587     response.setContentType("text/javascript");
25588 } else {
25589     response.setContentType("application/x-json");
25590 }
25591 Writer out = response.getWriter();
25592 if (scriptTag) {
25593     out.write(cb + "(");
25594 }
25595 out.print(dataBlock.toJsonString());
25596 if (scriptTag) {
25597     out.write(");");
25598 }
25599 </pre></code>
25600  *
25601  * @constructor
25602  * @param {Object} config A configuration object.
25603  */
25604 Roo.data.ScriptTagProxy = function(config){
25605     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25606     Roo.apply(this, config);
25607     this.head = document.getElementsByTagName("head")[0];
25608 };
25609
25610 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25611
25612 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25613     /**
25614      * @cfg {String} url The URL from which to request the data object.
25615      */
25616     /**
25617      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25618      */
25619     timeout : 30000,
25620     /**
25621      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25622      * the server the name of the callback function set up by the load call to process the returned data object.
25623      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25624      * javascript output which calls this named function passing the data object as its only parameter.
25625      */
25626     callbackParam : "callback",
25627     /**
25628      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25629      * name to the request.
25630      */
25631     nocache : true,
25632
25633     /**
25634      * Load data from the configured URL, read the data object into
25635      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25636      * process that block using the passed callback.
25637      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25638      * for the request to the remote server.
25639      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25640      * object into a block of Roo.data.Records.
25641      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25642      * The function must be passed <ul>
25643      * <li>The Record block object</li>
25644      * <li>The "arg" argument from the load function</li>
25645      * <li>A boolean success indicator</li>
25646      * </ul>
25647      * @param {Object} scope The scope in which to call the callback
25648      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25649      */
25650     load : function(params, reader, callback, scope, arg){
25651         if(this.fireEvent("beforeload", this, params) !== false){
25652
25653             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25654
25655             var url = this.url;
25656             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25657             if(this.nocache){
25658                 url += "&_dc=" + (new Date().getTime());
25659             }
25660             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25661             var trans = {
25662                 id : transId,
25663                 cb : "stcCallback"+transId,
25664                 scriptId : "stcScript"+transId,
25665                 params : params,
25666                 arg : arg,
25667                 url : url,
25668                 callback : callback,
25669                 scope : scope,
25670                 reader : reader
25671             };
25672             var conn = this;
25673
25674             window[trans.cb] = function(o){
25675                 conn.handleResponse(o, trans);
25676             };
25677
25678             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25679
25680             if(this.autoAbort !== false){
25681                 this.abort();
25682             }
25683
25684             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25685
25686             var script = document.createElement("script");
25687             script.setAttribute("src", url);
25688             script.setAttribute("type", "text/javascript");
25689             script.setAttribute("id", trans.scriptId);
25690             this.head.appendChild(script);
25691
25692             this.trans = trans;
25693         }else{
25694             callback.call(scope||this, null, arg, false);
25695         }
25696     },
25697
25698     // private
25699     isLoading : function(){
25700         return this.trans ? true : false;
25701     },
25702
25703     /**
25704      * Abort the current server request.
25705      */
25706     abort : function(){
25707         if(this.isLoading()){
25708             this.destroyTrans(this.trans);
25709         }
25710     },
25711
25712     // private
25713     destroyTrans : function(trans, isLoaded){
25714         this.head.removeChild(document.getElementById(trans.scriptId));
25715         clearTimeout(trans.timeoutId);
25716         if(isLoaded){
25717             window[trans.cb] = undefined;
25718             try{
25719                 delete window[trans.cb];
25720             }catch(e){}
25721         }else{
25722             // if hasn't been loaded, wait for load to remove it to prevent script error
25723             window[trans.cb] = function(){
25724                 window[trans.cb] = undefined;
25725                 try{
25726                     delete window[trans.cb];
25727                 }catch(e){}
25728             };
25729         }
25730     },
25731
25732     // private
25733     handleResponse : function(o, trans){
25734         this.trans = false;
25735         this.destroyTrans(trans, true);
25736         var result;
25737         try {
25738             result = trans.reader.readRecords(o);
25739         }catch(e){
25740             this.fireEvent("loadexception", this, o, trans.arg, e);
25741             trans.callback.call(trans.scope||window, null, trans.arg, false);
25742             return;
25743         }
25744         this.fireEvent("load", this, o, trans.arg);
25745         trans.callback.call(trans.scope||window, result, trans.arg, true);
25746     },
25747
25748     // private
25749     handleFailure : function(trans){
25750         this.trans = false;
25751         this.destroyTrans(trans, false);
25752         this.fireEvent("loadexception", this, null, trans.arg);
25753         trans.callback.call(trans.scope||window, null, trans.arg, false);
25754     }
25755 });/*
25756  * Based on:
25757  * Ext JS Library 1.1.1
25758  * Copyright(c) 2006-2007, Ext JS, LLC.
25759  *
25760  * Originally Released Under LGPL - original licence link has changed is not relivant.
25761  *
25762  * Fork - LGPL
25763  * <script type="text/javascript">
25764  */
25765
25766 /**
25767  * @class Roo.data.JsonReader
25768  * @extends Roo.data.DataReader
25769  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25770  * based on mappings in a provided Roo.data.Record constructor.
25771  * 
25772  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25773  * in the reply previously. 
25774  * 
25775  * <p>
25776  * Example code:
25777  * <pre><code>
25778 var RecordDef = Roo.data.Record.create([
25779     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25780     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25781 ]);
25782 var myReader = new Roo.data.JsonReader({
25783     totalProperty: "results",    // The property which contains the total dataset size (optional)
25784     root: "rows",                // The property which contains an Array of row objects
25785     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25786 }, RecordDef);
25787 </code></pre>
25788  * <p>
25789  * This would consume a JSON file like this:
25790  * <pre><code>
25791 { 'results': 2, 'rows': [
25792     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25793     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25794 }
25795 </code></pre>
25796  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25797  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25798  * paged from the remote server.
25799  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25800  * @cfg {String} root name of the property which contains the Array of row objects.
25801  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25802  * @cfg {Array} fields Array of field definition objects
25803  * @constructor
25804  * Create a new JsonReader
25805  * @param {Object} meta Metadata configuration options
25806  * @param {Object} recordType Either an Array of field definition objects,
25807  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25808  */
25809 Roo.data.JsonReader = function(meta, recordType){
25810     
25811     meta = meta || {};
25812     // set some defaults:
25813     Roo.applyIf(meta, {
25814         totalProperty: 'total',
25815         successProperty : 'success',
25816         root : 'data',
25817         id : 'id'
25818     });
25819     
25820     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25821 };
25822 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25823     
25824     readerType : 'Json',
25825     
25826     /**
25827      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25828      * Used by Store query builder to append _requestMeta to params.
25829      * 
25830      */
25831     metaFromRemote : false,
25832     /**
25833      * This method is only used by a DataProxy which has retrieved data from a remote server.
25834      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25835      * @return {Object} data A data block which is used by an Roo.data.Store object as
25836      * a cache of Roo.data.Records.
25837      */
25838     read : function(response){
25839         var json = response.responseText;
25840        
25841         var o = /* eval:var:o */ eval("("+json+")");
25842         if(!o) {
25843             throw {message: "JsonReader.read: Json object not found"};
25844         }
25845         
25846         if(o.metaData){
25847             
25848             delete this.ef;
25849             this.metaFromRemote = true;
25850             this.meta = o.metaData;
25851             this.recordType = Roo.data.Record.create(o.metaData.fields);
25852             this.onMetaChange(this.meta, this.recordType, o);
25853         }
25854         return this.readRecords(o);
25855     },
25856
25857     // private function a store will implement
25858     onMetaChange : function(meta, recordType, o){
25859
25860     },
25861
25862     /**
25863          * @ignore
25864          */
25865     simpleAccess: function(obj, subsc) {
25866         return obj[subsc];
25867     },
25868
25869         /**
25870          * @ignore
25871          */
25872     getJsonAccessor: function(){
25873         var re = /[\[\.]/;
25874         return function(expr) {
25875             try {
25876                 return(re.test(expr))
25877                     ? new Function("obj", "return obj." + expr)
25878                     : function(obj){
25879                         return obj[expr];
25880                     };
25881             } catch(e){}
25882             return Roo.emptyFn;
25883         };
25884     }(),
25885
25886     /**
25887      * Create a data block containing Roo.data.Records from an XML document.
25888      * @param {Object} o An object which contains an Array of row objects in the property specified
25889      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25890      * which contains the total size of the dataset.
25891      * @return {Object} data A data block which is used by an Roo.data.Store object as
25892      * a cache of Roo.data.Records.
25893      */
25894     readRecords : function(o){
25895         /**
25896          * After any data loads, the raw JSON data is available for further custom processing.
25897          * @type Object
25898          */
25899         this.o = o;
25900         var s = this.meta, Record = this.recordType,
25901             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25902
25903 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25904         if (!this.ef) {
25905             if(s.totalProperty) {
25906                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25907                 }
25908                 if(s.successProperty) {
25909                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25910                 }
25911                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25912                 if (s.id) {
25913                         var g = this.getJsonAccessor(s.id);
25914                         this.getId = function(rec) {
25915                                 var r = g(rec);  
25916                                 return (r === undefined || r === "") ? null : r;
25917                         };
25918                 } else {
25919                         this.getId = function(){return null;};
25920                 }
25921             this.ef = [];
25922             for(var jj = 0; jj < fl; jj++){
25923                 f = fi[jj];
25924                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25925                 this.ef[jj] = this.getJsonAccessor(map);
25926             }
25927         }
25928
25929         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25930         if(s.totalProperty){
25931             var vt = parseInt(this.getTotal(o), 10);
25932             if(!isNaN(vt)){
25933                 totalRecords = vt;
25934             }
25935         }
25936         if(s.successProperty){
25937             var vs = this.getSuccess(o);
25938             if(vs === false || vs === 'false'){
25939                 success = false;
25940             }
25941         }
25942         var records = [];
25943         for(var i = 0; i < c; i++){
25944             var n = root[i];
25945             var values = {};
25946             var id = this.getId(n);
25947             for(var j = 0; j < fl; j++){
25948                 f = fi[j];
25949                                 var v = this.ef[j](n);
25950                                 if (!f.convert) {
25951                                         Roo.log('missing convert for ' + f.name);
25952                                         Roo.log(f);
25953                                         continue;
25954                                 }
25955                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25956             }
25957                         if (!Record) {
25958                                 return {
25959                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
25960                                         success : false,
25961                                         records : [],
25962                                         totalRecords : 0
25963                                 };
25964                         }
25965             var record = new Record(values, id);
25966             record.json = n;
25967             records[i] = record;
25968         }
25969         return {
25970             raw : o,
25971             success : success,
25972             records : records,
25973             totalRecords : totalRecords
25974         };
25975     },
25976     // used when loading children.. @see loadDataFromChildren
25977     toLoadData: function(rec)
25978     {
25979         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25980         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25981         return { data : data, total : data.length };
25982         
25983     }
25984 });/*
25985  * Based on:
25986  * Ext JS Library 1.1.1
25987  * Copyright(c) 2006-2007, Ext JS, LLC.
25988  *
25989  * Originally Released Under LGPL - original licence link has changed is not relivant.
25990  *
25991  * Fork - LGPL
25992  * <script type="text/javascript">
25993  */
25994
25995 /**
25996  * @class Roo.data.XmlReader
25997  * @extends Roo.data.DataReader
25998  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25999  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26000  * <p>
26001  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26002  * header in the HTTP response must be set to "text/xml".</em>
26003  * <p>
26004  * Example code:
26005  * <pre><code>
26006 var RecordDef = Roo.data.Record.create([
26007    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26008    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26009 ]);
26010 var myReader = new Roo.data.XmlReader({
26011    totalRecords: "results", // The element which contains the total dataset size (optional)
26012    record: "row",           // The repeated element which contains row information
26013    id: "id"                 // The element within the row that provides an ID for the record (optional)
26014 }, RecordDef);
26015 </code></pre>
26016  * <p>
26017  * This would consume an XML file like this:
26018  * <pre><code>
26019 &lt;?xml?>
26020 &lt;dataset>
26021  &lt;results>2&lt;/results>
26022  &lt;row>
26023    &lt;id>1&lt;/id>
26024    &lt;name>Bill&lt;/name>
26025    &lt;occupation>Gardener&lt;/occupation>
26026  &lt;/row>
26027  &lt;row>
26028    &lt;id>2&lt;/id>
26029    &lt;name>Ben&lt;/name>
26030    &lt;occupation>Horticulturalist&lt;/occupation>
26031  &lt;/row>
26032 &lt;/dataset>
26033 </code></pre>
26034  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26035  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26036  * paged from the remote server.
26037  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26038  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26039  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26040  * a record identifier value.
26041  * @constructor
26042  * Create a new XmlReader
26043  * @param {Object} meta Metadata configuration options
26044  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26045  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26046  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26047  */
26048 Roo.data.XmlReader = function(meta, recordType){
26049     meta = meta || {};
26050     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26051 };
26052 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26053     
26054     readerType : 'Xml',
26055     
26056     /**
26057      * This method is only used by a DataProxy which has retrieved data from a remote server.
26058          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26059          * to contain a method called 'responseXML' that returns an XML document object.
26060      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26061      * a cache of Roo.data.Records.
26062      */
26063     read : function(response){
26064         var doc = response.responseXML;
26065         if(!doc) {
26066             throw {message: "XmlReader.read: XML Document not available"};
26067         }
26068         return this.readRecords(doc);
26069     },
26070
26071     /**
26072      * Create a data block containing Roo.data.Records from an XML document.
26073          * @param {Object} doc A parsed XML document.
26074      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26075      * a cache of Roo.data.Records.
26076      */
26077     readRecords : function(doc){
26078         /**
26079          * After any data loads/reads, the raw XML Document is available for further custom processing.
26080          * @type XMLDocument
26081          */
26082         this.xmlData = doc;
26083         var root = doc.documentElement || doc;
26084         var q = Roo.DomQuery;
26085         var recordType = this.recordType, fields = recordType.prototype.fields;
26086         var sid = this.meta.id;
26087         var totalRecords = 0, success = true;
26088         if(this.meta.totalRecords){
26089             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26090         }
26091         
26092         if(this.meta.success){
26093             var sv = q.selectValue(this.meta.success, root, true);
26094             success = sv !== false && sv !== 'false';
26095         }
26096         var records = [];
26097         var ns = q.select(this.meta.record, root);
26098         for(var i = 0, len = ns.length; i < len; i++) {
26099                 var n = ns[i];
26100                 var values = {};
26101                 var id = sid ? q.selectValue(sid, n) : undefined;
26102                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26103                     var f = fields.items[j];
26104                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26105                     v = f.convert(v);
26106                     values[f.name] = v;
26107                 }
26108                 var record = new recordType(values, id);
26109                 record.node = n;
26110                 records[records.length] = record;
26111             }
26112
26113             return {
26114                 success : success,
26115                 records : records,
26116                 totalRecords : totalRecords || records.length
26117             };
26118     }
26119 });/*
26120  * Based on:
26121  * Ext JS Library 1.1.1
26122  * Copyright(c) 2006-2007, Ext JS, LLC.
26123  *
26124  * Originally Released Under LGPL - original licence link has changed is not relivant.
26125  *
26126  * Fork - LGPL
26127  * <script type="text/javascript">
26128  */
26129
26130 /**
26131  * @class Roo.data.ArrayReader
26132  * @extends Roo.data.DataReader
26133  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26134  * Each element of that Array represents a row of data fields. The
26135  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26136  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26137  * <p>
26138  * Example code:.
26139  * <pre><code>
26140 var RecordDef = Roo.data.Record.create([
26141     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26142     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26143 ]);
26144 var myReader = new Roo.data.ArrayReader({
26145     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26146 }, RecordDef);
26147 </code></pre>
26148  * <p>
26149  * This would consume an Array like this:
26150  * <pre><code>
26151 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26152   </code></pre>
26153  
26154  * @constructor
26155  * Create a new JsonReader
26156  * @param {Object} meta Metadata configuration options.
26157  * @param {Object|Array} recordType Either an Array of field definition objects
26158  * 
26159  * @cfg {Array} fields Array of field definition objects
26160  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26161  * as specified to {@link Roo.data.Record#create},
26162  * or an {@link Roo.data.Record} object
26163  *
26164  * 
26165  * created using {@link Roo.data.Record#create}.
26166  */
26167 Roo.data.ArrayReader = function(meta, recordType)
26168 {    
26169     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26170 };
26171
26172 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26173     
26174       /**
26175      * Create a data block containing Roo.data.Records from an XML document.
26176      * @param {Object} o An Array of row objects which represents the dataset.
26177      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26178      * a cache of Roo.data.Records.
26179      */
26180     readRecords : function(o)
26181     {
26182         var sid = this.meta ? this.meta.id : null;
26183         var recordType = this.recordType, fields = recordType.prototype.fields;
26184         var records = [];
26185         var root = o;
26186         for(var i = 0; i < root.length; i++){
26187             var n = root[i];
26188             var values = {};
26189             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26190             for(var j = 0, jlen = fields.length; j < jlen; j++){
26191                 var f = fields.items[j];
26192                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26193                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26194                 v = f.convert(v);
26195                 values[f.name] = v;
26196             }
26197             var record = new recordType(values, id);
26198             record.json = n;
26199             records[records.length] = record;
26200         }
26201         return {
26202             records : records,
26203             totalRecords : records.length
26204         };
26205     },
26206     // used when loading children.. @see loadDataFromChildren
26207     toLoadData: function(rec)
26208     {
26209         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26210         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26211         
26212     }
26213     
26214     
26215 });/*
26216  * Based on:
26217  * Ext JS Library 1.1.1
26218  * Copyright(c) 2006-2007, Ext JS, LLC.
26219  *
26220  * Originally Released Under LGPL - original licence link has changed is not relivant.
26221  *
26222  * Fork - LGPL
26223  * <script type="text/javascript">
26224  */
26225
26226
26227 /**
26228  * @class Roo.data.Tree
26229  * @extends Roo.util.Observable
26230  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26231  * in the tree have most standard DOM functionality.
26232  * @constructor
26233  * @param {Node} root (optional) The root node
26234  */
26235 Roo.data.Tree = function(root){
26236    this.nodeHash = {};
26237    /**
26238     * The root node for this tree
26239     * @type Node
26240     */
26241    this.root = null;
26242    if(root){
26243        this.setRootNode(root);
26244    }
26245    this.addEvents({
26246        /**
26247         * @event append
26248         * Fires when a new child node is appended to a node in this tree.
26249         * @param {Tree} tree The owner tree
26250         * @param {Node} parent The parent node
26251         * @param {Node} node The newly appended node
26252         * @param {Number} index The index of the newly appended node
26253         */
26254        "append" : true,
26255        /**
26256         * @event remove
26257         * Fires when a child node is removed from a node in this tree.
26258         * @param {Tree} tree The owner tree
26259         * @param {Node} parent The parent node
26260         * @param {Node} node The child node removed
26261         */
26262        "remove" : true,
26263        /**
26264         * @event move
26265         * Fires when a node is moved to a new location in the tree
26266         * @param {Tree} tree The owner tree
26267         * @param {Node} node The node moved
26268         * @param {Node} oldParent The old parent of this node
26269         * @param {Node} newParent The new parent of this node
26270         * @param {Number} index The index it was moved to
26271         */
26272        "move" : true,
26273        /**
26274         * @event insert
26275         * Fires when a new child node is inserted in a node in this tree.
26276         * @param {Tree} tree The owner tree
26277         * @param {Node} parent The parent node
26278         * @param {Node} node The child node inserted
26279         * @param {Node} refNode The child node the node was inserted before
26280         */
26281        "insert" : true,
26282        /**
26283         * @event beforeappend
26284         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26285         * @param {Tree} tree The owner tree
26286         * @param {Node} parent The parent node
26287         * @param {Node} node The child node to be appended
26288         */
26289        "beforeappend" : true,
26290        /**
26291         * @event beforeremove
26292         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26293         * @param {Tree} tree The owner tree
26294         * @param {Node} parent The parent node
26295         * @param {Node} node The child node to be removed
26296         */
26297        "beforeremove" : true,
26298        /**
26299         * @event beforemove
26300         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26301         * @param {Tree} tree The owner tree
26302         * @param {Node} node The node being moved
26303         * @param {Node} oldParent The parent of the node
26304         * @param {Node} newParent The new parent the node is moving to
26305         * @param {Number} index The index it is being moved to
26306         */
26307        "beforemove" : true,
26308        /**
26309         * @event beforeinsert
26310         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26311         * @param {Tree} tree The owner tree
26312         * @param {Node} parent The parent node
26313         * @param {Node} node The child node to be inserted
26314         * @param {Node} refNode The child node the node is being inserted before
26315         */
26316        "beforeinsert" : true
26317    });
26318
26319     Roo.data.Tree.superclass.constructor.call(this);
26320 };
26321
26322 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26323     pathSeparator: "/",
26324
26325     proxyNodeEvent : function(){
26326         return this.fireEvent.apply(this, arguments);
26327     },
26328
26329     /**
26330      * Returns the root node for this tree.
26331      * @return {Node}
26332      */
26333     getRootNode : function(){
26334         return this.root;
26335     },
26336
26337     /**
26338      * Sets the root node for this tree.
26339      * @param {Node} node
26340      * @return {Node}
26341      */
26342     setRootNode : function(node){
26343         this.root = node;
26344         node.ownerTree = this;
26345         node.isRoot = true;
26346         this.registerNode(node);
26347         return node;
26348     },
26349
26350     /**
26351      * Gets a node in this tree by its id.
26352      * @param {String} id
26353      * @return {Node}
26354      */
26355     getNodeById : function(id){
26356         return this.nodeHash[id];
26357     },
26358
26359     registerNode : function(node){
26360         this.nodeHash[node.id] = node;
26361     },
26362
26363     unregisterNode : function(node){
26364         delete this.nodeHash[node.id];
26365     },
26366
26367     toString : function(){
26368         return "[Tree"+(this.id?" "+this.id:"")+"]";
26369     }
26370 });
26371
26372 /**
26373  * @class Roo.data.Node
26374  * @extends Roo.util.Observable
26375  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26376  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26377  * @constructor
26378  * @param {Object} attributes The attributes/config for the node
26379  */
26380 Roo.data.Node = function(attributes){
26381     /**
26382      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26383      * @type {Object}
26384      */
26385     this.attributes = attributes || {};
26386     this.leaf = this.attributes.leaf;
26387     /**
26388      * The node id. @type String
26389      */
26390     this.id = this.attributes.id;
26391     if(!this.id){
26392         this.id = Roo.id(null, "ynode-");
26393         this.attributes.id = this.id;
26394     }
26395      
26396     
26397     /**
26398      * All child nodes of this node. @type Array
26399      */
26400     this.childNodes = [];
26401     if(!this.childNodes.indexOf){ // indexOf is a must
26402         this.childNodes.indexOf = function(o){
26403             for(var i = 0, len = this.length; i < len; i++){
26404                 if(this[i] == o) {
26405                     return i;
26406                 }
26407             }
26408             return -1;
26409         };
26410     }
26411     /**
26412      * The parent node for this node. @type Node
26413      */
26414     this.parentNode = null;
26415     /**
26416      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26417      */
26418     this.firstChild = null;
26419     /**
26420      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26421      */
26422     this.lastChild = null;
26423     /**
26424      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26425      */
26426     this.previousSibling = null;
26427     /**
26428      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26429      */
26430     this.nextSibling = null;
26431
26432     this.addEvents({
26433        /**
26434         * @event append
26435         * Fires when a new child node is appended
26436         * @param {Tree} tree The owner tree
26437         * @param {Node} this This node
26438         * @param {Node} node The newly appended node
26439         * @param {Number} index The index of the newly appended node
26440         */
26441        "append" : true,
26442        /**
26443         * @event remove
26444         * Fires when a child node is removed
26445         * @param {Tree} tree The owner tree
26446         * @param {Node} this This node
26447         * @param {Node} node The removed node
26448         */
26449        "remove" : true,
26450        /**
26451         * @event move
26452         * Fires when this node is moved to a new location in the tree
26453         * @param {Tree} tree The owner tree
26454         * @param {Node} this This node
26455         * @param {Node} oldParent The old parent of this node
26456         * @param {Node} newParent The new parent of this node
26457         * @param {Number} index The index it was moved to
26458         */
26459        "move" : true,
26460        /**
26461         * @event insert
26462         * Fires when a new child node is inserted.
26463         * @param {Tree} tree The owner tree
26464         * @param {Node} this This node
26465         * @param {Node} node The child node inserted
26466         * @param {Node} refNode The child node the node was inserted before
26467         */
26468        "insert" : true,
26469        /**
26470         * @event beforeappend
26471         * Fires before a new child is appended, return false to cancel the append.
26472         * @param {Tree} tree The owner tree
26473         * @param {Node} this This node
26474         * @param {Node} node The child node to be appended
26475         */
26476        "beforeappend" : true,
26477        /**
26478         * @event beforeremove
26479         * Fires before a child is removed, return false to cancel the remove.
26480         * @param {Tree} tree The owner tree
26481         * @param {Node} this This node
26482         * @param {Node} node The child node to be removed
26483         */
26484        "beforeremove" : true,
26485        /**
26486         * @event beforemove
26487         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26488         * @param {Tree} tree The owner tree
26489         * @param {Node} this This node
26490         * @param {Node} oldParent The parent of this node
26491         * @param {Node} newParent The new parent this node is moving to
26492         * @param {Number} index The index it is being moved to
26493         */
26494        "beforemove" : true,
26495        /**
26496         * @event beforeinsert
26497         * Fires before a new child is inserted, return false to cancel the insert.
26498         * @param {Tree} tree The owner tree
26499         * @param {Node} this This node
26500         * @param {Node} node The child node to be inserted
26501         * @param {Node} refNode The child node the node is being inserted before
26502         */
26503        "beforeinsert" : true
26504    });
26505     this.listeners = this.attributes.listeners;
26506     Roo.data.Node.superclass.constructor.call(this);
26507 };
26508
26509 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26510     fireEvent : function(evtName){
26511         // first do standard event for this node
26512         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26513             return false;
26514         }
26515         // then bubble it up to the tree if the event wasn't cancelled
26516         var ot = this.getOwnerTree();
26517         if(ot){
26518             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26519                 return false;
26520             }
26521         }
26522         return true;
26523     },
26524
26525     /**
26526      * Returns true if this node is a leaf
26527      * @return {Boolean}
26528      */
26529     isLeaf : function(){
26530         return this.leaf === true;
26531     },
26532
26533     // private
26534     setFirstChild : function(node){
26535         this.firstChild = node;
26536     },
26537
26538     //private
26539     setLastChild : function(node){
26540         this.lastChild = node;
26541     },
26542
26543
26544     /**
26545      * Returns true if this node is the last child of its parent
26546      * @return {Boolean}
26547      */
26548     isLast : function(){
26549        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26550     },
26551
26552     /**
26553      * Returns true if this node is the first child of its parent
26554      * @return {Boolean}
26555      */
26556     isFirst : function(){
26557        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26558     },
26559
26560     hasChildNodes : function(){
26561         return !this.isLeaf() && this.childNodes.length > 0;
26562     },
26563
26564     /**
26565      * Insert node(s) as the last child node of this node.
26566      * @param {Node/Array} node The node or Array of nodes to append
26567      * @return {Node} The appended node if single append, or null if an array was passed
26568      */
26569     appendChild : function(node){
26570         var multi = false;
26571         if(node instanceof Array){
26572             multi = node;
26573         }else if(arguments.length > 1){
26574             multi = arguments;
26575         }
26576         
26577         // if passed an array or multiple args do them one by one
26578         if(multi){
26579             for(var i = 0, len = multi.length; i < len; i++) {
26580                 this.appendChild(multi[i]);
26581             }
26582         }else{
26583             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26584                 return false;
26585             }
26586             var index = this.childNodes.length;
26587             var oldParent = node.parentNode;
26588             // it's a move, make sure we move it cleanly
26589             if(oldParent){
26590                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26591                     return false;
26592                 }
26593                 oldParent.removeChild(node);
26594             }
26595             
26596             index = this.childNodes.length;
26597             if(index == 0){
26598                 this.setFirstChild(node);
26599             }
26600             this.childNodes.push(node);
26601             node.parentNode = this;
26602             var ps = this.childNodes[index-1];
26603             if(ps){
26604                 node.previousSibling = ps;
26605                 ps.nextSibling = node;
26606             }else{
26607                 node.previousSibling = null;
26608             }
26609             node.nextSibling = null;
26610             this.setLastChild(node);
26611             node.setOwnerTree(this.getOwnerTree());
26612             this.fireEvent("append", this.ownerTree, this, node, index);
26613             if(this.ownerTree) {
26614                 this.ownerTree.fireEvent("appendnode", this, node, index);
26615             }
26616             if(oldParent){
26617                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26618             }
26619             return node;
26620         }
26621     },
26622
26623     /**
26624      * Removes a child node from this node.
26625      * @param {Node} node The node to remove
26626      * @return {Node} The removed node
26627      */
26628     removeChild : function(node){
26629         var index = this.childNodes.indexOf(node);
26630         if(index == -1){
26631             return false;
26632         }
26633         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26634             return false;
26635         }
26636
26637         // remove it from childNodes collection
26638         this.childNodes.splice(index, 1);
26639
26640         // update siblings
26641         if(node.previousSibling){
26642             node.previousSibling.nextSibling = node.nextSibling;
26643         }
26644         if(node.nextSibling){
26645             node.nextSibling.previousSibling = node.previousSibling;
26646         }
26647
26648         // update child refs
26649         if(this.firstChild == node){
26650             this.setFirstChild(node.nextSibling);
26651         }
26652         if(this.lastChild == node){
26653             this.setLastChild(node.previousSibling);
26654         }
26655
26656         node.setOwnerTree(null);
26657         // clear any references from the node
26658         node.parentNode = null;
26659         node.previousSibling = null;
26660         node.nextSibling = null;
26661         this.fireEvent("remove", this.ownerTree, this, node);
26662         return node;
26663     },
26664
26665     /**
26666      * Inserts the first node before the second node in this nodes childNodes collection.
26667      * @param {Node} node The node to insert
26668      * @param {Node} refNode The node to insert before (if null the node is appended)
26669      * @return {Node} The inserted node
26670      */
26671     insertBefore : function(node, refNode){
26672         if(!refNode){ // like standard Dom, refNode can be null for append
26673             return this.appendChild(node);
26674         }
26675         // nothing to do
26676         if(node == refNode){
26677             return false;
26678         }
26679
26680         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26681             return false;
26682         }
26683         var index = this.childNodes.indexOf(refNode);
26684         var oldParent = node.parentNode;
26685         var refIndex = index;
26686
26687         // when moving internally, indexes will change after remove
26688         if(oldParent == this && this.childNodes.indexOf(node) < index){
26689             refIndex--;
26690         }
26691
26692         // it's a move, make sure we move it cleanly
26693         if(oldParent){
26694             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26695                 return false;
26696             }
26697             oldParent.removeChild(node);
26698         }
26699         if(refIndex == 0){
26700             this.setFirstChild(node);
26701         }
26702         this.childNodes.splice(refIndex, 0, node);
26703         node.parentNode = this;
26704         var ps = this.childNodes[refIndex-1];
26705         if(ps){
26706             node.previousSibling = ps;
26707             ps.nextSibling = node;
26708         }else{
26709             node.previousSibling = null;
26710         }
26711         node.nextSibling = refNode;
26712         refNode.previousSibling = node;
26713         node.setOwnerTree(this.getOwnerTree());
26714         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26715         if(oldParent){
26716             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26717         }
26718         return node;
26719     },
26720
26721     /**
26722      * Returns the child node at the specified index.
26723      * @param {Number} index
26724      * @return {Node}
26725      */
26726     item : function(index){
26727         return this.childNodes[index];
26728     },
26729
26730     /**
26731      * Replaces one child node in this node with another.
26732      * @param {Node} newChild The replacement node
26733      * @param {Node} oldChild The node to replace
26734      * @return {Node} The replaced node
26735      */
26736     replaceChild : function(newChild, oldChild){
26737         this.insertBefore(newChild, oldChild);
26738         this.removeChild(oldChild);
26739         return oldChild;
26740     },
26741
26742     /**
26743      * Returns the index of a child node
26744      * @param {Node} node
26745      * @return {Number} The index of the node or -1 if it was not found
26746      */
26747     indexOf : function(child){
26748         return this.childNodes.indexOf(child);
26749     },
26750
26751     /**
26752      * Returns the tree this node is in.
26753      * @return {Tree}
26754      */
26755     getOwnerTree : function(){
26756         // if it doesn't have one, look for one
26757         if(!this.ownerTree){
26758             var p = this;
26759             while(p){
26760                 if(p.ownerTree){
26761                     this.ownerTree = p.ownerTree;
26762                     break;
26763                 }
26764                 p = p.parentNode;
26765             }
26766         }
26767         return this.ownerTree;
26768     },
26769
26770     /**
26771      * Returns depth of this node (the root node has a depth of 0)
26772      * @return {Number}
26773      */
26774     getDepth : function(){
26775         var depth = 0;
26776         var p = this;
26777         while(p.parentNode){
26778             ++depth;
26779             p = p.parentNode;
26780         }
26781         return depth;
26782     },
26783
26784     // private
26785     setOwnerTree : function(tree){
26786         // if it's move, we need to update everyone
26787         if(tree != this.ownerTree){
26788             if(this.ownerTree){
26789                 this.ownerTree.unregisterNode(this);
26790             }
26791             this.ownerTree = tree;
26792             var cs = this.childNodes;
26793             for(var i = 0, len = cs.length; i < len; i++) {
26794                 cs[i].setOwnerTree(tree);
26795             }
26796             if(tree){
26797                 tree.registerNode(this);
26798             }
26799         }
26800     },
26801
26802     /**
26803      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26804      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26805      * @return {String} The path
26806      */
26807     getPath : function(attr){
26808         attr = attr || "id";
26809         var p = this.parentNode;
26810         var b = [this.attributes[attr]];
26811         while(p){
26812             b.unshift(p.attributes[attr]);
26813             p = p.parentNode;
26814         }
26815         var sep = this.getOwnerTree().pathSeparator;
26816         return sep + b.join(sep);
26817     },
26818
26819     /**
26820      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26821      * function call will be the scope provided or the current node. The arguments to the function
26822      * will be the args provided or the current node. If the function returns false at any point,
26823      * the bubble is stopped.
26824      * @param {Function} fn The function to call
26825      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26826      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26827      */
26828     bubble : function(fn, scope, args){
26829         var p = this;
26830         while(p){
26831             if(fn.call(scope || p, args || p) === false){
26832                 break;
26833             }
26834             p = p.parentNode;
26835         }
26836     },
26837
26838     /**
26839      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26840      * function call will be the scope provided or the current node. The arguments to the function
26841      * will be the args provided or the current node. If the function returns false at any point,
26842      * the cascade is stopped on that branch.
26843      * @param {Function} fn The function to call
26844      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26845      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26846      */
26847     cascade : function(fn, scope, args){
26848         if(fn.call(scope || this, args || this) !== false){
26849             var cs = this.childNodes;
26850             for(var i = 0, len = cs.length; i < len; i++) {
26851                 cs[i].cascade(fn, scope, args);
26852             }
26853         }
26854     },
26855
26856     /**
26857      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26858      * function call will be the scope provided or the current node. The arguments to the function
26859      * will be the args provided or the current node. If the function returns false at any point,
26860      * the iteration stops.
26861      * @param {Function} fn The function to call
26862      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26863      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26864      */
26865     eachChild : function(fn, scope, args){
26866         var cs = this.childNodes;
26867         for(var i = 0, len = cs.length; i < len; i++) {
26868                 if(fn.call(scope || this, args || cs[i]) === false){
26869                     break;
26870                 }
26871         }
26872     },
26873
26874     /**
26875      * Finds the first child that has the attribute with the specified value.
26876      * @param {String} attribute The attribute name
26877      * @param {Mixed} value The value to search for
26878      * @return {Node} The found child or null if none was found
26879      */
26880     findChild : function(attribute, value){
26881         var cs = this.childNodes;
26882         for(var i = 0, len = cs.length; i < len; i++) {
26883                 if(cs[i].attributes[attribute] == value){
26884                     return cs[i];
26885                 }
26886         }
26887         return null;
26888     },
26889
26890     /**
26891      * Finds the first child by a custom function. The child matches if the function passed
26892      * returns true.
26893      * @param {Function} fn
26894      * @param {Object} scope (optional)
26895      * @return {Node} The found child or null if none was found
26896      */
26897     findChildBy : function(fn, scope){
26898         var cs = this.childNodes;
26899         for(var i = 0, len = cs.length; i < len; i++) {
26900                 if(fn.call(scope||cs[i], cs[i]) === true){
26901                     return cs[i];
26902                 }
26903         }
26904         return null;
26905     },
26906
26907     /**
26908      * Sorts this nodes children using the supplied sort function
26909      * @param {Function} fn
26910      * @param {Object} scope (optional)
26911      */
26912     sort : function(fn, scope){
26913         var cs = this.childNodes;
26914         var len = cs.length;
26915         if(len > 0){
26916             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26917             cs.sort(sortFn);
26918             for(var i = 0; i < len; i++){
26919                 var n = cs[i];
26920                 n.previousSibling = cs[i-1];
26921                 n.nextSibling = cs[i+1];
26922                 if(i == 0){
26923                     this.setFirstChild(n);
26924                 }
26925                 if(i == len-1){
26926                     this.setLastChild(n);
26927                 }
26928             }
26929         }
26930     },
26931
26932     /**
26933      * Returns true if this node is an ancestor (at any point) of the passed node.
26934      * @param {Node} node
26935      * @return {Boolean}
26936      */
26937     contains : function(node){
26938         return node.isAncestor(this);
26939     },
26940
26941     /**
26942      * Returns true if the passed node is an ancestor (at any point) of this node.
26943      * @param {Node} node
26944      * @return {Boolean}
26945      */
26946     isAncestor : function(node){
26947         var p = this.parentNode;
26948         while(p){
26949             if(p == node){
26950                 return true;
26951             }
26952             p = p.parentNode;
26953         }
26954         return false;
26955     },
26956
26957     toString : function(){
26958         return "[Node"+(this.id?" "+this.id:"")+"]";
26959     }
26960 });/*
26961  * Based on:
26962  * Ext JS Library 1.1.1
26963  * Copyright(c) 2006-2007, Ext JS, LLC.
26964  *
26965  * Originally Released Under LGPL - original licence link has changed is not relivant.
26966  *
26967  * Fork - LGPL
26968  * <script type="text/javascript">
26969  */
26970
26971
26972 /**
26973  * @class Roo.Shadow
26974  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26975  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26976  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26977  * @constructor
26978  * Create a new Shadow
26979  * @param {Object} config The config object
26980  */
26981 Roo.Shadow = function(config){
26982     Roo.apply(this, config);
26983     if(typeof this.mode != "string"){
26984         this.mode = this.defaultMode;
26985     }
26986     var o = this.offset, a = {h: 0};
26987     var rad = Math.floor(this.offset/2);
26988     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26989         case "drop":
26990             a.w = 0;
26991             a.l = a.t = o;
26992             a.t -= 1;
26993             if(Roo.isIE){
26994                 a.l -= this.offset + rad;
26995                 a.t -= this.offset + rad;
26996                 a.w -= rad;
26997                 a.h -= rad;
26998                 a.t += 1;
26999             }
27000         break;
27001         case "sides":
27002             a.w = (o*2);
27003             a.l = -o;
27004             a.t = o-1;
27005             if(Roo.isIE){
27006                 a.l -= (this.offset - rad);
27007                 a.t -= this.offset + rad;
27008                 a.l += 1;
27009                 a.w -= (this.offset - rad)*2;
27010                 a.w -= rad + 1;
27011                 a.h -= 1;
27012             }
27013         break;
27014         case "frame":
27015             a.w = a.h = (o*2);
27016             a.l = a.t = -o;
27017             a.t += 1;
27018             a.h -= 2;
27019             if(Roo.isIE){
27020                 a.l -= (this.offset - rad);
27021                 a.t -= (this.offset - rad);
27022                 a.l += 1;
27023                 a.w -= (this.offset + rad + 1);
27024                 a.h -= (this.offset + rad);
27025                 a.h += 1;
27026             }
27027         break;
27028     };
27029
27030     this.adjusts = a;
27031 };
27032
27033 Roo.Shadow.prototype = {
27034     /**
27035      * @cfg {String} mode
27036      * The shadow display mode.  Supports the following options:<br />
27037      * sides: Shadow displays on both sides and bottom only<br />
27038      * frame: Shadow displays equally on all four sides<br />
27039      * drop: Traditional bottom-right drop shadow (default)
27040      */
27041     mode: false,
27042     /**
27043      * @cfg {String} offset
27044      * The number of pixels to offset the shadow from the element (defaults to 4)
27045      */
27046     offset: 4,
27047
27048     // private
27049     defaultMode: "drop",
27050
27051     /**
27052      * Displays the shadow under the target element
27053      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27054      */
27055     show : function(target){
27056         target = Roo.get(target);
27057         if(!this.el){
27058             this.el = Roo.Shadow.Pool.pull();
27059             if(this.el.dom.nextSibling != target.dom){
27060                 this.el.insertBefore(target);
27061             }
27062         }
27063         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27064         if(Roo.isIE){
27065             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27066         }
27067         this.realign(
27068             target.getLeft(true),
27069             target.getTop(true),
27070             target.getWidth(),
27071             target.getHeight()
27072         );
27073         this.el.dom.style.display = "block";
27074     },
27075
27076     /**
27077      * Returns true if the shadow is visible, else false
27078      */
27079     isVisible : function(){
27080         return this.el ? true : false;  
27081     },
27082
27083     /**
27084      * Direct alignment when values are already available. Show must be called at least once before
27085      * calling this method to ensure it is initialized.
27086      * @param {Number} left The target element left position
27087      * @param {Number} top The target element top position
27088      * @param {Number} width The target element width
27089      * @param {Number} height The target element height
27090      */
27091     realign : function(l, t, w, h){
27092         if(!this.el){
27093             return;
27094         }
27095         var a = this.adjusts, d = this.el.dom, s = d.style;
27096         var iea = 0;
27097         s.left = (l+a.l)+"px";
27098         s.top = (t+a.t)+"px";
27099         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27100  
27101         if(s.width != sws || s.height != shs){
27102             s.width = sws;
27103             s.height = shs;
27104             if(!Roo.isIE){
27105                 var cn = d.childNodes;
27106                 var sww = Math.max(0, (sw-12))+"px";
27107                 cn[0].childNodes[1].style.width = sww;
27108                 cn[1].childNodes[1].style.width = sww;
27109                 cn[2].childNodes[1].style.width = sww;
27110                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27111             }
27112         }
27113     },
27114
27115     /**
27116      * Hides this shadow
27117      */
27118     hide : function(){
27119         if(this.el){
27120             this.el.dom.style.display = "none";
27121             Roo.Shadow.Pool.push(this.el);
27122             delete this.el;
27123         }
27124     },
27125
27126     /**
27127      * Adjust the z-index of this shadow
27128      * @param {Number} zindex The new z-index
27129      */
27130     setZIndex : function(z){
27131         this.zIndex = z;
27132         if(this.el){
27133             this.el.setStyle("z-index", z);
27134         }
27135     }
27136 };
27137
27138 // Private utility class that manages the internal Shadow cache
27139 Roo.Shadow.Pool = function(){
27140     var p = [];
27141     var markup = Roo.isIE ?
27142                  '<div class="x-ie-shadow"></div>' :
27143                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
27144     return {
27145         pull : function(){
27146             var sh = p.shift();
27147             if(!sh){
27148                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27149                 sh.autoBoxAdjust = false;
27150             }
27151             return sh;
27152         },
27153
27154         push : function(sh){
27155             p.push(sh);
27156         }
27157     };
27158 }();/*
27159  * Based on:
27160  * Ext JS Library 1.1.1
27161  * Copyright(c) 2006-2007, Ext JS, LLC.
27162  *
27163  * Originally Released Under LGPL - original licence link has changed is not relivant.
27164  *
27165  * Fork - LGPL
27166  * <script type="text/javascript">
27167  */
27168
27169
27170 /**
27171  * @class Roo.SplitBar
27172  * @extends Roo.util.Observable
27173  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27174  * <br><br>
27175  * Usage:
27176  * <pre><code>
27177 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27178                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27179 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27180 split.minSize = 100;
27181 split.maxSize = 600;
27182 split.animate = true;
27183 split.on('moved', splitterMoved);
27184 </code></pre>
27185  * @constructor
27186  * Create a new SplitBar
27187  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27188  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27189  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27190  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27191                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27192                         position of the SplitBar).
27193  */
27194 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27195     
27196     /** @private */
27197     this.el = Roo.get(dragElement, true);
27198     this.el.dom.unselectable = "on";
27199     /** @private */
27200     this.resizingEl = Roo.get(resizingElement, true);
27201
27202     /**
27203      * @private
27204      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27205      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27206      * @type Number
27207      */
27208     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27209     
27210     /**
27211      * The minimum size of the resizing element. (Defaults to 0)
27212      * @type Number
27213      */
27214     this.minSize = 0;
27215     
27216     /**
27217      * The maximum size of the resizing element. (Defaults to 2000)
27218      * @type Number
27219      */
27220     this.maxSize = 2000;
27221     
27222     /**
27223      * Whether to animate the transition to the new size
27224      * @type Boolean
27225      */
27226     this.animate = false;
27227     
27228     /**
27229      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27230      * @type Boolean
27231      */
27232     this.useShim = false;
27233     
27234     /** @private */
27235     this.shim = null;
27236     
27237     if(!existingProxy){
27238         /** @private */
27239         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27240     }else{
27241         this.proxy = Roo.get(existingProxy).dom;
27242     }
27243     /** @private */
27244     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27245     
27246     /** @private */
27247     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27248     
27249     /** @private */
27250     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27251     
27252     /** @private */
27253     this.dragSpecs = {};
27254     
27255     /**
27256      * @private The adapter to use to positon and resize elements
27257      */
27258     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27259     this.adapter.init(this);
27260     
27261     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27262         /** @private */
27263         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27264         this.el.addClass("x-splitbar-h");
27265     }else{
27266         /** @private */
27267         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27268         this.el.addClass("x-splitbar-v");
27269     }
27270     
27271     this.addEvents({
27272         /**
27273          * @event resize
27274          * Fires when the splitter is moved (alias for {@link #event-moved})
27275          * @param {Roo.SplitBar} this
27276          * @param {Number} newSize the new width or height
27277          */
27278         "resize" : true,
27279         /**
27280          * @event moved
27281          * Fires when the splitter is moved
27282          * @param {Roo.SplitBar} this
27283          * @param {Number} newSize the new width or height
27284          */
27285         "moved" : true,
27286         /**
27287          * @event beforeresize
27288          * Fires before the splitter is dragged
27289          * @param {Roo.SplitBar} this
27290          */
27291         "beforeresize" : true,
27292
27293         "beforeapply" : true
27294     });
27295
27296     Roo.util.Observable.call(this);
27297 };
27298
27299 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27300     onStartProxyDrag : function(x, y){
27301         this.fireEvent("beforeresize", this);
27302         if(!this.overlay){
27303             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27304             o.unselectable();
27305             o.enableDisplayMode("block");
27306             // all splitbars share the same overlay
27307             Roo.SplitBar.prototype.overlay = o;
27308         }
27309         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27310         this.overlay.show();
27311         Roo.get(this.proxy).setDisplayed("block");
27312         var size = this.adapter.getElementSize(this);
27313         this.activeMinSize = this.getMinimumSize();;
27314         this.activeMaxSize = this.getMaximumSize();;
27315         var c1 = size - this.activeMinSize;
27316         var c2 = Math.max(this.activeMaxSize - size, 0);
27317         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27318             this.dd.resetConstraints();
27319             this.dd.setXConstraint(
27320                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27321                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27322             );
27323             this.dd.setYConstraint(0, 0);
27324         }else{
27325             this.dd.resetConstraints();
27326             this.dd.setXConstraint(0, 0);
27327             this.dd.setYConstraint(
27328                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27329                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27330             );
27331          }
27332         this.dragSpecs.startSize = size;
27333         this.dragSpecs.startPoint = [x, y];
27334         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27335     },
27336     
27337     /** 
27338      * @private Called after the drag operation by the DDProxy
27339      */
27340     onEndProxyDrag : function(e){
27341         Roo.get(this.proxy).setDisplayed(false);
27342         var endPoint = Roo.lib.Event.getXY(e);
27343         if(this.overlay){
27344             this.overlay.hide();
27345         }
27346         var newSize;
27347         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27348             newSize = this.dragSpecs.startSize + 
27349                 (this.placement == Roo.SplitBar.LEFT ?
27350                     endPoint[0] - this.dragSpecs.startPoint[0] :
27351                     this.dragSpecs.startPoint[0] - endPoint[0]
27352                 );
27353         }else{
27354             newSize = this.dragSpecs.startSize + 
27355                 (this.placement == Roo.SplitBar.TOP ?
27356                     endPoint[1] - this.dragSpecs.startPoint[1] :
27357                     this.dragSpecs.startPoint[1] - endPoint[1]
27358                 );
27359         }
27360         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27361         if(newSize != this.dragSpecs.startSize){
27362             if(this.fireEvent('beforeapply', this, newSize) !== false){
27363                 this.adapter.setElementSize(this, newSize);
27364                 this.fireEvent("moved", this, newSize);
27365                 this.fireEvent("resize", this, newSize);
27366             }
27367         }
27368     },
27369     
27370     /**
27371      * Get the adapter this SplitBar uses
27372      * @return The adapter object
27373      */
27374     getAdapter : function(){
27375         return this.adapter;
27376     },
27377     
27378     /**
27379      * Set the adapter this SplitBar uses
27380      * @param {Object} adapter A SplitBar adapter object
27381      */
27382     setAdapter : function(adapter){
27383         this.adapter = adapter;
27384         this.adapter.init(this);
27385     },
27386     
27387     /**
27388      * Gets the minimum size for the resizing element
27389      * @return {Number} The minimum size
27390      */
27391     getMinimumSize : function(){
27392         return this.minSize;
27393     },
27394     
27395     /**
27396      * Sets the minimum size for the resizing element
27397      * @param {Number} minSize The minimum size
27398      */
27399     setMinimumSize : function(minSize){
27400         this.minSize = minSize;
27401     },
27402     
27403     /**
27404      * Gets the maximum size for the resizing element
27405      * @return {Number} The maximum size
27406      */
27407     getMaximumSize : function(){
27408         return this.maxSize;
27409     },
27410     
27411     /**
27412      * Sets the maximum size for the resizing element
27413      * @param {Number} maxSize The maximum size
27414      */
27415     setMaximumSize : function(maxSize){
27416         this.maxSize = maxSize;
27417     },
27418     
27419     /**
27420      * Sets the initialize size for the resizing element
27421      * @param {Number} size The initial size
27422      */
27423     setCurrentSize : function(size){
27424         var oldAnimate = this.animate;
27425         this.animate = false;
27426         this.adapter.setElementSize(this, size);
27427         this.animate = oldAnimate;
27428     },
27429     
27430     /**
27431      * Destroy this splitbar. 
27432      * @param {Boolean} removeEl True to remove the element
27433      */
27434     destroy : function(removeEl){
27435         if(this.shim){
27436             this.shim.remove();
27437         }
27438         this.dd.unreg();
27439         this.proxy.parentNode.removeChild(this.proxy);
27440         if(removeEl){
27441             this.el.remove();
27442         }
27443     }
27444 });
27445
27446 /**
27447  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
27448  */
27449 Roo.SplitBar.createProxy = function(dir){
27450     var proxy = new Roo.Element(document.createElement("div"));
27451     proxy.unselectable();
27452     var cls = 'x-splitbar-proxy';
27453     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27454     document.body.appendChild(proxy.dom);
27455     return proxy.dom;
27456 };
27457
27458 /** 
27459  * @class Roo.SplitBar.BasicLayoutAdapter
27460  * Default Adapter. It assumes the splitter and resizing element are not positioned
27461  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27462  */
27463 Roo.SplitBar.BasicLayoutAdapter = function(){
27464 };
27465
27466 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27467     // do nothing for now
27468     init : function(s){
27469     
27470     },
27471     /**
27472      * Called before drag operations to get the current size of the resizing element. 
27473      * @param {Roo.SplitBar} s The SplitBar using this adapter
27474      */
27475      getElementSize : function(s){
27476         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27477             return s.resizingEl.getWidth();
27478         }else{
27479             return s.resizingEl.getHeight();
27480         }
27481     },
27482     
27483     /**
27484      * Called after drag operations to set the size of the resizing element.
27485      * @param {Roo.SplitBar} s The SplitBar using this adapter
27486      * @param {Number} newSize The new size to set
27487      * @param {Function} onComplete A function to be invoked when resizing is complete
27488      */
27489     setElementSize : function(s, newSize, onComplete){
27490         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27491             if(!s.animate){
27492                 s.resizingEl.setWidth(newSize);
27493                 if(onComplete){
27494                     onComplete(s, newSize);
27495                 }
27496             }else{
27497                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27498             }
27499         }else{
27500             
27501             if(!s.animate){
27502                 s.resizingEl.setHeight(newSize);
27503                 if(onComplete){
27504                     onComplete(s, newSize);
27505                 }
27506             }else{
27507                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27508             }
27509         }
27510     }
27511 };
27512
27513 /** 
27514  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27515  * @extends Roo.SplitBar.BasicLayoutAdapter
27516  * Adapter that  moves the splitter element to align with the resized sizing element. 
27517  * Used with an absolute positioned SplitBar.
27518  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27519  * document.body, make sure you assign an id to the body element.
27520  */
27521 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27522     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27523     this.container = Roo.get(container);
27524 };
27525
27526 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27527     init : function(s){
27528         this.basic.init(s);
27529     },
27530     
27531     getElementSize : function(s){
27532         return this.basic.getElementSize(s);
27533     },
27534     
27535     setElementSize : function(s, newSize, onComplete){
27536         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27537     },
27538     
27539     moveSplitter : function(s){
27540         var yes = Roo.SplitBar;
27541         switch(s.placement){
27542             case yes.LEFT:
27543                 s.el.setX(s.resizingEl.getRight());
27544                 break;
27545             case yes.RIGHT:
27546                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27547                 break;
27548             case yes.TOP:
27549                 s.el.setY(s.resizingEl.getBottom());
27550                 break;
27551             case yes.BOTTOM:
27552                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27553                 break;
27554         }
27555     }
27556 };
27557
27558 /**
27559  * Orientation constant - Create a vertical SplitBar
27560  * @static
27561  * @type Number
27562  */
27563 Roo.SplitBar.VERTICAL = 1;
27564
27565 /**
27566  * Orientation constant - Create a horizontal SplitBar
27567  * @static
27568  * @type Number
27569  */
27570 Roo.SplitBar.HORIZONTAL = 2;
27571
27572 /**
27573  * Placement constant - The resizing element is to the left of the splitter element
27574  * @static
27575  * @type Number
27576  */
27577 Roo.SplitBar.LEFT = 1;
27578
27579 /**
27580  * Placement constant - The resizing element is to the right of the splitter element
27581  * @static
27582  * @type Number
27583  */
27584 Roo.SplitBar.RIGHT = 2;
27585
27586 /**
27587  * Placement constant - The resizing element is positioned above the splitter element
27588  * @static
27589  * @type Number
27590  */
27591 Roo.SplitBar.TOP = 3;
27592
27593 /**
27594  * Placement constant - The resizing element is positioned under splitter element
27595  * @static
27596  * @type Number
27597  */
27598 Roo.SplitBar.BOTTOM = 4;
27599 /*
27600  * Based on:
27601  * Ext JS Library 1.1.1
27602  * Copyright(c) 2006-2007, Ext JS, LLC.
27603  *
27604  * Originally Released Under LGPL - original licence link has changed is not relivant.
27605  *
27606  * Fork - LGPL
27607  * <script type="text/javascript">
27608  */
27609
27610 /**
27611  * @class Roo.View
27612  * @extends Roo.util.Observable
27613  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27614  * This class also supports single and multi selection modes. <br>
27615  * Create a data model bound view:
27616  <pre><code>
27617  var store = new Roo.data.Store(...);
27618
27619  var view = new Roo.View({
27620     el : "my-element",
27621     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27622  
27623     singleSelect: true,
27624     selectedClass: "ydataview-selected",
27625     store: store
27626  });
27627
27628  // listen for node click?
27629  view.on("click", function(vw, index, node, e){
27630  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27631  });
27632
27633  // load XML data
27634  dataModel.load("foobar.xml");
27635  </code></pre>
27636  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27637  * <br><br>
27638  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27639  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27640  * 
27641  * Note: old style constructor is still suported (container, template, config)
27642  * 
27643  * @constructor
27644  * Create a new View
27645  * @param {Object} config The config object
27646  * 
27647  */
27648 Roo.View = function(config, depreciated_tpl, depreciated_config){
27649     
27650     this.parent = false;
27651     
27652     if (typeof(depreciated_tpl) == 'undefined') {
27653         // new way.. - universal constructor.
27654         Roo.apply(this, config);
27655         this.el  = Roo.get(this.el);
27656     } else {
27657         // old format..
27658         this.el  = Roo.get(config);
27659         this.tpl = depreciated_tpl;
27660         Roo.apply(this, depreciated_config);
27661     }
27662     this.wrapEl  = this.el.wrap().wrap();
27663     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27664     
27665     
27666     if(typeof(this.tpl) == "string"){
27667         this.tpl = new Roo.Template(this.tpl);
27668     } else {
27669         // support xtype ctors..
27670         this.tpl = new Roo.factory(this.tpl, Roo);
27671     }
27672     
27673     
27674     this.tpl.compile();
27675     
27676     /** @private */
27677     this.addEvents({
27678         /**
27679          * @event beforeclick
27680          * Fires before a click is processed. Returns false to cancel the default action.
27681          * @param {Roo.View} this
27682          * @param {Number} index The index of the target node
27683          * @param {HTMLElement} node The target node
27684          * @param {Roo.EventObject} e The raw event object
27685          */
27686             "beforeclick" : true,
27687         /**
27688          * @event click
27689          * Fires when a template node is clicked.
27690          * @param {Roo.View} this
27691          * @param {Number} index The index of the target node
27692          * @param {HTMLElement} node The target node
27693          * @param {Roo.EventObject} e The raw event object
27694          */
27695             "click" : true,
27696         /**
27697          * @event dblclick
27698          * Fires when a template node is double clicked.
27699          * @param {Roo.View} this
27700          * @param {Number} index The index of the target node
27701          * @param {HTMLElement} node The target node
27702          * @param {Roo.EventObject} e The raw event object
27703          */
27704             "dblclick" : true,
27705         /**
27706          * @event contextmenu
27707          * Fires when a template node is right clicked.
27708          * @param {Roo.View} this
27709          * @param {Number} index The index of the target node
27710          * @param {HTMLElement} node The target node
27711          * @param {Roo.EventObject} e The raw event object
27712          */
27713             "contextmenu" : true,
27714         /**
27715          * @event selectionchange
27716          * Fires when the selected nodes change.
27717          * @param {Roo.View} this
27718          * @param {Array} selections Array of the selected nodes
27719          */
27720             "selectionchange" : true,
27721     
27722         /**
27723          * @event beforeselect
27724          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27725          * @param {Roo.View} this
27726          * @param {HTMLElement} node The node to be selected
27727          * @param {Array} selections Array of currently selected nodes
27728          */
27729             "beforeselect" : true,
27730         /**
27731          * @event preparedata
27732          * Fires on every row to render, to allow you to change the data.
27733          * @param {Roo.View} this
27734          * @param {Object} data to be rendered (change this)
27735          */
27736           "preparedata" : true
27737           
27738           
27739         });
27740
27741
27742
27743     this.el.on({
27744         "click": this.onClick,
27745         "dblclick": this.onDblClick,
27746         "contextmenu": this.onContextMenu,
27747         scope:this
27748     });
27749
27750     this.selections = [];
27751     this.nodes = [];
27752     this.cmp = new Roo.CompositeElementLite([]);
27753     if(this.store){
27754         this.store = Roo.factory(this.store, Roo.data);
27755         this.setStore(this.store, true);
27756     }
27757     
27758     if ( this.footer && this.footer.xtype) {
27759            
27760          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27761         
27762         this.footer.dataSource = this.store;
27763         this.footer.container = fctr;
27764         this.footer = Roo.factory(this.footer, Roo);
27765         fctr.insertFirst(this.el);
27766         
27767         // this is a bit insane - as the paging toolbar seems to detach the el..
27768 //        dom.parentNode.parentNode.parentNode
27769          // they get detached?
27770     }
27771     
27772     
27773     Roo.View.superclass.constructor.call(this);
27774     
27775     
27776 };
27777
27778 Roo.extend(Roo.View, Roo.util.Observable, {
27779     
27780      /**
27781      * @cfg {Roo.data.Store} store Data store to load data from.
27782      */
27783     store : false,
27784     
27785     /**
27786      * @cfg {String|Roo.Element} el The container element.
27787      */
27788     el : '',
27789     
27790     /**
27791      * @cfg {String|Roo.Template} tpl The template used by this View 
27792      */
27793     tpl : false,
27794     /**
27795      * @cfg {String} dataName the named area of the template to use as the data area
27796      *                          Works with domtemplates roo-name="name"
27797      */
27798     dataName: false,
27799     /**
27800      * @cfg {String} selectedClass The css class to add to selected nodes
27801      */
27802     selectedClass : "x-view-selected",
27803      /**
27804      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27805      */
27806     emptyText : "",
27807     
27808     /**
27809      * @cfg {String} text to display on mask (default Loading)
27810      */
27811     mask : false,
27812     /**
27813      * @cfg {Boolean} multiSelect Allow multiple selection
27814      */
27815     multiSelect : false,
27816     /**
27817      * @cfg {Boolean} singleSelect Allow single selection
27818      */
27819     singleSelect:  false,
27820     
27821     /**
27822      * @cfg {Boolean} toggleSelect - selecting 
27823      */
27824     toggleSelect : false,
27825     
27826     /**
27827      * @cfg {Boolean} tickable - selecting 
27828      */
27829     tickable : false,
27830     
27831     /**
27832      * Returns the element this view is bound to.
27833      * @return {Roo.Element}
27834      */
27835     getEl : function(){
27836         return this.wrapEl;
27837     },
27838     
27839     
27840
27841     /**
27842      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27843      */
27844     refresh : function(){
27845         //Roo.log('refresh');
27846         var t = this.tpl;
27847         
27848         // if we are using something like 'domtemplate', then
27849         // the what gets used is:
27850         // t.applySubtemplate(NAME, data, wrapping data..)
27851         // the outer template then get' applied with
27852         //     the store 'extra data'
27853         // and the body get's added to the
27854         //      roo-name="data" node?
27855         //      <span class='roo-tpl-{name}'></span> ?????
27856         
27857         
27858         
27859         this.clearSelections();
27860         this.el.update("");
27861         var html = [];
27862         var records = this.store.getRange();
27863         if(records.length < 1) {
27864             
27865             // is this valid??  = should it render a template??
27866             
27867             this.el.update(this.emptyText);
27868             return;
27869         }
27870         var el = this.el;
27871         if (this.dataName) {
27872             this.el.update(t.apply(this.store.meta)); //????
27873             el = this.el.child('.roo-tpl-' + this.dataName);
27874         }
27875         
27876         for(var i = 0, len = records.length; i < len; i++){
27877             var data = this.prepareData(records[i].data, i, records[i]);
27878             this.fireEvent("preparedata", this, data, i, records[i]);
27879             
27880             var d = Roo.apply({}, data);
27881             
27882             if(this.tickable){
27883                 Roo.apply(d, {'roo-id' : Roo.id()});
27884                 
27885                 var _this = this;
27886             
27887                 Roo.each(this.parent.item, function(item){
27888                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27889                         return;
27890                     }
27891                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27892                 });
27893             }
27894             
27895             html[html.length] = Roo.util.Format.trim(
27896                 this.dataName ?
27897                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27898                     t.apply(d)
27899             );
27900         }
27901         
27902         
27903         
27904         el.update(html.join(""));
27905         this.nodes = el.dom.childNodes;
27906         this.updateIndexes(0);
27907     },
27908     
27909
27910     /**
27911      * Function to override to reformat the data that is sent to
27912      * the template for each node.
27913      * DEPRICATED - use the preparedata event handler.
27914      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27915      * a JSON object for an UpdateManager bound view).
27916      */
27917     prepareData : function(data, index, record)
27918     {
27919         this.fireEvent("preparedata", this, data, index, record);
27920         return data;
27921     },
27922
27923     onUpdate : function(ds, record){
27924         // Roo.log('on update');   
27925         this.clearSelections();
27926         var index = this.store.indexOf(record);
27927         var n = this.nodes[index];
27928         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27929         n.parentNode.removeChild(n);
27930         this.updateIndexes(index, index);
27931     },
27932
27933     
27934     
27935 // --------- FIXME     
27936     onAdd : function(ds, records, index)
27937     {
27938         //Roo.log(['on Add', ds, records, index] );        
27939         this.clearSelections();
27940         if(this.nodes.length == 0){
27941             this.refresh();
27942             return;
27943         }
27944         var n = this.nodes[index];
27945         for(var i = 0, len = records.length; i < len; i++){
27946             var d = this.prepareData(records[i].data, i, records[i]);
27947             if(n){
27948                 this.tpl.insertBefore(n, d);
27949             }else{
27950                 
27951                 this.tpl.append(this.el, d);
27952             }
27953         }
27954         this.updateIndexes(index);
27955     },
27956
27957     onRemove : function(ds, record, index){
27958        // Roo.log('onRemove');
27959         this.clearSelections();
27960         var el = this.dataName  ?
27961             this.el.child('.roo-tpl-' + this.dataName) :
27962             this.el; 
27963         
27964         el.dom.removeChild(this.nodes[index]);
27965         this.updateIndexes(index);
27966     },
27967
27968     /**
27969      * Refresh an individual node.
27970      * @param {Number} index
27971      */
27972     refreshNode : function(index){
27973         this.onUpdate(this.store, this.store.getAt(index));
27974     },
27975
27976     updateIndexes : function(startIndex, endIndex){
27977         var ns = this.nodes;
27978         startIndex = startIndex || 0;
27979         endIndex = endIndex || ns.length - 1;
27980         for(var i = startIndex; i <= endIndex; i++){
27981             ns[i].nodeIndex = i;
27982         }
27983     },
27984
27985     /**
27986      * Changes the data store this view uses and refresh the view.
27987      * @param {Store} store
27988      */
27989     setStore : function(store, initial){
27990         if(!initial && this.store){
27991             this.store.un("datachanged", this.refresh);
27992             this.store.un("add", this.onAdd);
27993             this.store.un("remove", this.onRemove);
27994             this.store.un("update", this.onUpdate);
27995             this.store.un("clear", this.refresh);
27996             this.store.un("beforeload", this.onBeforeLoad);
27997             this.store.un("load", this.onLoad);
27998             this.store.un("loadexception", this.onLoad);
27999         }
28000         if(store){
28001           
28002             store.on("datachanged", this.refresh, this);
28003             store.on("add", this.onAdd, this);
28004             store.on("remove", this.onRemove, this);
28005             store.on("update", this.onUpdate, this);
28006             store.on("clear", this.refresh, this);
28007             store.on("beforeload", this.onBeforeLoad, this);
28008             store.on("load", this.onLoad, this);
28009             store.on("loadexception", this.onLoad, this);
28010         }
28011         
28012         if(store){
28013             this.refresh();
28014         }
28015     },
28016     /**
28017      * onbeforeLoad - masks the loading area.
28018      *
28019      */
28020     onBeforeLoad : function(store,opts)
28021     {
28022          //Roo.log('onBeforeLoad');   
28023         if (!opts.add) {
28024             this.el.update("");
28025         }
28026         this.el.mask(this.mask ? this.mask : "Loading" ); 
28027     },
28028     onLoad : function ()
28029     {
28030         this.el.unmask();
28031     },
28032     
28033
28034     /**
28035      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28036      * @param {HTMLElement} node
28037      * @return {HTMLElement} The template node
28038      */
28039     findItemFromChild : function(node){
28040         var el = this.dataName  ?
28041             this.el.child('.roo-tpl-' + this.dataName,true) :
28042             this.el.dom; 
28043         
28044         if(!node || node.parentNode == el){
28045                     return node;
28046             }
28047             var p = node.parentNode;
28048             while(p && p != el){
28049             if(p.parentNode == el){
28050                 return p;
28051             }
28052             p = p.parentNode;
28053         }
28054             return null;
28055     },
28056
28057     /** @ignore */
28058     onClick : function(e){
28059         var item = this.findItemFromChild(e.getTarget());
28060         if(item){
28061             var index = this.indexOf(item);
28062             if(this.onItemClick(item, index, e) !== false){
28063                 this.fireEvent("click", this, index, item, e);
28064             }
28065         }else{
28066             this.clearSelections();
28067         }
28068     },
28069
28070     /** @ignore */
28071     onContextMenu : function(e){
28072         var item = this.findItemFromChild(e.getTarget());
28073         if(item){
28074             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28075         }
28076     },
28077
28078     /** @ignore */
28079     onDblClick : function(e){
28080         var item = this.findItemFromChild(e.getTarget());
28081         if(item){
28082             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28083         }
28084     },
28085
28086     onItemClick : function(item, index, e)
28087     {
28088         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28089             return false;
28090         }
28091         if (this.toggleSelect) {
28092             var m = this.isSelected(item) ? 'unselect' : 'select';
28093             //Roo.log(m);
28094             var _t = this;
28095             _t[m](item, true, false);
28096             return true;
28097         }
28098         if(this.multiSelect || this.singleSelect){
28099             if(this.multiSelect && e.shiftKey && this.lastSelection){
28100                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28101             }else{
28102                 this.select(item, this.multiSelect && e.ctrlKey);
28103                 this.lastSelection = item;
28104             }
28105             
28106             if(!this.tickable){
28107                 e.preventDefault();
28108             }
28109             
28110         }
28111         return true;
28112     },
28113
28114     /**
28115      * Get the number of selected nodes.
28116      * @return {Number}
28117      */
28118     getSelectionCount : function(){
28119         return this.selections.length;
28120     },
28121
28122     /**
28123      * Get the currently selected nodes.
28124      * @return {Array} An array of HTMLElements
28125      */
28126     getSelectedNodes : function(){
28127         return this.selections;
28128     },
28129
28130     /**
28131      * Get the indexes of the selected nodes.
28132      * @return {Array}
28133      */
28134     getSelectedIndexes : function(){
28135         var indexes = [], s = this.selections;
28136         for(var i = 0, len = s.length; i < len; i++){
28137             indexes.push(s[i].nodeIndex);
28138         }
28139         return indexes;
28140     },
28141
28142     /**
28143      * Clear all selections
28144      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28145      */
28146     clearSelections : function(suppressEvent){
28147         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28148             this.cmp.elements = this.selections;
28149             this.cmp.removeClass(this.selectedClass);
28150             this.selections = [];
28151             if(!suppressEvent){
28152                 this.fireEvent("selectionchange", this, this.selections);
28153             }
28154         }
28155     },
28156
28157     /**
28158      * Returns true if the passed node is selected
28159      * @param {HTMLElement/Number} node The node or node index
28160      * @return {Boolean}
28161      */
28162     isSelected : function(node){
28163         var s = this.selections;
28164         if(s.length < 1){
28165             return false;
28166         }
28167         node = this.getNode(node);
28168         return s.indexOf(node) !== -1;
28169     },
28170
28171     /**
28172      * Selects nodes.
28173      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
28174      * @param {Boolean} keepExisting (optional) true to keep existing selections
28175      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28176      */
28177     select : function(nodeInfo, keepExisting, suppressEvent){
28178         if(nodeInfo instanceof Array){
28179             if(!keepExisting){
28180                 this.clearSelections(true);
28181             }
28182             for(var i = 0, len = nodeInfo.length; i < len; i++){
28183                 this.select(nodeInfo[i], true, true);
28184             }
28185             return;
28186         } 
28187         var node = this.getNode(nodeInfo);
28188         if(!node || this.isSelected(node)){
28189             return; // already selected.
28190         }
28191         if(!keepExisting){
28192             this.clearSelections(true);
28193         }
28194         
28195         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28196             Roo.fly(node).addClass(this.selectedClass);
28197             this.selections.push(node);
28198             if(!suppressEvent){
28199                 this.fireEvent("selectionchange", this, this.selections);
28200             }
28201         }
28202         
28203         
28204     },
28205       /**
28206      * Unselects nodes.
28207      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
28208      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28209      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28210      */
28211     unselect : function(nodeInfo, keepExisting, suppressEvent)
28212     {
28213         if(nodeInfo instanceof Array){
28214             Roo.each(this.selections, function(s) {
28215                 this.unselect(s, nodeInfo);
28216             }, this);
28217             return;
28218         }
28219         var node = this.getNode(nodeInfo);
28220         if(!node || !this.isSelected(node)){
28221             //Roo.log("not selected");
28222             return; // not selected.
28223         }
28224         // fireevent???
28225         var ns = [];
28226         Roo.each(this.selections, function(s) {
28227             if (s == node ) {
28228                 Roo.fly(node).removeClass(this.selectedClass);
28229
28230                 return;
28231             }
28232             ns.push(s);
28233         },this);
28234         
28235         this.selections= ns;
28236         this.fireEvent("selectionchange", this, this.selections);
28237     },
28238
28239     /**
28240      * Gets a template node.
28241      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28242      * @return {HTMLElement} The node or null if it wasn't found
28243      */
28244     getNode : function(nodeInfo){
28245         if(typeof nodeInfo == "string"){
28246             return document.getElementById(nodeInfo);
28247         }else if(typeof nodeInfo == "number"){
28248             return this.nodes[nodeInfo];
28249         }
28250         return nodeInfo;
28251     },
28252
28253     /**
28254      * Gets a range template nodes.
28255      * @param {Number} startIndex
28256      * @param {Number} endIndex
28257      * @return {Array} An array of nodes
28258      */
28259     getNodes : function(start, end){
28260         var ns = this.nodes;
28261         start = start || 0;
28262         end = typeof end == "undefined" ? ns.length - 1 : end;
28263         var nodes = [];
28264         if(start <= end){
28265             for(var i = start; i <= end; i++){
28266                 nodes.push(ns[i]);
28267             }
28268         } else{
28269             for(var i = start; i >= end; i--){
28270                 nodes.push(ns[i]);
28271             }
28272         }
28273         return nodes;
28274     },
28275
28276     /**
28277      * Finds the index of the passed node
28278      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28279      * @return {Number} The index of the node or -1
28280      */
28281     indexOf : function(node){
28282         node = this.getNode(node);
28283         if(typeof node.nodeIndex == "number"){
28284             return node.nodeIndex;
28285         }
28286         var ns = this.nodes;
28287         for(var i = 0, len = ns.length; i < len; i++){
28288             if(ns[i] == node){
28289                 return i;
28290             }
28291         }
28292         return -1;
28293     }
28294 });
28295 /*
28296  * Based on:
28297  * Ext JS Library 1.1.1
28298  * Copyright(c) 2006-2007, Ext JS, LLC.
28299  *
28300  * Originally Released Under LGPL - original licence link has changed is not relivant.
28301  *
28302  * Fork - LGPL
28303  * <script type="text/javascript">
28304  */
28305
28306 /**
28307  * @class Roo.JsonView
28308  * @extends Roo.View
28309  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28310 <pre><code>
28311 var view = new Roo.JsonView({
28312     container: "my-element",
28313     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28314     multiSelect: true, 
28315     jsonRoot: "data" 
28316 });
28317
28318 // listen for node click?
28319 view.on("click", function(vw, index, node, e){
28320     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28321 });
28322
28323 // direct load of JSON data
28324 view.load("foobar.php");
28325
28326 // Example from my blog list
28327 var tpl = new Roo.Template(
28328     '&lt;div class="entry"&gt;' +
28329     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28330     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28331     "&lt;/div&gt;&lt;hr /&gt;"
28332 );
28333
28334 var moreView = new Roo.JsonView({
28335     container :  "entry-list", 
28336     template : tpl,
28337     jsonRoot: "posts"
28338 });
28339 moreView.on("beforerender", this.sortEntries, this);
28340 moreView.load({
28341     url: "/blog/get-posts.php",
28342     params: "allposts=true",
28343     text: "Loading Blog Entries..."
28344 });
28345 </code></pre>
28346
28347 * Note: old code is supported with arguments : (container, template, config)
28348
28349
28350  * @constructor
28351  * Create a new JsonView
28352  * 
28353  * @param {Object} config The config object
28354  * 
28355  */
28356 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28357     
28358     
28359     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28360
28361     var um = this.el.getUpdateManager();
28362     um.setRenderer(this);
28363     um.on("update", this.onLoad, this);
28364     um.on("failure", this.onLoadException, this);
28365
28366     /**
28367      * @event beforerender
28368      * Fires before rendering of the downloaded JSON data.
28369      * @param {Roo.JsonView} this
28370      * @param {Object} data The JSON data loaded
28371      */
28372     /**
28373      * @event load
28374      * Fires when data is loaded.
28375      * @param {Roo.JsonView} this
28376      * @param {Object} data The JSON data loaded
28377      * @param {Object} response The raw Connect response object
28378      */
28379     /**
28380      * @event loadexception
28381      * Fires when loading fails.
28382      * @param {Roo.JsonView} this
28383      * @param {Object} response The raw Connect response object
28384      */
28385     this.addEvents({
28386         'beforerender' : true,
28387         'load' : true,
28388         'loadexception' : true
28389     });
28390 };
28391 Roo.extend(Roo.JsonView, Roo.View, {
28392     /**
28393      * @type {String} The root property in the loaded JSON object that contains the data
28394      */
28395     jsonRoot : "",
28396
28397     /**
28398      * Refreshes the view.
28399      */
28400     refresh : function(){
28401         this.clearSelections();
28402         this.el.update("");
28403         var html = [];
28404         var o = this.jsonData;
28405         if(o && o.length > 0){
28406             for(var i = 0, len = o.length; i < len; i++){
28407                 var data = this.prepareData(o[i], i, o);
28408                 html[html.length] = this.tpl.apply(data);
28409             }
28410         }else{
28411             html.push(this.emptyText);
28412         }
28413         this.el.update(html.join(""));
28414         this.nodes = this.el.dom.childNodes;
28415         this.updateIndexes(0);
28416     },
28417
28418     /**
28419      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
28420      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
28421      <pre><code>
28422      view.load({
28423          url: "your-url.php",
28424          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28425          callback: yourFunction,
28426          scope: yourObject, //(optional scope)
28427          discardUrl: false,
28428          nocache: false,
28429          text: "Loading...",
28430          timeout: 30,
28431          scripts: false
28432      });
28433      </code></pre>
28434      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28435      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
28436      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
28437      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28438      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
28439      */
28440     load : function(){
28441         var um = this.el.getUpdateManager();
28442         um.update.apply(um, arguments);
28443     },
28444
28445     // note - render is a standard framework call...
28446     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28447     render : function(el, response){
28448         
28449         this.clearSelections();
28450         this.el.update("");
28451         var o;
28452         try{
28453             if (response != '') {
28454                 o = Roo.util.JSON.decode(response.responseText);
28455                 if(this.jsonRoot){
28456                     
28457                     o = o[this.jsonRoot];
28458                 }
28459             }
28460         } catch(e){
28461         }
28462         /**
28463          * The current JSON data or null
28464          */
28465         this.jsonData = o;
28466         this.beforeRender();
28467         this.refresh();
28468     },
28469
28470 /**
28471  * Get the number of records in the current JSON dataset
28472  * @return {Number}
28473  */
28474     getCount : function(){
28475         return this.jsonData ? this.jsonData.length : 0;
28476     },
28477
28478 /**
28479  * Returns the JSON object for the specified node(s)
28480  * @param {HTMLElement/Array} node The node or an array of nodes
28481  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28482  * you get the JSON object for the node
28483  */
28484     getNodeData : function(node){
28485         if(node instanceof Array){
28486             var data = [];
28487             for(var i = 0, len = node.length; i < len; i++){
28488                 data.push(this.getNodeData(node[i]));
28489             }
28490             return data;
28491         }
28492         return this.jsonData[this.indexOf(node)] || null;
28493     },
28494
28495     beforeRender : function(){
28496         this.snapshot = this.jsonData;
28497         if(this.sortInfo){
28498             this.sort.apply(this, this.sortInfo);
28499         }
28500         this.fireEvent("beforerender", this, this.jsonData);
28501     },
28502
28503     onLoad : function(el, o){
28504         this.fireEvent("load", this, this.jsonData, o);
28505     },
28506
28507     onLoadException : function(el, o){
28508         this.fireEvent("loadexception", this, o);
28509     },
28510
28511 /**
28512  * Filter the data by a specific property.
28513  * @param {String} property A property on your JSON objects
28514  * @param {String/RegExp} value Either string that the property values
28515  * should start with, or a RegExp to test against the property
28516  */
28517     filter : function(property, value){
28518         if(this.jsonData){
28519             var data = [];
28520             var ss = this.snapshot;
28521             if(typeof value == "string"){
28522                 var vlen = value.length;
28523                 if(vlen == 0){
28524                     this.clearFilter();
28525                     return;
28526                 }
28527                 value = value.toLowerCase();
28528                 for(var i = 0, len = ss.length; i < len; i++){
28529                     var o = ss[i];
28530                     if(o[property].substr(0, vlen).toLowerCase() == value){
28531                         data.push(o);
28532                     }
28533                 }
28534             } else if(value.exec){ // regex?
28535                 for(var i = 0, len = ss.length; i < len; i++){
28536                     var o = ss[i];
28537                     if(value.test(o[property])){
28538                         data.push(o);
28539                     }
28540                 }
28541             } else{
28542                 return;
28543             }
28544             this.jsonData = data;
28545             this.refresh();
28546         }
28547     },
28548
28549 /**
28550  * Filter by a function. The passed function will be called with each
28551  * object in the current dataset. If the function returns true the value is kept,
28552  * otherwise it is filtered.
28553  * @param {Function} fn
28554  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28555  */
28556     filterBy : function(fn, scope){
28557         if(this.jsonData){
28558             var data = [];
28559             var ss = this.snapshot;
28560             for(var i = 0, len = ss.length; i < len; i++){
28561                 var o = ss[i];
28562                 if(fn.call(scope || this, o)){
28563                     data.push(o);
28564                 }
28565             }
28566             this.jsonData = data;
28567             this.refresh();
28568         }
28569     },
28570
28571 /**
28572  * Clears the current filter.
28573  */
28574     clearFilter : function(){
28575         if(this.snapshot && this.jsonData != this.snapshot){
28576             this.jsonData = this.snapshot;
28577             this.refresh();
28578         }
28579     },
28580
28581
28582 /**
28583  * Sorts the data for this view and refreshes it.
28584  * @param {String} property A property on your JSON objects to sort on
28585  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28586  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28587  */
28588     sort : function(property, dir, sortType){
28589         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28590         if(this.jsonData){
28591             var p = property;
28592             var dsc = dir && dir.toLowerCase() == "desc";
28593             var f = function(o1, o2){
28594                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28595                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28596                 ;
28597                 if(v1 < v2){
28598                     return dsc ? +1 : -1;
28599                 } else if(v1 > v2){
28600                     return dsc ? -1 : +1;
28601                 } else{
28602                     return 0;
28603                 }
28604             };
28605             this.jsonData.sort(f);
28606             this.refresh();
28607             if(this.jsonData != this.snapshot){
28608                 this.snapshot.sort(f);
28609             }
28610         }
28611     }
28612 });/*
28613  * Based on:
28614  * Ext JS Library 1.1.1
28615  * Copyright(c) 2006-2007, Ext JS, LLC.
28616  *
28617  * Originally Released Under LGPL - original licence link has changed is not relivant.
28618  *
28619  * Fork - LGPL
28620  * <script type="text/javascript">
28621  */
28622  
28623
28624 /**
28625  * @class Roo.ColorPalette
28626  * @extends Roo.Component
28627  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28628  * Here's an example of typical usage:
28629  * <pre><code>
28630 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28631 cp.render('my-div');
28632
28633 cp.on('select', function(palette, selColor){
28634     // do something with selColor
28635 });
28636 </code></pre>
28637  * @constructor
28638  * Create a new ColorPalette
28639  * @param {Object} config The config object
28640  */
28641 Roo.ColorPalette = function(config){
28642     Roo.ColorPalette.superclass.constructor.call(this, config);
28643     this.addEvents({
28644         /**
28645              * @event select
28646              * Fires when a color is selected
28647              * @param {ColorPalette} this
28648              * @param {String} color The 6-digit color hex code (without the # symbol)
28649              */
28650         select: true
28651     });
28652
28653     if(this.handler){
28654         this.on("select", this.handler, this.scope, true);
28655     }
28656 };
28657 Roo.extend(Roo.ColorPalette, Roo.Component, {
28658     /**
28659      * @cfg {String} itemCls
28660      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28661      */
28662     itemCls : "x-color-palette",
28663     /**
28664      * @cfg {String} value
28665      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28666      * the hex codes are case-sensitive.
28667      */
28668     value : null,
28669     clickEvent:'click',
28670     // private
28671     ctype: "Roo.ColorPalette",
28672
28673     /**
28674      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28675      */
28676     allowReselect : false,
28677
28678     /**
28679      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28680      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28681      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28682      * of colors with the width setting until the box is symmetrical.</p>
28683      * <p>You can override individual colors if needed:</p>
28684      * <pre><code>
28685 var cp = new Roo.ColorPalette();
28686 cp.colors[0] = "FF0000";  // change the first box to red
28687 </code></pre>
28688
28689 Or you can provide a custom array of your own for complete control:
28690 <pre><code>
28691 var cp = new Roo.ColorPalette();
28692 cp.colors = ["000000", "993300", "333300"];
28693 </code></pre>
28694      * @type Array
28695      */
28696     colors : [
28697         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28698         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28699         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28700         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28701         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28702     ],
28703
28704     // private
28705     onRender : function(container, position){
28706         var t = new Roo.MasterTemplate(
28707             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28708         );
28709         var c = this.colors;
28710         for(var i = 0, len = c.length; i < len; i++){
28711             t.add([c[i]]);
28712         }
28713         var el = document.createElement("div");
28714         el.className = this.itemCls;
28715         t.overwrite(el);
28716         container.dom.insertBefore(el, position);
28717         this.el = Roo.get(el);
28718         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28719         if(this.clickEvent != 'click'){
28720             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28721         }
28722     },
28723
28724     // private
28725     afterRender : function(){
28726         Roo.ColorPalette.superclass.afterRender.call(this);
28727         if(this.value){
28728             var s = this.value;
28729             this.value = null;
28730             this.select(s);
28731         }
28732     },
28733
28734     // private
28735     handleClick : function(e, t){
28736         e.preventDefault();
28737         if(!this.disabled){
28738             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28739             this.select(c.toUpperCase());
28740         }
28741     },
28742
28743     /**
28744      * Selects the specified color in the palette (fires the select event)
28745      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28746      */
28747     select : function(color){
28748         color = color.replace("#", "");
28749         if(color != this.value || this.allowReselect){
28750             var el = this.el;
28751             if(this.value){
28752                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28753             }
28754             el.child("a.color-"+color).addClass("x-color-palette-sel");
28755             this.value = color;
28756             this.fireEvent("select", this, color);
28757         }
28758     }
28759 });/*
28760  * Based on:
28761  * Ext JS Library 1.1.1
28762  * Copyright(c) 2006-2007, Ext JS, LLC.
28763  *
28764  * Originally Released Under LGPL - original licence link has changed is not relivant.
28765  *
28766  * Fork - LGPL
28767  * <script type="text/javascript">
28768  */
28769  
28770 /**
28771  * @class Roo.DatePicker
28772  * @extends Roo.Component
28773  * Simple date picker class.
28774  * @constructor
28775  * Create a new DatePicker
28776  * @param {Object} config The config object
28777  */
28778 Roo.DatePicker = function(config){
28779     Roo.DatePicker.superclass.constructor.call(this, config);
28780
28781     this.value = config && config.value ?
28782                  config.value.clearTime() : new Date().clearTime();
28783
28784     this.addEvents({
28785         /**
28786              * @event select
28787              * Fires when a date is selected
28788              * @param {DatePicker} this
28789              * @param {Date} date The selected date
28790              */
28791         'select': true,
28792         /**
28793              * @event monthchange
28794              * Fires when the displayed month changes 
28795              * @param {DatePicker} this
28796              * @param {Date} date The selected month
28797              */
28798         'monthchange': true
28799     });
28800
28801     if(this.handler){
28802         this.on("select", this.handler,  this.scope || this);
28803     }
28804     // build the disabledDatesRE
28805     if(!this.disabledDatesRE && this.disabledDates){
28806         var dd = this.disabledDates;
28807         var re = "(?:";
28808         for(var i = 0; i < dd.length; i++){
28809             re += dd[i];
28810             if(i != dd.length-1) {
28811                 re += "|";
28812             }
28813         }
28814         this.disabledDatesRE = new RegExp(re + ")");
28815     }
28816 };
28817
28818 Roo.extend(Roo.DatePicker, Roo.Component, {
28819     /**
28820      * @cfg {String} todayText
28821      * The text to display on the button that selects the current date (defaults to "Today")
28822      */
28823     todayText : "Today",
28824     /**
28825      * @cfg {String} okText
28826      * The text to display on the ok button
28827      */
28828     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28829     /**
28830      * @cfg {String} cancelText
28831      * The text to display on the cancel button
28832      */
28833     cancelText : "Cancel",
28834     /**
28835      * @cfg {String} todayTip
28836      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28837      */
28838     todayTip : "{0} (Spacebar)",
28839     /**
28840      * @cfg {Date} minDate
28841      * Minimum allowable date (JavaScript date object, defaults to null)
28842      */
28843     minDate : null,
28844     /**
28845      * @cfg {Date} maxDate
28846      * Maximum allowable date (JavaScript date object, defaults to null)
28847      */
28848     maxDate : null,
28849     /**
28850      * @cfg {String} minText
28851      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28852      */
28853     minText : "This date is before the minimum date",
28854     /**
28855      * @cfg {String} maxText
28856      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28857      */
28858     maxText : "This date is after the maximum date",
28859     /**
28860      * @cfg {String} format
28861      * The default date format string which can be overriden for localization support.  The format must be
28862      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28863      */
28864     format : "m/d/y",
28865     /**
28866      * @cfg {Array} disabledDays
28867      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28868      */
28869     disabledDays : null,
28870     /**
28871      * @cfg {String} disabledDaysText
28872      * The tooltip to display when the date falls on a disabled day (defaults to "")
28873      */
28874     disabledDaysText : "",
28875     /**
28876      * @cfg {RegExp} disabledDatesRE
28877      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28878      */
28879     disabledDatesRE : null,
28880     /**
28881      * @cfg {String} disabledDatesText
28882      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28883      */
28884     disabledDatesText : "",
28885     /**
28886      * @cfg {Boolean} constrainToViewport
28887      * True to constrain the date picker to the viewport (defaults to true)
28888      */
28889     constrainToViewport : true,
28890     /**
28891      * @cfg {Array} monthNames
28892      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28893      */
28894     monthNames : Date.monthNames,
28895     /**
28896      * @cfg {Array} dayNames
28897      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28898      */
28899     dayNames : Date.dayNames,
28900     /**
28901      * @cfg {String} nextText
28902      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28903      */
28904     nextText: 'Next Month (Control+Right)',
28905     /**
28906      * @cfg {String} prevText
28907      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28908      */
28909     prevText: 'Previous Month (Control+Left)',
28910     /**
28911      * @cfg {String} monthYearText
28912      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28913      */
28914     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28915     /**
28916      * @cfg {Number} startDay
28917      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28918      */
28919     startDay : 0,
28920     /**
28921      * @cfg {Bool} showClear
28922      * Show a clear button (usefull for date form elements that can be blank.)
28923      */
28924     
28925     showClear: false,
28926     
28927     /**
28928      * Sets the value of the date field
28929      * @param {Date} value The date to set
28930      */
28931     setValue : function(value){
28932         var old = this.value;
28933         
28934         if (typeof(value) == 'string') {
28935          
28936             value = Date.parseDate(value, this.format);
28937         }
28938         if (!value) {
28939             value = new Date();
28940         }
28941         
28942         this.value = value.clearTime(true);
28943         if(this.el){
28944             this.update(this.value);
28945         }
28946     },
28947
28948     /**
28949      * Gets the current selected value of the date field
28950      * @return {Date} The selected date
28951      */
28952     getValue : function(){
28953         return this.value;
28954     },
28955
28956     // private
28957     focus : function(){
28958         if(this.el){
28959             this.update(this.activeDate);
28960         }
28961     },
28962
28963     // privateval
28964     onRender : function(container, position){
28965         
28966         var m = [
28967              '<table cellspacing="0">',
28968                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
28969                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28970         var dn = this.dayNames;
28971         for(var i = 0; i < 7; i++){
28972             var d = this.startDay+i;
28973             if(d > 6){
28974                 d = d-7;
28975             }
28976             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28977         }
28978         m[m.length] = "</tr></thead><tbody><tr>";
28979         for(var i = 0; i < 42; i++) {
28980             if(i % 7 == 0 && i != 0){
28981                 m[m.length] = "</tr><tr>";
28982             }
28983             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28984         }
28985         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28986             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28987
28988         var el = document.createElement("div");
28989         el.className = "x-date-picker";
28990         el.innerHTML = m.join("");
28991
28992         container.dom.insertBefore(el, position);
28993
28994         this.el = Roo.get(el);
28995         this.eventEl = Roo.get(el.firstChild);
28996
28997         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28998             handler: this.showPrevMonth,
28999             scope: this,
29000             preventDefault:true,
29001             stopDefault:true
29002         });
29003
29004         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29005             handler: this.showNextMonth,
29006             scope: this,
29007             preventDefault:true,
29008             stopDefault:true
29009         });
29010
29011         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29012
29013         this.monthPicker = this.el.down('div.x-date-mp');
29014         this.monthPicker.enableDisplayMode('block');
29015         
29016         var kn = new Roo.KeyNav(this.eventEl, {
29017             "left" : function(e){
29018                 e.ctrlKey ?
29019                     this.showPrevMonth() :
29020                     this.update(this.activeDate.add("d", -1));
29021             },
29022
29023             "right" : function(e){
29024                 e.ctrlKey ?
29025                     this.showNextMonth() :
29026                     this.update(this.activeDate.add("d", 1));
29027             },
29028
29029             "up" : function(e){
29030                 e.ctrlKey ?
29031                     this.showNextYear() :
29032                     this.update(this.activeDate.add("d", -7));
29033             },
29034
29035             "down" : function(e){
29036                 e.ctrlKey ?
29037                     this.showPrevYear() :
29038                     this.update(this.activeDate.add("d", 7));
29039             },
29040
29041             "pageUp" : function(e){
29042                 this.showNextMonth();
29043             },
29044
29045             "pageDown" : function(e){
29046                 this.showPrevMonth();
29047             },
29048
29049             "enter" : function(e){
29050                 e.stopPropagation();
29051                 return true;
29052             },
29053
29054             scope : this
29055         });
29056
29057         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29058
29059         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29060
29061         this.el.unselectable();
29062         
29063         this.cells = this.el.select("table.x-date-inner tbody td");
29064         this.textNodes = this.el.query("table.x-date-inner tbody span");
29065
29066         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29067             text: "&#160;",
29068             tooltip: this.monthYearText
29069         });
29070
29071         this.mbtn.on('click', this.showMonthPicker, this);
29072         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29073
29074
29075         var today = (new Date()).dateFormat(this.format);
29076         
29077         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29078         if (this.showClear) {
29079             baseTb.add( new Roo.Toolbar.Fill());
29080         }
29081         baseTb.add({
29082             text: String.format(this.todayText, today),
29083             tooltip: String.format(this.todayTip, today),
29084             handler: this.selectToday,
29085             scope: this
29086         });
29087         
29088         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29089             
29090         //});
29091         if (this.showClear) {
29092             
29093             baseTb.add( new Roo.Toolbar.Fill());
29094             baseTb.add({
29095                 text: '&#160;',
29096                 cls: 'x-btn-icon x-btn-clear',
29097                 handler: function() {
29098                     //this.value = '';
29099                     this.fireEvent("select", this, '');
29100                 },
29101                 scope: this
29102             });
29103         }
29104         
29105         
29106         if(Roo.isIE){
29107             this.el.repaint();
29108         }
29109         this.update(this.value);
29110     },
29111
29112     createMonthPicker : function(){
29113         if(!this.monthPicker.dom.firstChild){
29114             var buf = ['<table border="0" cellspacing="0">'];
29115             for(var i = 0; i < 6; i++){
29116                 buf.push(
29117                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29118                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29119                     i == 0 ?
29120                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
29121                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29122                 );
29123             }
29124             buf.push(
29125                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29126                     this.okText,
29127                     '</button><button type="button" class="x-date-mp-cancel">',
29128                     this.cancelText,
29129                     '</button></td></tr>',
29130                 '</table>'
29131             );
29132             this.monthPicker.update(buf.join(''));
29133             this.monthPicker.on('click', this.onMonthClick, this);
29134             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29135
29136             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29137             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29138
29139             this.mpMonths.each(function(m, a, i){
29140                 i += 1;
29141                 if((i%2) == 0){
29142                     m.dom.xmonth = 5 + Math.round(i * .5);
29143                 }else{
29144                     m.dom.xmonth = Math.round((i-1) * .5);
29145                 }
29146             });
29147         }
29148     },
29149
29150     showMonthPicker : function(){
29151         this.createMonthPicker();
29152         var size = this.el.getSize();
29153         this.monthPicker.setSize(size);
29154         this.monthPicker.child('table').setSize(size);
29155
29156         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29157         this.updateMPMonth(this.mpSelMonth);
29158         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29159         this.updateMPYear(this.mpSelYear);
29160
29161         this.monthPicker.slideIn('t', {duration:.2});
29162     },
29163
29164     updateMPYear : function(y){
29165         this.mpyear = y;
29166         var ys = this.mpYears.elements;
29167         for(var i = 1; i <= 10; i++){
29168             var td = ys[i-1], y2;
29169             if((i%2) == 0){
29170                 y2 = y + Math.round(i * .5);
29171                 td.firstChild.innerHTML = y2;
29172                 td.xyear = y2;
29173             }else{
29174                 y2 = y - (5-Math.round(i * .5));
29175                 td.firstChild.innerHTML = y2;
29176                 td.xyear = y2;
29177             }
29178             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29179         }
29180     },
29181
29182     updateMPMonth : function(sm){
29183         this.mpMonths.each(function(m, a, i){
29184             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29185         });
29186     },
29187
29188     selectMPMonth: function(m){
29189         
29190     },
29191
29192     onMonthClick : function(e, t){
29193         e.stopEvent();
29194         var el = new Roo.Element(t), pn;
29195         if(el.is('button.x-date-mp-cancel')){
29196             this.hideMonthPicker();
29197         }
29198         else if(el.is('button.x-date-mp-ok')){
29199             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29200             this.hideMonthPicker();
29201         }
29202         else if(pn = el.up('td.x-date-mp-month', 2)){
29203             this.mpMonths.removeClass('x-date-mp-sel');
29204             pn.addClass('x-date-mp-sel');
29205             this.mpSelMonth = pn.dom.xmonth;
29206         }
29207         else if(pn = el.up('td.x-date-mp-year', 2)){
29208             this.mpYears.removeClass('x-date-mp-sel');
29209             pn.addClass('x-date-mp-sel');
29210             this.mpSelYear = pn.dom.xyear;
29211         }
29212         else if(el.is('a.x-date-mp-prev')){
29213             this.updateMPYear(this.mpyear-10);
29214         }
29215         else if(el.is('a.x-date-mp-next')){
29216             this.updateMPYear(this.mpyear+10);
29217         }
29218     },
29219
29220     onMonthDblClick : function(e, t){
29221         e.stopEvent();
29222         var el = new Roo.Element(t), pn;
29223         if(pn = el.up('td.x-date-mp-month', 2)){
29224             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29225             this.hideMonthPicker();
29226         }
29227         else if(pn = el.up('td.x-date-mp-year', 2)){
29228             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29229             this.hideMonthPicker();
29230         }
29231     },
29232
29233     hideMonthPicker : function(disableAnim){
29234         if(this.monthPicker){
29235             if(disableAnim === true){
29236                 this.monthPicker.hide();
29237             }else{
29238                 this.monthPicker.slideOut('t', {duration:.2});
29239             }
29240         }
29241     },
29242
29243     // private
29244     showPrevMonth : function(e){
29245         this.update(this.activeDate.add("mo", -1));
29246     },
29247
29248     // private
29249     showNextMonth : function(e){
29250         this.update(this.activeDate.add("mo", 1));
29251     },
29252
29253     // private
29254     showPrevYear : function(){
29255         this.update(this.activeDate.add("y", -1));
29256     },
29257
29258     // private
29259     showNextYear : function(){
29260         this.update(this.activeDate.add("y", 1));
29261     },
29262
29263     // private
29264     handleMouseWheel : function(e){
29265         var delta = e.getWheelDelta();
29266         if(delta > 0){
29267             this.showPrevMonth();
29268             e.stopEvent();
29269         } else if(delta < 0){
29270             this.showNextMonth();
29271             e.stopEvent();
29272         }
29273     },
29274
29275     // private
29276     handleDateClick : function(e, t){
29277         e.stopEvent();
29278         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29279             this.setValue(new Date(t.dateValue));
29280             this.fireEvent("select", this, this.value);
29281         }
29282     },
29283
29284     // private
29285     selectToday : function(){
29286         this.setValue(new Date().clearTime());
29287         this.fireEvent("select", this, this.value);
29288     },
29289
29290     // private
29291     update : function(date)
29292     {
29293         var vd = this.activeDate;
29294         this.activeDate = date;
29295         if(vd && this.el){
29296             var t = date.getTime();
29297             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29298                 this.cells.removeClass("x-date-selected");
29299                 this.cells.each(function(c){
29300                    if(c.dom.firstChild.dateValue == t){
29301                        c.addClass("x-date-selected");
29302                        setTimeout(function(){
29303                             try{c.dom.firstChild.focus();}catch(e){}
29304                        }, 50);
29305                        return false;
29306                    }
29307                 });
29308                 return;
29309             }
29310         }
29311         
29312         var days = date.getDaysInMonth();
29313         var firstOfMonth = date.getFirstDateOfMonth();
29314         var startingPos = firstOfMonth.getDay()-this.startDay;
29315
29316         if(startingPos <= this.startDay){
29317             startingPos += 7;
29318         }
29319
29320         var pm = date.add("mo", -1);
29321         var prevStart = pm.getDaysInMonth()-startingPos;
29322
29323         var cells = this.cells.elements;
29324         var textEls = this.textNodes;
29325         days += startingPos;
29326
29327         // convert everything to numbers so it's fast
29328         var day = 86400000;
29329         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29330         var today = new Date().clearTime().getTime();
29331         var sel = date.clearTime().getTime();
29332         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29333         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29334         var ddMatch = this.disabledDatesRE;
29335         var ddText = this.disabledDatesText;
29336         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29337         var ddaysText = this.disabledDaysText;
29338         var format = this.format;
29339
29340         var setCellClass = function(cal, cell){
29341             cell.title = "";
29342             var t = d.getTime();
29343             cell.firstChild.dateValue = t;
29344             if(t == today){
29345                 cell.className += " x-date-today";
29346                 cell.title = cal.todayText;
29347             }
29348             if(t == sel){
29349                 cell.className += " x-date-selected";
29350                 setTimeout(function(){
29351                     try{cell.firstChild.focus();}catch(e){}
29352                 }, 50);
29353             }
29354             // disabling
29355             if(t < min) {
29356                 cell.className = " x-date-disabled";
29357                 cell.title = cal.minText;
29358                 return;
29359             }
29360             if(t > max) {
29361                 cell.className = " x-date-disabled";
29362                 cell.title = cal.maxText;
29363                 return;
29364             }
29365             if(ddays){
29366                 if(ddays.indexOf(d.getDay()) != -1){
29367                     cell.title = ddaysText;
29368                     cell.className = " x-date-disabled";
29369                 }
29370             }
29371             if(ddMatch && format){
29372                 var fvalue = d.dateFormat(format);
29373                 if(ddMatch.test(fvalue)){
29374                     cell.title = ddText.replace("%0", fvalue);
29375                     cell.className = " x-date-disabled";
29376                 }
29377             }
29378         };
29379
29380         var i = 0;
29381         for(; i < startingPos; i++) {
29382             textEls[i].innerHTML = (++prevStart);
29383             d.setDate(d.getDate()+1);
29384             cells[i].className = "x-date-prevday";
29385             setCellClass(this, cells[i]);
29386         }
29387         for(; i < days; i++){
29388             intDay = i - startingPos + 1;
29389             textEls[i].innerHTML = (intDay);
29390             d.setDate(d.getDate()+1);
29391             cells[i].className = "x-date-active";
29392             setCellClass(this, cells[i]);
29393         }
29394         var extraDays = 0;
29395         for(; i < 42; i++) {
29396              textEls[i].innerHTML = (++extraDays);
29397              d.setDate(d.getDate()+1);
29398              cells[i].className = "x-date-nextday";
29399              setCellClass(this, cells[i]);
29400         }
29401
29402         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29403         this.fireEvent('monthchange', this, date);
29404         
29405         if(!this.internalRender){
29406             var main = this.el.dom.firstChild;
29407             var w = main.offsetWidth;
29408             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29409             Roo.fly(main).setWidth(w);
29410             this.internalRender = true;
29411             // opera does not respect the auto grow header center column
29412             // then, after it gets a width opera refuses to recalculate
29413             // without a second pass
29414             if(Roo.isOpera && !this.secondPass){
29415                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29416                 this.secondPass = true;
29417                 this.update.defer(10, this, [date]);
29418             }
29419         }
29420         
29421         
29422     }
29423 });        /*
29424  * Based on:
29425  * Ext JS Library 1.1.1
29426  * Copyright(c) 2006-2007, Ext JS, LLC.
29427  *
29428  * Originally Released Under LGPL - original licence link has changed is not relivant.
29429  *
29430  * Fork - LGPL
29431  * <script type="text/javascript">
29432  */
29433 /**
29434  * @class Roo.TabPanel
29435  * @extends Roo.util.Observable
29436  * A lightweight tab container.
29437  * <br><br>
29438  * Usage:
29439  * <pre><code>
29440 // basic tabs 1, built from existing content
29441 var tabs = new Roo.TabPanel("tabs1");
29442 tabs.addTab("script", "View Script");
29443 tabs.addTab("markup", "View Markup");
29444 tabs.activate("script");
29445
29446 // more advanced tabs, built from javascript
29447 var jtabs = new Roo.TabPanel("jtabs");
29448 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29449
29450 // set up the UpdateManager
29451 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29452 var updater = tab2.getUpdateManager();
29453 updater.setDefaultUrl("ajax1.htm");
29454 tab2.on('activate', updater.refresh, updater, true);
29455
29456 // Use setUrl for Ajax loading
29457 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29458 tab3.setUrl("ajax2.htm", null, true);
29459
29460 // Disabled tab
29461 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29462 tab4.disable();
29463
29464 jtabs.activate("jtabs-1");
29465  * </code></pre>
29466  * @constructor
29467  * Create a new TabPanel.
29468  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29469  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29470  */
29471 Roo.TabPanel = function(container, config){
29472     /**
29473     * The container element for this TabPanel.
29474     * @type Roo.Element
29475     */
29476     this.el = Roo.get(container, true);
29477     if(config){
29478         if(typeof config == "boolean"){
29479             this.tabPosition = config ? "bottom" : "top";
29480         }else{
29481             Roo.apply(this, config);
29482         }
29483     }
29484     if(this.tabPosition == "bottom"){
29485         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29486         this.el.addClass("x-tabs-bottom");
29487     }
29488     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29489     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29490     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29491     if(Roo.isIE){
29492         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29493     }
29494     if(this.tabPosition != "bottom"){
29495         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29496          * @type Roo.Element
29497          */
29498         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29499         this.el.addClass("x-tabs-top");
29500     }
29501     this.items = [];
29502
29503     this.bodyEl.setStyle("position", "relative");
29504
29505     this.active = null;
29506     this.activateDelegate = this.activate.createDelegate(this);
29507
29508     this.addEvents({
29509         /**
29510          * @event tabchange
29511          * Fires when the active tab changes
29512          * @param {Roo.TabPanel} this
29513          * @param {Roo.TabPanelItem} activePanel The new active tab
29514          */
29515         "tabchange": true,
29516         /**
29517          * @event beforetabchange
29518          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29519          * @param {Roo.TabPanel} this
29520          * @param {Object} e Set cancel to true on this object to cancel the tab change
29521          * @param {Roo.TabPanelItem} tab The tab being changed to
29522          */
29523         "beforetabchange" : true
29524     });
29525
29526     Roo.EventManager.onWindowResize(this.onResize, this);
29527     this.cpad = this.el.getPadding("lr");
29528     this.hiddenCount = 0;
29529
29530
29531     // toolbar on the tabbar support...
29532     if (this.toolbar) {
29533         var tcfg = this.toolbar;
29534         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29535         this.toolbar = new Roo.Toolbar(tcfg);
29536         if (Roo.isSafari) {
29537             var tbl = tcfg.container.child('table', true);
29538             tbl.setAttribute('width', '100%');
29539         }
29540         
29541     }
29542    
29543
29544
29545     Roo.TabPanel.superclass.constructor.call(this);
29546 };
29547
29548 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29549     /*
29550      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29551      */
29552     tabPosition : "top",
29553     /*
29554      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29555      */
29556     currentTabWidth : 0,
29557     /*
29558      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29559      */
29560     minTabWidth : 40,
29561     /*
29562      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29563      */
29564     maxTabWidth : 250,
29565     /*
29566      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29567      */
29568     preferredTabWidth : 175,
29569     /*
29570      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29571      */
29572     resizeTabs : false,
29573     /*
29574      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29575      */
29576     monitorResize : true,
29577     /*
29578      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29579      */
29580     toolbar : false,
29581
29582     /**
29583      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29584      * @param {String} id The id of the div to use <b>or create</b>
29585      * @param {String} text The text for the tab
29586      * @param {String} content (optional) Content to put in the TabPanelItem body
29587      * @param {Boolean} closable (optional) True to create a close icon on the tab
29588      * @return {Roo.TabPanelItem} The created TabPanelItem
29589      */
29590     addTab : function(id, text, content, closable){
29591         var item = new Roo.TabPanelItem(this, id, text, closable);
29592         this.addTabItem(item);
29593         if(content){
29594             item.setContent(content);
29595         }
29596         return item;
29597     },
29598
29599     /**
29600      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29601      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29602      * @return {Roo.TabPanelItem}
29603      */
29604     getTab : function(id){
29605         return this.items[id];
29606     },
29607
29608     /**
29609      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29610      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29611      */
29612     hideTab : function(id){
29613         var t = this.items[id];
29614         if(!t.isHidden()){
29615            t.setHidden(true);
29616            this.hiddenCount++;
29617            this.autoSizeTabs();
29618         }
29619     },
29620
29621     /**
29622      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29623      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29624      */
29625     unhideTab : function(id){
29626         var t = this.items[id];
29627         if(t.isHidden()){
29628            t.setHidden(false);
29629            this.hiddenCount--;
29630            this.autoSizeTabs();
29631         }
29632     },
29633
29634     /**
29635      * Adds an existing {@link Roo.TabPanelItem}.
29636      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29637      */
29638     addTabItem : function(item){
29639         this.items[item.id] = item;
29640         this.items.push(item);
29641         if(this.resizeTabs){
29642            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29643            this.autoSizeTabs();
29644         }else{
29645             item.autoSize();
29646         }
29647     },
29648
29649     /**
29650      * Removes a {@link Roo.TabPanelItem}.
29651      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29652      */
29653     removeTab : function(id){
29654         var items = this.items;
29655         var tab = items[id];
29656         if(!tab) { return; }
29657         var index = items.indexOf(tab);
29658         if(this.active == tab && items.length > 1){
29659             var newTab = this.getNextAvailable(index);
29660             if(newTab) {
29661                 newTab.activate();
29662             }
29663         }
29664         this.stripEl.dom.removeChild(tab.pnode.dom);
29665         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29666             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29667         }
29668         items.splice(index, 1);
29669         delete this.items[tab.id];
29670         tab.fireEvent("close", tab);
29671         tab.purgeListeners();
29672         this.autoSizeTabs();
29673     },
29674
29675     getNextAvailable : function(start){
29676         var items = this.items;
29677         var index = start;
29678         // look for a next tab that will slide over to
29679         // replace the one being removed
29680         while(index < items.length){
29681             var item = items[++index];
29682             if(item && !item.isHidden()){
29683                 return item;
29684             }
29685         }
29686         // if one isn't found select the previous tab (on the left)
29687         index = start;
29688         while(index >= 0){
29689             var item = items[--index];
29690             if(item && !item.isHidden()){
29691                 return item;
29692             }
29693         }
29694         return null;
29695     },
29696
29697     /**
29698      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29699      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29700      */
29701     disableTab : function(id){
29702         var tab = this.items[id];
29703         if(tab && this.active != tab){
29704             tab.disable();
29705         }
29706     },
29707
29708     /**
29709      * Enables a {@link Roo.TabPanelItem} that is disabled.
29710      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29711      */
29712     enableTab : function(id){
29713         var tab = this.items[id];
29714         tab.enable();
29715     },
29716
29717     /**
29718      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29719      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29720      * @return {Roo.TabPanelItem} The TabPanelItem.
29721      */
29722     activate : function(id){
29723         var tab = this.items[id];
29724         if(!tab){
29725             return null;
29726         }
29727         if(tab == this.active || tab.disabled){
29728             return tab;
29729         }
29730         var e = {};
29731         this.fireEvent("beforetabchange", this, e, tab);
29732         if(e.cancel !== true && !tab.disabled){
29733             if(this.active){
29734                 this.active.hide();
29735             }
29736             this.active = this.items[id];
29737             this.active.show();
29738             this.fireEvent("tabchange", this, this.active);
29739         }
29740         return tab;
29741     },
29742
29743     /**
29744      * Gets the active {@link Roo.TabPanelItem}.
29745      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29746      */
29747     getActiveTab : function(){
29748         return this.active;
29749     },
29750
29751     /**
29752      * Updates the tab body element to fit the height of the container element
29753      * for overflow scrolling
29754      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29755      */
29756     syncHeight : function(targetHeight){
29757         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29758         var bm = this.bodyEl.getMargins();
29759         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29760         this.bodyEl.setHeight(newHeight);
29761         return newHeight;
29762     },
29763
29764     onResize : function(){
29765         if(this.monitorResize){
29766             this.autoSizeTabs();
29767         }
29768     },
29769
29770     /**
29771      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29772      */
29773     beginUpdate : function(){
29774         this.updating = true;
29775     },
29776
29777     /**
29778      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29779      */
29780     endUpdate : function(){
29781         this.updating = false;
29782         this.autoSizeTabs();
29783     },
29784
29785     /**
29786      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29787      */
29788     autoSizeTabs : function(){
29789         var count = this.items.length;
29790         var vcount = count - this.hiddenCount;
29791         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29792             return;
29793         }
29794         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29795         var availWidth = Math.floor(w / vcount);
29796         var b = this.stripBody;
29797         if(b.getWidth() > w){
29798             var tabs = this.items;
29799             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29800             if(availWidth < this.minTabWidth){
29801                 /*if(!this.sleft){    // incomplete scrolling code
29802                     this.createScrollButtons();
29803                 }
29804                 this.showScroll();
29805                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29806             }
29807         }else{
29808             if(this.currentTabWidth < this.preferredTabWidth){
29809                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29810             }
29811         }
29812     },
29813
29814     /**
29815      * Returns the number of tabs in this TabPanel.
29816      * @return {Number}
29817      */
29818      getCount : function(){
29819          return this.items.length;
29820      },
29821
29822     /**
29823      * Resizes all the tabs to the passed width
29824      * @param {Number} The new width
29825      */
29826     setTabWidth : function(width){
29827         this.currentTabWidth = width;
29828         for(var i = 0, len = this.items.length; i < len; i++) {
29829                 if(!this.items[i].isHidden()) {
29830                 this.items[i].setWidth(width);
29831             }
29832         }
29833     },
29834
29835     /**
29836      * Destroys this TabPanel
29837      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29838      */
29839     destroy : function(removeEl){
29840         Roo.EventManager.removeResizeListener(this.onResize, this);
29841         for(var i = 0, len = this.items.length; i < len; i++){
29842             this.items[i].purgeListeners();
29843         }
29844         if(removeEl === true){
29845             this.el.update("");
29846             this.el.remove();
29847         }
29848     }
29849 });
29850
29851 /**
29852  * @class Roo.TabPanelItem
29853  * @extends Roo.util.Observable
29854  * Represents an individual item (tab plus body) in a TabPanel.
29855  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29856  * @param {String} id The id of this TabPanelItem
29857  * @param {String} text The text for the tab of this TabPanelItem
29858  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29859  */
29860 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29861     /**
29862      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29863      * @type Roo.TabPanel
29864      */
29865     this.tabPanel = tabPanel;
29866     /**
29867      * The id for this TabPanelItem
29868      * @type String
29869      */
29870     this.id = id;
29871     /** @private */
29872     this.disabled = false;
29873     /** @private */
29874     this.text = text;
29875     /** @private */
29876     this.loaded = false;
29877     this.closable = closable;
29878
29879     /**
29880      * The body element for this TabPanelItem.
29881      * @type Roo.Element
29882      */
29883     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29884     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29885     this.bodyEl.setStyle("display", "block");
29886     this.bodyEl.setStyle("zoom", "1");
29887     this.hideAction();
29888
29889     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29890     /** @private */
29891     this.el = Roo.get(els.el, true);
29892     this.inner = Roo.get(els.inner, true);
29893     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29894     this.pnode = Roo.get(els.el.parentNode, true);
29895     this.el.on("mousedown", this.onTabMouseDown, this);
29896     this.el.on("click", this.onTabClick, this);
29897     /** @private */
29898     if(closable){
29899         var c = Roo.get(els.close, true);
29900         c.dom.title = this.closeText;
29901         c.addClassOnOver("close-over");
29902         c.on("click", this.closeClick, this);
29903      }
29904
29905     this.addEvents({
29906          /**
29907          * @event activate
29908          * Fires when this tab becomes the active tab.
29909          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29910          * @param {Roo.TabPanelItem} this
29911          */
29912         "activate": true,
29913         /**
29914          * @event beforeclose
29915          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29916          * @param {Roo.TabPanelItem} this
29917          * @param {Object} e Set cancel to true on this object to cancel the close.
29918          */
29919         "beforeclose": true,
29920         /**
29921          * @event close
29922          * Fires when this tab is closed.
29923          * @param {Roo.TabPanelItem} this
29924          */
29925          "close": true,
29926         /**
29927          * @event deactivate
29928          * Fires when this tab is no longer the active tab.
29929          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29930          * @param {Roo.TabPanelItem} this
29931          */
29932          "deactivate" : true
29933     });
29934     this.hidden = false;
29935
29936     Roo.TabPanelItem.superclass.constructor.call(this);
29937 };
29938
29939 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29940     purgeListeners : function(){
29941        Roo.util.Observable.prototype.purgeListeners.call(this);
29942        this.el.removeAllListeners();
29943     },
29944     /**
29945      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29946      */
29947     show : function(){
29948         this.pnode.addClass("on");
29949         this.showAction();
29950         if(Roo.isOpera){
29951             this.tabPanel.stripWrap.repaint();
29952         }
29953         this.fireEvent("activate", this.tabPanel, this);
29954     },
29955
29956     /**
29957      * Returns true if this tab is the active tab.
29958      * @return {Boolean}
29959      */
29960     isActive : function(){
29961         return this.tabPanel.getActiveTab() == this;
29962     },
29963
29964     /**
29965      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29966      */
29967     hide : function(){
29968         this.pnode.removeClass("on");
29969         this.hideAction();
29970         this.fireEvent("deactivate", this.tabPanel, this);
29971     },
29972
29973     hideAction : function(){
29974         this.bodyEl.hide();
29975         this.bodyEl.setStyle("position", "absolute");
29976         this.bodyEl.setLeft("-20000px");
29977         this.bodyEl.setTop("-20000px");
29978     },
29979
29980     showAction : function(){
29981         this.bodyEl.setStyle("position", "relative");
29982         this.bodyEl.setTop("");
29983         this.bodyEl.setLeft("");
29984         this.bodyEl.show();
29985     },
29986
29987     /**
29988      * Set the tooltip for the tab.
29989      * @param {String} tooltip The tab's tooltip
29990      */
29991     setTooltip : function(text){
29992         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29993             this.textEl.dom.qtip = text;
29994             this.textEl.dom.removeAttribute('title');
29995         }else{
29996             this.textEl.dom.title = text;
29997         }
29998     },
29999
30000     onTabClick : function(e){
30001         e.preventDefault();
30002         this.tabPanel.activate(this.id);
30003     },
30004
30005     onTabMouseDown : function(e){
30006         e.preventDefault();
30007         this.tabPanel.activate(this.id);
30008     },
30009
30010     getWidth : function(){
30011         return this.inner.getWidth();
30012     },
30013
30014     setWidth : function(width){
30015         var iwidth = width - this.pnode.getPadding("lr");
30016         this.inner.setWidth(iwidth);
30017         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30018         this.pnode.setWidth(width);
30019     },
30020
30021     /**
30022      * Show or hide the tab
30023      * @param {Boolean} hidden True to hide or false to show.
30024      */
30025     setHidden : function(hidden){
30026         this.hidden = hidden;
30027         this.pnode.setStyle("display", hidden ? "none" : "");
30028     },
30029
30030     /**
30031      * Returns true if this tab is "hidden"
30032      * @return {Boolean}
30033      */
30034     isHidden : function(){
30035         return this.hidden;
30036     },
30037
30038     /**
30039      * Returns the text for this tab
30040      * @return {String}
30041      */
30042     getText : function(){
30043         return this.text;
30044     },
30045
30046     autoSize : function(){
30047         //this.el.beginMeasure();
30048         this.textEl.setWidth(1);
30049         /*
30050          *  #2804 [new] Tabs in Roojs
30051          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30052          */
30053         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30054         //this.el.endMeasure();
30055     },
30056
30057     /**
30058      * Sets the text for the tab (Note: this also sets the tooltip text)
30059      * @param {String} text The tab's text and tooltip
30060      */
30061     setText : function(text){
30062         this.text = text;
30063         this.textEl.update(text);
30064         this.setTooltip(text);
30065         if(!this.tabPanel.resizeTabs){
30066             this.autoSize();
30067         }
30068     },
30069     /**
30070      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30071      */
30072     activate : function(){
30073         this.tabPanel.activate(this.id);
30074     },
30075
30076     /**
30077      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30078      */
30079     disable : function(){
30080         if(this.tabPanel.active != this){
30081             this.disabled = true;
30082             this.pnode.addClass("disabled");
30083         }
30084     },
30085
30086     /**
30087      * Enables this TabPanelItem if it was previously disabled.
30088      */
30089     enable : function(){
30090         this.disabled = false;
30091         this.pnode.removeClass("disabled");
30092     },
30093
30094     /**
30095      * Sets the content for this TabPanelItem.
30096      * @param {String} content The content
30097      * @param {Boolean} loadScripts true to look for and load scripts
30098      */
30099     setContent : function(content, loadScripts){
30100         this.bodyEl.update(content, loadScripts);
30101     },
30102
30103     /**
30104      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30105      * @return {Roo.UpdateManager} The UpdateManager
30106      */
30107     getUpdateManager : function(){
30108         return this.bodyEl.getUpdateManager();
30109     },
30110
30111     /**
30112      * Set a URL to be used to load the content for this TabPanelItem.
30113      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30114      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
30115      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
30116      * @return {Roo.UpdateManager} The UpdateManager
30117      */
30118     setUrl : function(url, params, loadOnce){
30119         if(this.refreshDelegate){
30120             this.un('activate', this.refreshDelegate);
30121         }
30122         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30123         this.on("activate", this.refreshDelegate);
30124         return this.bodyEl.getUpdateManager();
30125     },
30126
30127     /** @private */
30128     _handleRefresh : function(url, params, loadOnce){
30129         if(!loadOnce || !this.loaded){
30130             var updater = this.bodyEl.getUpdateManager();
30131             updater.update(url, params, this._setLoaded.createDelegate(this));
30132         }
30133     },
30134
30135     /**
30136      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30137      *   Will fail silently if the setUrl method has not been called.
30138      *   This does not activate the panel, just updates its content.
30139      */
30140     refresh : function(){
30141         if(this.refreshDelegate){
30142            this.loaded = false;
30143            this.refreshDelegate();
30144         }
30145     },
30146
30147     /** @private */
30148     _setLoaded : function(){
30149         this.loaded = true;
30150     },
30151
30152     /** @private */
30153     closeClick : function(e){
30154         var o = {};
30155         e.stopEvent();
30156         this.fireEvent("beforeclose", this, o);
30157         if(o.cancel !== true){
30158             this.tabPanel.removeTab(this.id);
30159         }
30160     },
30161     /**
30162      * The text displayed in the tooltip for the close icon.
30163      * @type String
30164      */
30165     closeText : "Close this tab"
30166 });
30167
30168 /** @private */
30169 Roo.TabPanel.prototype.createStrip = function(container){
30170     var strip = document.createElement("div");
30171     strip.className = "x-tabs-wrap";
30172     container.appendChild(strip);
30173     return strip;
30174 };
30175 /** @private */
30176 Roo.TabPanel.prototype.createStripList = function(strip){
30177     // div wrapper for retard IE
30178     // returns the "tr" element.
30179     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30180         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30181         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30182     return strip.firstChild.firstChild.firstChild.firstChild;
30183 };
30184 /** @private */
30185 Roo.TabPanel.prototype.createBody = function(container){
30186     var body = document.createElement("div");
30187     Roo.id(body, "tab-body");
30188     Roo.fly(body).addClass("x-tabs-body");
30189     container.appendChild(body);
30190     return body;
30191 };
30192 /** @private */
30193 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30194     var body = Roo.getDom(id);
30195     if(!body){
30196         body = document.createElement("div");
30197         body.id = id;
30198     }
30199     Roo.fly(body).addClass("x-tabs-item-body");
30200     bodyEl.insertBefore(body, bodyEl.firstChild);
30201     return body;
30202 };
30203 /** @private */
30204 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30205     var td = document.createElement("td");
30206     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30207     //stripEl.appendChild(td);
30208     if(closable){
30209         td.className = "x-tabs-closable";
30210         if(!this.closeTpl){
30211             this.closeTpl = new Roo.Template(
30212                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30213                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30214                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30215             );
30216         }
30217         var el = this.closeTpl.overwrite(td, {"text": text});
30218         var close = el.getElementsByTagName("div")[0];
30219         var inner = el.getElementsByTagName("em")[0];
30220         return {"el": el, "close": close, "inner": inner};
30221     } else {
30222         if(!this.tabTpl){
30223             this.tabTpl = new Roo.Template(
30224                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30225                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30226             );
30227         }
30228         var el = this.tabTpl.overwrite(td, {"text": text});
30229         var inner = el.getElementsByTagName("em")[0];
30230         return {"el": el, "inner": inner};
30231     }
30232 };/*
30233  * Based on:
30234  * Ext JS Library 1.1.1
30235  * Copyright(c) 2006-2007, Ext JS, LLC.
30236  *
30237  * Originally Released Under LGPL - original licence link has changed is not relivant.
30238  *
30239  * Fork - LGPL
30240  * <script type="text/javascript">
30241  */
30242
30243 /**
30244  * @class Roo.Button
30245  * @extends Roo.util.Observable
30246  * Simple Button class
30247  * @cfg {String} text The button text
30248  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30249  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30250  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30251  * @cfg {Object} scope The scope of the handler
30252  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30253  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30254  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30255  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30256  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30257  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30258    applies if enableToggle = true)
30259  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30260  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30261   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30262  * @constructor
30263  * Create a new button
30264  * @param {Object} config The config object
30265  */
30266 Roo.Button = function(renderTo, config)
30267 {
30268     if (!config) {
30269         config = renderTo;
30270         renderTo = config.renderTo || false;
30271     }
30272     
30273     Roo.apply(this, config);
30274     this.addEvents({
30275         /**
30276              * @event click
30277              * Fires when this button is clicked
30278              * @param {Button} this
30279              * @param {EventObject} e The click event
30280              */
30281             "click" : true,
30282         /**
30283              * @event toggle
30284              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30285              * @param {Button} this
30286              * @param {Boolean} pressed
30287              */
30288             "toggle" : true,
30289         /**
30290              * @event mouseover
30291              * Fires when the mouse hovers over the button
30292              * @param {Button} this
30293              * @param {Event} e The event object
30294              */
30295         'mouseover' : true,
30296         /**
30297              * @event mouseout
30298              * Fires when the mouse exits the button
30299              * @param {Button} this
30300              * @param {Event} e The event object
30301              */
30302         'mouseout': true,
30303          /**
30304              * @event render
30305              * Fires when the button is rendered
30306              * @param {Button} this
30307              */
30308         'render': true
30309     });
30310     if(this.menu){
30311         this.menu = Roo.menu.MenuMgr.get(this.menu);
30312     }
30313     // register listeners first!!  - so render can be captured..
30314     Roo.util.Observable.call(this);
30315     if(renderTo){
30316         this.render(renderTo);
30317     }
30318     
30319   
30320 };
30321
30322 Roo.extend(Roo.Button, Roo.util.Observable, {
30323     /**
30324      * 
30325      */
30326     
30327     /**
30328      * Read-only. True if this button is hidden
30329      * @type Boolean
30330      */
30331     hidden : false,
30332     /**
30333      * Read-only. True if this button is disabled
30334      * @type Boolean
30335      */
30336     disabled : false,
30337     /**
30338      * Read-only. True if this button is pressed (only if enableToggle = true)
30339      * @type Boolean
30340      */
30341     pressed : false,
30342
30343     /**
30344      * @cfg {Number} tabIndex 
30345      * The DOM tabIndex for this button (defaults to undefined)
30346      */
30347     tabIndex : undefined,
30348
30349     /**
30350      * @cfg {Boolean} enableToggle
30351      * True to enable pressed/not pressed toggling (defaults to false)
30352      */
30353     enableToggle: false,
30354     /**
30355      * @cfg {Roo.menu.Menu} menu
30356      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30357      */
30358     menu : undefined,
30359     /**
30360      * @cfg {String} menuAlign
30361      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30362      */
30363     menuAlign : "tl-bl?",
30364
30365     /**
30366      * @cfg {String} iconCls
30367      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30368      */
30369     iconCls : undefined,
30370     /**
30371      * @cfg {String} type
30372      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30373      */
30374     type : 'button',
30375
30376     // private
30377     menuClassTarget: 'tr',
30378
30379     /**
30380      * @cfg {String} clickEvent
30381      * The type of event to map to the button's event handler (defaults to 'click')
30382      */
30383     clickEvent : 'click',
30384
30385     /**
30386      * @cfg {Boolean} handleMouseEvents
30387      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30388      */
30389     handleMouseEvents : true,
30390
30391     /**
30392      * @cfg {String} tooltipType
30393      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30394      */
30395     tooltipType : 'qtip',
30396
30397     /**
30398      * @cfg {String} cls
30399      * A CSS class to apply to the button's main element.
30400      */
30401     
30402     /**
30403      * @cfg {Roo.Template} template (Optional)
30404      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30405      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30406      * require code modifications if required elements (e.g. a button) aren't present.
30407      */
30408
30409     // private
30410     render : function(renderTo){
30411         var btn;
30412         if(this.hideParent){
30413             this.parentEl = Roo.get(renderTo);
30414         }
30415         if(!this.dhconfig){
30416             if(!this.template){
30417                 if(!Roo.Button.buttonTemplate){
30418                     // hideous table template
30419                     Roo.Button.buttonTemplate = new Roo.Template(
30420                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30421                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
30422                         "</tr></tbody></table>");
30423                 }
30424                 this.template = Roo.Button.buttonTemplate;
30425             }
30426             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30427             var btnEl = btn.child("button:first");
30428             btnEl.on('focus', this.onFocus, this);
30429             btnEl.on('blur', this.onBlur, this);
30430             if(this.cls){
30431                 btn.addClass(this.cls);
30432             }
30433             if(this.icon){
30434                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30435             }
30436             if(this.iconCls){
30437                 btnEl.addClass(this.iconCls);
30438                 if(!this.cls){
30439                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30440                 }
30441             }
30442             if(this.tabIndex !== undefined){
30443                 btnEl.dom.tabIndex = this.tabIndex;
30444             }
30445             if(this.tooltip){
30446                 if(typeof this.tooltip == 'object'){
30447                     Roo.QuickTips.tips(Roo.apply({
30448                           target: btnEl.id
30449                     }, this.tooltip));
30450                 } else {
30451                     btnEl.dom[this.tooltipType] = this.tooltip;
30452                 }
30453             }
30454         }else{
30455             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30456         }
30457         this.el = btn;
30458         if(this.id){
30459             this.el.dom.id = this.el.id = this.id;
30460         }
30461         if(this.menu){
30462             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30463             this.menu.on("show", this.onMenuShow, this);
30464             this.menu.on("hide", this.onMenuHide, this);
30465         }
30466         btn.addClass("x-btn");
30467         if(Roo.isIE && !Roo.isIE7){
30468             this.autoWidth.defer(1, this);
30469         }else{
30470             this.autoWidth();
30471         }
30472         if(this.handleMouseEvents){
30473             btn.on("mouseover", this.onMouseOver, this);
30474             btn.on("mouseout", this.onMouseOut, this);
30475             btn.on("mousedown", this.onMouseDown, this);
30476         }
30477         btn.on(this.clickEvent, this.onClick, this);
30478         //btn.on("mouseup", this.onMouseUp, this);
30479         if(this.hidden){
30480             this.hide();
30481         }
30482         if(this.disabled){
30483             this.disable();
30484         }
30485         Roo.ButtonToggleMgr.register(this);
30486         if(this.pressed){
30487             this.el.addClass("x-btn-pressed");
30488         }
30489         if(this.repeat){
30490             var repeater = new Roo.util.ClickRepeater(btn,
30491                 typeof this.repeat == "object" ? this.repeat : {}
30492             );
30493             repeater.on("click", this.onClick,  this);
30494         }
30495         
30496         this.fireEvent('render', this);
30497         
30498     },
30499     /**
30500      * Returns the button's underlying element
30501      * @return {Roo.Element} The element
30502      */
30503     getEl : function(){
30504         return this.el;  
30505     },
30506     
30507     /**
30508      * Destroys this Button and removes any listeners.
30509      */
30510     destroy : function(){
30511         Roo.ButtonToggleMgr.unregister(this);
30512         this.el.removeAllListeners();
30513         this.purgeListeners();
30514         this.el.remove();
30515     },
30516
30517     // private
30518     autoWidth : function(){
30519         if(this.el){
30520             this.el.setWidth("auto");
30521             if(Roo.isIE7 && Roo.isStrict){
30522                 var ib = this.el.child('button');
30523                 if(ib && ib.getWidth() > 20){
30524                     ib.clip();
30525                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30526                 }
30527             }
30528             if(this.minWidth){
30529                 if(this.hidden){
30530                     this.el.beginMeasure();
30531                 }
30532                 if(this.el.getWidth() < this.minWidth){
30533                     this.el.setWidth(this.minWidth);
30534                 }
30535                 if(this.hidden){
30536                     this.el.endMeasure();
30537                 }
30538             }
30539         }
30540     },
30541
30542     /**
30543      * Assigns this button's click handler
30544      * @param {Function} handler The function to call when the button is clicked
30545      * @param {Object} scope (optional) Scope for the function passed in
30546      */
30547     setHandler : function(handler, scope){
30548         this.handler = handler;
30549         this.scope = scope;  
30550     },
30551     
30552     /**
30553      * Sets this button's text
30554      * @param {String} text The button text
30555      */
30556     setText : function(text){
30557         this.text = text;
30558         if(this.el){
30559             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30560         }
30561         this.autoWidth();
30562     },
30563     
30564     /**
30565      * Gets the text for this button
30566      * @return {String} The button text
30567      */
30568     getText : function(){
30569         return this.text;  
30570     },
30571     
30572     /**
30573      * Show this button
30574      */
30575     show: function(){
30576         this.hidden = false;
30577         if(this.el){
30578             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30579         }
30580     },
30581     
30582     /**
30583      * Hide this button
30584      */
30585     hide: function(){
30586         this.hidden = true;
30587         if(this.el){
30588             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30589         }
30590     },
30591     
30592     /**
30593      * Convenience function for boolean show/hide
30594      * @param {Boolean} visible True to show, false to hide
30595      */
30596     setVisible: function(visible){
30597         if(visible) {
30598             this.show();
30599         }else{
30600             this.hide();
30601         }
30602     },
30603     
30604     /**
30605      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30606      * @param {Boolean} state (optional) Force a particular state
30607      */
30608     toggle : function(state){
30609         state = state === undefined ? !this.pressed : state;
30610         if(state != this.pressed){
30611             if(state){
30612                 this.el.addClass("x-btn-pressed");
30613                 this.pressed = true;
30614                 this.fireEvent("toggle", this, true);
30615             }else{
30616                 this.el.removeClass("x-btn-pressed");
30617                 this.pressed = false;
30618                 this.fireEvent("toggle", this, false);
30619             }
30620             if(this.toggleHandler){
30621                 this.toggleHandler.call(this.scope || this, this, state);
30622             }
30623         }
30624     },
30625     
30626     /**
30627      * Focus the button
30628      */
30629     focus : function(){
30630         this.el.child('button:first').focus();
30631     },
30632     
30633     /**
30634      * Disable this button
30635      */
30636     disable : function(){
30637         if(this.el){
30638             this.el.addClass("x-btn-disabled");
30639         }
30640         this.disabled = true;
30641     },
30642     
30643     /**
30644      * Enable this button
30645      */
30646     enable : function(){
30647         if(this.el){
30648             this.el.removeClass("x-btn-disabled");
30649         }
30650         this.disabled = false;
30651     },
30652
30653     /**
30654      * Convenience function for boolean enable/disable
30655      * @param {Boolean} enabled True to enable, false to disable
30656      */
30657     setDisabled : function(v){
30658         this[v !== true ? "enable" : "disable"]();
30659     },
30660
30661     // private
30662     onClick : function(e)
30663     {
30664         if(e){
30665             e.preventDefault();
30666         }
30667         if(e.button != 0){
30668             return;
30669         }
30670         if(!this.disabled){
30671             if(this.enableToggle){
30672                 this.toggle();
30673             }
30674             if(this.menu && !this.menu.isVisible()){
30675                 this.menu.show(this.el, this.menuAlign);
30676             }
30677             this.fireEvent("click", this, e);
30678             if(this.handler){
30679                 this.el.removeClass("x-btn-over");
30680                 this.handler.call(this.scope || this, this, e);
30681             }
30682         }
30683     },
30684     // private
30685     onMouseOver : function(e){
30686         if(!this.disabled){
30687             this.el.addClass("x-btn-over");
30688             this.fireEvent('mouseover', this, e);
30689         }
30690     },
30691     // private
30692     onMouseOut : function(e){
30693         if(!e.within(this.el,  true)){
30694             this.el.removeClass("x-btn-over");
30695             this.fireEvent('mouseout', this, e);
30696         }
30697     },
30698     // private
30699     onFocus : function(e){
30700         if(!this.disabled){
30701             this.el.addClass("x-btn-focus");
30702         }
30703     },
30704     // private
30705     onBlur : function(e){
30706         this.el.removeClass("x-btn-focus");
30707     },
30708     // private
30709     onMouseDown : function(e){
30710         if(!this.disabled && e.button == 0){
30711             this.el.addClass("x-btn-click");
30712             Roo.get(document).on('mouseup', this.onMouseUp, this);
30713         }
30714     },
30715     // private
30716     onMouseUp : function(e){
30717         if(e.button == 0){
30718             this.el.removeClass("x-btn-click");
30719             Roo.get(document).un('mouseup', this.onMouseUp, this);
30720         }
30721     },
30722     // private
30723     onMenuShow : function(e){
30724         this.el.addClass("x-btn-menu-active");
30725     },
30726     // private
30727     onMenuHide : function(e){
30728         this.el.removeClass("x-btn-menu-active");
30729     }   
30730 });
30731
30732 // Private utility class used by Button
30733 Roo.ButtonToggleMgr = function(){
30734    var groups = {};
30735    
30736    function toggleGroup(btn, state){
30737        if(state){
30738            var g = groups[btn.toggleGroup];
30739            for(var i = 0, l = g.length; i < l; i++){
30740                if(g[i] != btn){
30741                    g[i].toggle(false);
30742                }
30743            }
30744        }
30745    }
30746    
30747    return {
30748        register : function(btn){
30749            if(!btn.toggleGroup){
30750                return;
30751            }
30752            var g = groups[btn.toggleGroup];
30753            if(!g){
30754                g = groups[btn.toggleGroup] = [];
30755            }
30756            g.push(btn);
30757            btn.on("toggle", toggleGroup);
30758        },
30759        
30760        unregister : function(btn){
30761            if(!btn.toggleGroup){
30762                return;
30763            }
30764            var g = groups[btn.toggleGroup];
30765            if(g){
30766                g.remove(btn);
30767                btn.un("toggle", toggleGroup);
30768            }
30769        }
30770    };
30771 }();/*
30772  * Based on:
30773  * Ext JS Library 1.1.1
30774  * Copyright(c) 2006-2007, Ext JS, LLC.
30775  *
30776  * Originally Released Under LGPL - original licence link has changed is not relivant.
30777  *
30778  * Fork - LGPL
30779  * <script type="text/javascript">
30780  */
30781  
30782 /**
30783  * @class Roo.SplitButton
30784  * @extends Roo.Button
30785  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30786  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30787  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30788  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30789  * @cfg {String} arrowTooltip The title attribute of the arrow
30790  * @constructor
30791  * Create a new menu button
30792  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30793  * @param {Object} config The config object
30794  */
30795 Roo.SplitButton = function(renderTo, config){
30796     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30797     /**
30798      * @event arrowclick
30799      * Fires when this button's arrow is clicked
30800      * @param {SplitButton} this
30801      * @param {EventObject} e The click event
30802      */
30803     this.addEvents({"arrowclick":true});
30804 };
30805
30806 Roo.extend(Roo.SplitButton, Roo.Button, {
30807     render : function(renderTo){
30808         // this is one sweet looking template!
30809         var tpl = new Roo.Template(
30810             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30811             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30812             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
30813             "</tbody></table></td><td>",
30814             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30815             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
30816             "</tbody></table></td></tr></table>"
30817         );
30818         var btn = tpl.append(renderTo, [this.text, this.type], true);
30819         var btnEl = btn.child("button");
30820         if(this.cls){
30821             btn.addClass(this.cls);
30822         }
30823         if(this.icon){
30824             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30825         }
30826         if(this.iconCls){
30827             btnEl.addClass(this.iconCls);
30828             if(!this.cls){
30829                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30830             }
30831         }
30832         this.el = btn;
30833         if(this.handleMouseEvents){
30834             btn.on("mouseover", this.onMouseOver, this);
30835             btn.on("mouseout", this.onMouseOut, this);
30836             btn.on("mousedown", this.onMouseDown, this);
30837             btn.on("mouseup", this.onMouseUp, this);
30838         }
30839         btn.on(this.clickEvent, this.onClick, this);
30840         if(this.tooltip){
30841             if(typeof this.tooltip == 'object'){
30842                 Roo.QuickTips.tips(Roo.apply({
30843                       target: btnEl.id
30844                 }, this.tooltip));
30845             } else {
30846                 btnEl.dom[this.tooltipType] = this.tooltip;
30847             }
30848         }
30849         if(this.arrowTooltip){
30850             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30851         }
30852         if(this.hidden){
30853             this.hide();
30854         }
30855         if(this.disabled){
30856             this.disable();
30857         }
30858         if(this.pressed){
30859             this.el.addClass("x-btn-pressed");
30860         }
30861         if(Roo.isIE && !Roo.isIE7){
30862             this.autoWidth.defer(1, this);
30863         }else{
30864             this.autoWidth();
30865         }
30866         if(this.menu){
30867             this.menu.on("show", this.onMenuShow, this);
30868             this.menu.on("hide", this.onMenuHide, this);
30869         }
30870         this.fireEvent('render', this);
30871     },
30872
30873     // private
30874     autoWidth : function(){
30875         if(this.el){
30876             var tbl = this.el.child("table:first");
30877             var tbl2 = this.el.child("table:last");
30878             this.el.setWidth("auto");
30879             tbl.setWidth("auto");
30880             if(Roo.isIE7 && Roo.isStrict){
30881                 var ib = this.el.child('button:first');
30882                 if(ib && ib.getWidth() > 20){
30883                     ib.clip();
30884                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30885                 }
30886             }
30887             if(this.minWidth){
30888                 if(this.hidden){
30889                     this.el.beginMeasure();
30890                 }
30891                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30892                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30893                 }
30894                 if(this.hidden){
30895                     this.el.endMeasure();
30896                 }
30897             }
30898             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30899         } 
30900     },
30901     /**
30902      * Sets this button's click handler
30903      * @param {Function} handler The function to call when the button is clicked
30904      * @param {Object} scope (optional) Scope for the function passed above
30905      */
30906     setHandler : function(handler, scope){
30907         this.handler = handler;
30908         this.scope = scope;  
30909     },
30910     
30911     /**
30912      * Sets this button's arrow click handler
30913      * @param {Function} handler The function to call when the arrow is clicked
30914      * @param {Object} scope (optional) Scope for the function passed above
30915      */
30916     setArrowHandler : function(handler, scope){
30917         this.arrowHandler = handler;
30918         this.scope = scope;  
30919     },
30920     
30921     /**
30922      * Focus the button
30923      */
30924     focus : function(){
30925         if(this.el){
30926             this.el.child("button:first").focus();
30927         }
30928     },
30929
30930     // private
30931     onClick : function(e){
30932         e.preventDefault();
30933         if(!this.disabled){
30934             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30935                 if(this.menu && !this.menu.isVisible()){
30936                     this.menu.show(this.el, this.menuAlign);
30937                 }
30938                 this.fireEvent("arrowclick", this, e);
30939                 if(this.arrowHandler){
30940                     this.arrowHandler.call(this.scope || this, this, e);
30941                 }
30942             }else{
30943                 this.fireEvent("click", this, e);
30944                 if(this.handler){
30945                     this.handler.call(this.scope || this, this, e);
30946                 }
30947             }
30948         }
30949     },
30950     // private
30951     onMouseDown : function(e){
30952         if(!this.disabled){
30953             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30954         }
30955     },
30956     // private
30957     onMouseUp : function(e){
30958         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30959     }   
30960 });
30961
30962
30963 // backwards compat
30964 Roo.MenuButton = Roo.SplitButton;/*
30965  * Based on:
30966  * Ext JS Library 1.1.1
30967  * Copyright(c) 2006-2007, Ext JS, LLC.
30968  *
30969  * Originally Released Under LGPL - original licence link has changed is not relivant.
30970  *
30971  * Fork - LGPL
30972  * <script type="text/javascript">
30973  */
30974
30975 /**
30976  * @class Roo.Toolbar
30977  * @children   Roo.Toolbar.Item Roo.form.Field
30978  * Basic Toolbar class.
30979  * @constructor
30980  * Creates a new Toolbar
30981  * @param {Object} container The config object
30982  */ 
30983 Roo.Toolbar = function(container, buttons, config)
30984 {
30985     /// old consturctor format still supported..
30986     if(container instanceof Array){ // omit the container for later rendering
30987         buttons = container;
30988         config = buttons;
30989         container = null;
30990     }
30991     if (typeof(container) == 'object' && container.xtype) {
30992         config = container;
30993         container = config.container;
30994         buttons = config.buttons || []; // not really - use items!!
30995     }
30996     var xitems = [];
30997     if (config && config.items) {
30998         xitems = config.items;
30999         delete config.items;
31000     }
31001     Roo.apply(this, config);
31002     this.buttons = buttons;
31003     
31004     if(container){
31005         this.render(container);
31006     }
31007     this.xitems = xitems;
31008     Roo.each(xitems, function(b) {
31009         this.add(b);
31010     }, this);
31011     
31012 };
31013
31014 Roo.Toolbar.prototype = {
31015     /**
31016      * @cfg {Array} items
31017      * array of button configs or elements to add (will be converted to a MixedCollection)
31018      */
31019     items: false,
31020     /**
31021      * @cfg {String/HTMLElement/Element} container
31022      * The id or element that will contain the toolbar
31023      */
31024     // private
31025     render : function(ct){
31026         this.el = Roo.get(ct);
31027         if(this.cls){
31028             this.el.addClass(this.cls);
31029         }
31030         // using a table allows for vertical alignment
31031         // 100% width is needed by Safari...
31032         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31033         this.tr = this.el.child("tr", true);
31034         var autoId = 0;
31035         this.items = new Roo.util.MixedCollection(false, function(o){
31036             return o.id || ("item" + (++autoId));
31037         });
31038         if(this.buttons){
31039             this.add.apply(this, this.buttons);
31040             delete this.buttons;
31041         }
31042     },
31043
31044     /**
31045      * Adds element(s) to the toolbar -- this function takes a variable number of 
31046      * arguments of mixed type and adds them to the toolbar.
31047      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31048      * <ul>
31049      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31050      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31051      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31052      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31053      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31054      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31055      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31056      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31057      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31058      * </ul>
31059      * @param {Mixed} arg2
31060      * @param {Mixed} etc.
31061      */
31062     add : function(){
31063         var a = arguments, l = a.length;
31064         for(var i = 0; i < l; i++){
31065             this._add(a[i]);
31066         }
31067     },
31068     // private..
31069     _add : function(el) {
31070         
31071         if (el.xtype) {
31072             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31073         }
31074         
31075         if (el.applyTo){ // some kind of form field
31076             return this.addField(el);
31077         } 
31078         if (el.render){ // some kind of Toolbar.Item
31079             return this.addItem(el);
31080         }
31081         if (typeof el == "string"){ // string
31082             if(el == "separator" || el == "-"){
31083                 return this.addSeparator();
31084             }
31085             if (el == " "){
31086                 return this.addSpacer();
31087             }
31088             if(el == "->"){
31089                 return this.addFill();
31090             }
31091             return this.addText(el);
31092             
31093         }
31094         if(el.tagName){ // element
31095             return this.addElement(el);
31096         }
31097         if(typeof el == "object"){ // must be button config?
31098             return this.addButton(el);
31099         }
31100         // and now what?!?!
31101         return false;
31102         
31103     },
31104     
31105     /**
31106      * Add an Xtype element
31107      * @param {Object} xtype Xtype Object
31108      * @return {Object} created Object
31109      */
31110     addxtype : function(e){
31111         return this.add(e);  
31112     },
31113     
31114     /**
31115      * Returns the Element for this toolbar.
31116      * @return {Roo.Element}
31117      */
31118     getEl : function(){
31119         return this.el;  
31120     },
31121     
31122     /**
31123      * Adds a separator
31124      * @return {Roo.Toolbar.Item} The separator item
31125      */
31126     addSeparator : function(){
31127         return this.addItem(new Roo.Toolbar.Separator());
31128     },
31129
31130     /**
31131      * Adds a spacer element
31132      * @return {Roo.Toolbar.Spacer} The spacer item
31133      */
31134     addSpacer : function(){
31135         return this.addItem(new Roo.Toolbar.Spacer());
31136     },
31137
31138     /**
31139      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31140      * @return {Roo.Toolbar.Fill} The fill item
31141      */
31142     addFill : function(){
31143         return this.addItem(new Roo.Toolbar.Fill());
31144     },
31145
31146     /**
31147      * Adds any standard HTML element to the toolbar
31148      * @param {String/HTMLElement/Element} el The element or id of the element to add
31149      * @return {Roo.Toolbar.Item} The element's item
31150      */
31151     addElement : function(el){
31152         return this.addItem(new Roo.Toolbar.Item(el));
31153     },
31154     /**
31155      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31156      * @type Roo.util.MixedCollection  
31157      */
31158     items : false,
31159      
31160     /**
31161      * Adds any Toolbar.Item or subclass
31162      * @param {Roo.Toolbar.Item} item
31163      * @return {Roo.Toolbar.Item} The item
31164      */
31165     addItem : function(item){
31166         var td = this.nextBlock();
31167         item.render(td);
31168         this.items.add(item);
31169         return item;
31170     },
31171     
31172     /**
31173      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31174      * @param {Object/Array} config A button config or array of configs
31175      * @return {Roo.Toolbar.Button/Array}
31176      */
31177     addButton : function(config){
31178         if(config instanceof Array){
31179             var buttons = [];
31180             for(var i = 0, len = config.length; i < len; i++) {
31181                 buttons.push(this.addButton(config[i]));
31182             }
31183             return buttons;
31184         }
31185         var b = config;
31186         if(!(config instanceof Roo.Toolbar.Button)){
31187             b = config.split ?
31188                 new Roo.Toolbar.SplitButton(config) :
31189                 new Roo.Toolbar.Button(config);
31190         }
31191         var td = this.nextBlock();
31192         b.render(td);
31193         this.items.add(b);
31194         return b;
31195     },
31196     
31197     /**
31198      * Adds text to the toolbar
31199      * @param {String} text The text to add
31200      * @return {Roo.Toolbar.Item} The element's item
31201      */
31202     addText : function(text){
31203         return this.addItem(new Roo.Toolbar.TextItem(text));
31204     },
31205     
31206     /**
31207      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31208      * @param {Number} index The index where the item is to be inserted
31209      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31210      * @return {Roo.Toolbar.Button/Item}
31211      */
31212     insertButton : function(index, item){
31213         if(item instanceof Array){
31214             var buttons = [];
31215             for(var i = 0, len = item.length; i < len; i++) {
31216                buttons.push(this.insertButton(index + i, item[i]));
31217             }
31218             return buttons;
31219         }
31220         if (!(item instanceof Roo.Toolbar.Button)){
31221            item = new Roo.Toolbar.Button(item);
31222         }
31223         var td = document.createElement("td");
31224         this.tr.insertBefore(td, this.tr.childNodes[index]);
31225         item.render(td);
31226         this.items.insert(index, item);
31227         return item;
31228     },
31229     
31230     /**
31231      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31232      * @param {Object} config
31233      * @return {Roo.Toolbar.Item} The element's item
31234      */
31235     addDom : function(config, returnEl){
31236         var td = this.nextBlock();
31237         Roo.DomHelper.overwrite(td, config);
31238         var ti = new Roo.Toolbar.Item(td.firstChild);
31239         ti.render(td);
31240         this.items.add(ti);
31241         return ti;
31242     },
31243
31244     /**
31245      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31246      * @type Roo.util.MixedCollection  
31247      */
31248     fields : false,
31249     
31250     /**
31251      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31252      * Note: the field should not have been rendered yet. For a field that has already been
31253      * rendered, use {@link #addElement}.
31254      * @param {Roo.form.Field} field
31255      * @return {Roo.ToolbarItem}
31256      */
31257      
31258       
31259     addField : function(field) {
31260         if (!this.fields) {
31261             var autoId = 0;
31262             this.fields = new Roo.util.MixedCollection(false, function(o){
31263                 return o.id || ("item" + (++autoId));
31264             });
31265
31266         }
31267         
31268         var td = this.nextBlock();
31269         field.render(td);
31270         var ti = new Roo.Toolbar.Item(td.firstChild);
31271         ti.render(td);
31272         this.items.add(ti);
31273         this.fields.add(field);
31274         return ti;
31275     },
31276     /**
31277      * Hide the toolbar
31278      * @method hide
31279      */
31280      
31281       
31282     hide : function()
31283     {
31284         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31285         this.el.child('div').hide();
31286     },
31287     /**
31288      * Show the toolbar
31289      * @method show
31290      */
31291     show : function()
31292     {
31293         this.el.child('div').show();
31294     },
31295       
31296     // private
31297     nextBlock : function(){
31298         var td = document.createElement("td");
31299         this.tr.appendChild(td);
31300         return td;
31301     },
31302
31303     // private
31304     destroy : function(){
31305         if(this.items){ // rendered?
31306             Roo.destroy.apply(Roo, this.items.items);
31307         }
31308         if(this.fields){ // rendered?
31309             Roo.destroy.apply(Roo, this.fields.items);
31310         }
31311         Roo.Element.uncache(this.el, this.tr);
31312     }
31313 };
31314
31315 /**
31316  * @class Roo.Toolbar.Item
31317  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31318  * @constructor
31319  * Creates a new Item
31320  * @param {HTMLElement} el 
31321  */
31322 Roo.Toolbar.Item = function(el){
31323     var cfg = {};
31324     if (typeof (el.xtype) != 'undefined') {
31325         cfg = el;
31326         el = cfg.el;
31327     }
31328     
31329     this.el = Roo.getDom(el);
31330     this.id = Roo.id(this.el);
31331     this.hidden = false;
31332     
31333     this.addEvents({
31334          /**
31335              * @event render
31336              * Fires when the button is rendered
31337              * @param {Button} this
31338              */
31339         'render': true
31340     });
31341     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31342 };
31343 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31344 //Roo.Toolbar.Item.prototype = {
31345     
31346     /**
31347      * Get this item's HTML Element
31348      * @return {HTMLElement}
31349      */
31350     getEl : function(){
31351        return this.el;  
31352     },
31353
31354     // private
31355     render : function(td){
31356         
31357          this.td = td;
31358         td.appendChild(this.el);
31359         
31360         this.fireEvent('render', this);
31361     },
31362     
31363     /**
31364      * Removes and destroys this item.
31365      */
31366     destroy : function(){
31367         this.td.parentNode.removeChild(this.td);
31368     },
31369     
31370     /**
31371      * Shows this item.
31372      */
31373     show: function(){
31374         this.hidden = false;
31375         this.td.style.display = "";
31376     },
31377     
31378     /**
31379      * Hides this item.
31380      */
31381     hide: function(){
31382         this.hidden = true;
31383         this.td.style.display = "none";
31384     },
31385     
31386     /**
31387      * Convenience function for boolean show/hide.
31388      * @param {Boolean} visible true to show/false to hide
31389      */
31390     setVisible: function(visible){
31391         if(visible) {
31392             this.show();
31393         }else{
31394             this.hide();
31395         }
31396     },
31397     
31398     /**
31399      * Try to focus this item.
31400      */
31401     focus : function(){
31402         Roo.fly(this.el).focus();
31403     },
31404     
31405     /**
31406      * Disables this item.
31407      */
31408     disable : function(){
31409         Roo.fly(this.td).addClass("x-item-disabled");
31410         this.disabled = true;
31411         this.el.disabled = true;
31412     },
31413     
31414     /**
31415      * Enables this item.
31416      */
31417     enable : function(){
31418         Roo.fly(this.td).removeClass("x-item-disabled");
31419         this.disabled = false;
31420         this.el.disabled = false;
31421     }
31422 });
31423
31424
31425 /**
31426  * @class Roo.Toolbar.Separator
31427  * @extends Roo.Toolbar.Item
31428  * A simple toolbar separator class
31429  * @constructor
31430  * Creates a new Separator
31431  */
31432 Roo.Toolbar.Separator = function(cfg){
31433     
31434     var s = document.createElement("span");
31435     s.className = "ytb-sep";
31436     if (cfg) {
31437         cfg.el = s;
31438     }
31439     
31440     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31441 };
31442 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31443     enable:Roo.emptyFn,
31444     disable:Roo.emptyFn,
31445     focus:Roo.emptyFn
31446 });
31447
31448 /**
31449  * @class Roo.Toolbar.Spacer
31450  * @extends Roo.Toolbar.Item
31451  * A simple element that adds extra horizontal space to a toolbar.
31452  * @constructor
31453  * Creates a new Spacer
31454  */
31455 Roo.Toolbar.Spacer = function(cfg){
31456     var s = document.createElement("div");
31457     s.className = "ytb-spacer";
31458     if (cfg) {
31459         cfg.el = s;
31460     }
31461     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31462 };
31463 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31464     enable:Roo.emptyFn,
31465     disable:Roo.emptyFn,
31466     focus:Roo.emptyFn
31467 });
31468
31469 /**
31470  * @class Roo.Toolbar.Fill
31471  * @extends Roo.Toolbar.Spacer
31472  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31473  * @constructor
31474  * Creates a new Spacer
31475  */
31476 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31477     // private
31478     render : function(td){
31479         td.style.width = '100%';
31480         Roo.Toolbar.Fill.superclass.render.call(this, td);
31481     }
31482 });
31483
31484 /**
31485  * @class Roo.Toolbar.TextItem
31486  * @extends Roo.Toolbar.Item
31487  * A simple class that renders text directly into a toolbar.
31488  * @constructor
31489  * Creates a new TextItem
31490  * @cfg {string} text 
31491  */
31492 Roo.Toolbar.TextItem = function(cfg){
31493     var  text = cfg || "";
31494     if (typeof(cfg) == 'object') {
31495         text = cfg.text || "";
31496     }  else {
31497         cfg = null;
31498     }
31499     var s = document.createElement("span");
31500     s.className = "ytb-text";
31501     s.innerHTML = text;
31502     if (cfg) {
31503         cfg.el  = s;
31504     }
31505     
31506     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31507 };
31508 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31509     
31510      
31511     enable:Roo.emptyFn,
31512     disable:Roo.emptyFn,
31513     focus:Roo.emptyFn
31514 });
31515
31516 /**
31517  * @class Roo.Toolbar.Button
31518  * @extends Roo.Button
31519  * A button that renders into a toolbar.
31520  * @constructor
31521  * Creates a new Button
31522  * @param {Object} config A standard {@link Roo.Button} config object
31523  */
31524 Roo.Toolbar.Button = function(config){
31525     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31526 };
31527 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31528 {
31529     
31530     
31531     render : function(td){
31532         this.td = td;
31533         Roo.Toolbar.Button.superclass.render.call(this, td);
31534     },
31535     
31536     /**
31537      * Removes and destroys this button
31538      */
31539     destroy : function(){
31540         Roo.Toolbar.Button.superclass.destroy.call(this);
31541         this.td.parentNode.removeChild(this.td);
31542     },
31543     
31544     /**
31545      * Shows this button
31546      */
31547     show: function(){
31548         this.hidden = false;
31549         this.td.style.display = "";
31550     },
31551     
31552     /**
31553      * Hides this button
31554      */
31555     hide: function(){
31556         this.hidden = true;
31557         this.td.style.display = "none";
31558     },
31559
31560     /**
31561      * Disables this item
31562      */
31563     disable : function(){
31564         Roo.fly(this.td).addClass("x-item-disabled");
31565         this.disabled = true;
31566     },
31567
31568     /**
31569      * Enables this item
31570      */
31571     enable : function(){
31572         Roo.fly(this.td).removeClass("x-item-disabled");
31573         this.disabled = false;
31574     }
31575 });
31576 // backwards compat
31577 Roo.ToolbarButton = Roo.Toolbar.Button;
31578
31579 /**
31580  * @class Roo.Toolbar.SplitButton
31581  * @extends Roo.SplitButton
31582  * A menu button that renders into a toolbar.
31583  * @constructor
31584  * Creates a new SplitButton
31585  * @param {Object} config A standard {@link Roo.SplitButton} config object
31586  */
31587 Roo.Toolbar.SplitButton = function(config){
31588     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31589 };
31590 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31591     render : function(td){
31592         this.td = td;
31593         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31594     },
31595     
31596     /**
31597      * Removes and destroys this button
31598      */
31599     destroy : function(){
31600         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31601         this.td.parentNode.removeChild(this.td);
31602     },
31603     
31604     /**
31605      * Shows this button
31606      */
31607     show: function(){
31608         this.hidden = false;
31609         this.td.style.display = "";
31610     },
31611     
31612     /**
31613      * Hides this button
31614      */
31615     hide: function(){
31616         this.hidden = true;
31617         this.td.style.display = "none";
31618     }
31619 });
31620
31621 // backwards compat
31622 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31623  * Based on:
31624  * Ext JS Library 1.1.1
31625  * Copyright(c) 2006-2007, Ext JS, LLC.
31626  *
31627  * Originally Released Under LGPL - original licence link has changed is not relivant.
31628  *
31629  * Fork - LGPL
31630  * <script type="text/javascript">
31631  */
31632  
31633 /**
31634  * @class Roo.PagingToolbar
31635  * @extends Roo.Toolbar
31636  * @children   Roo.Toolbar.Item Roo.form.Field
31637  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31638  * @constructor
31639  * Create a new PagingToolbar
31640  * @param {Object} config The config object
31641  */
31642 Roo.PagingToolbar = function(el, ds, config)
31643 {
31644     // old args format still supported... - xtype is prefered..
31645     if (typeof(el) == 'object' && el.xtype) {
31646         // created from xtype...
31647         config = el;
31648         ds = el.dataSource;
31649         el = config.container;
31650     }
31651     var items = [];
31652     if (config.items) {
31653         items = config.items;
31654         config.items = [];
31655     }
31656     
31657     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31658     this.ds = ds;
31659     this.cursor = 0;
31660     this.renderButtons(this.el);
31661     this.bind(ds);
31662     
31663     // supprot items array.
31664    
31665     Roo.each(items, function(e) {
31666         this.add(Roo.factory(e));
31667     },this);
31668     
31669 };
31670
31671 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31672    
31673     /**
31674      * @cfg {String/HTMLElement/Element} container
31675      * container The id or element that will contain the toolbar
31676      */
31677     /**
31678      * @cfg {Boolean} displayInfo
31679      * True to display the displayMsg (defaults to false)
31680      */
31681     
31682     
31683     /**
31684      * @cfg {Number} pageSize
31685      * The number of records to display per page (defaults to 20)
31686      */
31687     pageSize: 20,
31688     /**
31689      * @cfg {String} displayMsg
31690      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31691      */
31692     displayMsg : 'Displaying {0} - {1} of {2}',
31693     /**
31694      * @cfg {String} emptyMsg
31695      * The message to display when no records are found (defaults to "No data to display")
31696      */
31697     emptyMsg : 'No data to display',
31698     /**
31699      * Customizable piece of the default paging text (defaults to "Page")
31700      * @type String
31701      */
31702     beforePageText : "Page",
31703     /**
31704      * Customizable piece of the default paging text (defaults to "of %0")
31705      * @type String
31706      */
31707     afterPageText : "of {0}",
31708     /**
31709      * Customizable piece of the default paging text (defaults to "First Page")
31710      * @type String
31711      */
31712     firstText : "First Page",
31713     /**
31714      * Customizable piece of the default paging text (defaults to "Previous Page")
31715      * @type String
31716      */
31717     prevText : "Previous Page",
31718     /**
31719      * Customizable piece of the default paging text (defaults to "Next Page")
31720      * @type String
31721      */
31722     nextText : "Next Page",
31723     /**
31724      * Customizable piece of the default paging text (defaults to "Last Page")
31725      * @type String
31726      */
31727     lastText : "Last Page",
31728     /**
31729      * Customizable piece of the default paging text (defaults to "Refresh")
31730      * @type String
31731      */
31732     refreshText : "Refresh",
31733
31734     // private
31735     renderButtons : function(el){
31736         Roo.PagingToolbar.superclass.render.call(this, el);
31737         this.first = this.addButton({
31738             tooltip: this.firstText,
31739             cls: "x-btn-icon x-grid-page-first",
31740             disabled: true,
31741             handler: this.onClick.createDelegate(this, ["first"])
31742         });
31743         this.prev = this.addButton({
31744             tooltip: this.prevText,
31745             cls: "x-btn-icon x-grid-page-prev",
31746             disabled: true,
31747             handler: this.onClick.createDelegate(this, ["prev"])
31748         });
31749         //this.addSeparator();
31750         this.add(this.beforePageText);
31751         this.field = Roo.get(this.addDom({
31752            tag: "input",
31753            type: "text",
31754            size: "3",
31755            value: "1",
31756            cls: "x-grid-page-number"
31757         }).el);
31758         this.field.on("keydown", this.onPagingKeydown, this);
31759         this.field.on("focus", function(){this.dom.select();});
31760         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31761         this.field.setHeight(18);
31762         //this.addSeparator();
31763         this.next = this.addButton({
31764             tooltip: this.nextText,
31765             cls: "x-btn-icon x-grid-page-next",
31766             disabled: true,
31767             handler: this.onClick.createDelegate(this, ["next"])
31768         });
31769         this.last = this.addButton({
31770             tooltip: this.lastText,
31771             cls: "x-btn-icon x-grid-page-last",
31772             disabled: true,
31773             handler: this.onClick.createDelegate(this, ["last"])
31774         });
31775         //this.addSeparator();
31776         this.loading = this.addButton({
31777             tooltip: this.refreshText,
31778             cls: "x-btn-icon x-grid-loading",
31779             handler: this.onClick.createDelegate(this, ["refresh"])
31780         });
31781
31782         if(this.displayInfo){
31783             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31784         }
31785     },
31786
31787     // private
31788     updateInfo : function(){
31789         if(this.displayEl){
31790             var count = this.ds.getCount();
31791             var msg = count == 0 ?
31792                 this.emptyMsg :
31793                 String.format(
31794                     this.displayMsg,
31795                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31796                 );
31797             this.displayEl.update(msg);
31798         }
31799     },
31800
31801     // private
31802     onLoad : function(ds, r, o){
31803        this.cursor = o.params ? o.params.start : 0;
31804        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31805
31806        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31807        this.field.dom.value = ap;
31808        this.first.setDisabled(ap == 1);
31809        this.prev.setDisabled(ap == 1);
31810        this.next.setDisabled(ap == ps);
31811        this.last.setDisabled(ap == ps);
31812        this.loading.enable();
31813        this.updateInfo();
31814     },
31815
31816     // private
31817     getPageData : function(){
31818         var total = this.ds.getTotalCount();
31819         return {
31820             total : total,
31821             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31822             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31823         };
31824     },
31825
31826     // private
31827     onLoadError : function(){
31828         this.loading.enable();
31829     },
31830
31831     // private
31832     onPagingKeydown : function(e){
31833         var k = e.getKey();
31834         var d = this.getPageData();
31835         if(k == e.RETURN){
31836             var v = this.field.dom.value, pageNum;
31837             if(!v || isNaN(pageNum = parseInt(v, 10))){
31838                 this.field.dom.value = d.activePage;
31839                 return;
31840             }
31841             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31842             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31843             e.stopEvent();
31844         }
31845         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
31846         {
31847           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31848           this.field.dom.value = pageNum;
31849           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31850           e.stopEvent();
31851         }
31852         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31853         {
31854           var v = this.field.dom.value, pageNum; 
31855           var increment = (e.shiftKey) ? 10 : 1;
31856           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31857             increment *= -1;
31858           }
31859           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31860             this.field.dom.value = d.activePage;
31861             return;
31862           }
31863           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31864           {
31865             this.field.dom.value = parseInt(v, 10) + increment;
31866             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31867             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31868           }
31869           e.stopEvent();
31870         }
31871     },
31872
31873     // private
31874     beforeLoad : function(){
31875         if(this.loading){
31876             this.loading.disable();
31877         }
31878     },
31879
31880     // private
31881     onClick : function(which){
31882         var ds = this.ds;
31883         switch(which){
31884             case "first":
31885                 ds.load({params:{start: 0, limit: this.pageSize}});
31886             break;
31887             case "prev":
31888                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31889             break;
31890             case "next":
31891                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31892             break;
31893             case "last":
31894                 var total = ds.getTotalCount();
31895                 var extra = total % this.pageSize;
31896                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31897                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31898             break;
31899             case "refresh":
31900                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31901             break;
31902         }
31903     },
31904
31905     /**
31906      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31907      * @param {Roo.data.Store} store The data store to unbind
31908      */
31909     unbind : function(ds){
31910         ds.un("beforeload", this.beforeLoad, this);
31911         ds.un("load", this.onLoad, this);
31912         ds.un("loadexception", this.onLoadError, this);
31913         ds.un("remove", this.updateInfo, this);
31914         ds.un("add", this.updateInfo, this);
31915         this.ds = undefined;
31916     },
31917
31918     /**
31919      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31920      * @param {Roo.data.Store} store The data store to bind
31921      */
31922     bind : function(ds){
31923         ds.on("beforeload", this.beforeLoad, this);
31924         ds.on("load", this.onLoad, this);
31925         ds.on("loadexception", this.onLoadError, this);
31926         ds.on("remove", this.updateInfo, this);
31927         ds.on("add", this.updateInfo, this);
31928         this.ds = ds;
31929     }
31930 });/*
31931  * Based on:
31932  * Ext JS Library 1.1.1
31933  * Copyright(c) 2006-2007, Ext JS, LLC.
31934  *
31935  * Originally Released Under LGPL - original licence link has changed is not relivant.
31936  *
31937  * Fork - LGPL
31938  * <script type="text/javascript">
31939  */
31940
31941 /**
31942  * @class Roo.Resizable
31943  * @extends Roo.util.Observable
31944  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31945  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31946  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
31947  * the element will be wrapped for you automatically.</p>
31948  * <p>Here is the list of valid resize handles:</p>
31949  * <pre>
31950 Value   Description
31951 ------  -------------------
31952  'n'     north
31953  's'     south
31954  'e'     east
31955  'w'     west
31956  'nw'    northwest
31957  'sw'    southwest
31958  'se'    southeast
31959  'ne'    northeast
31960  'hd'    horizontal drag
31961  'all'   all
31962 </pre>
31963  * <p>Here's an example showing the creation of a typical Resizable:</p>
31964  * <pre><code>
31965 var resizer = new Roo.Resizable("element-id", {
31966     handles: 'all',
31967     minWidth: 200,
31968     minHeight: 100,
31969     maxWidth: 500,
31970     maxHeight: 400,
31971     pinned: true
31972 });
31973 resizer.on("resize", myHandler);
31974 </code></pre>
31975  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31976  * resizer.east.setDisplayed(false);</p>
31977  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31978  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31979  * resize operation's new size (defaults to [0, 0])
31980  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31981  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31982  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31983  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31984  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31985  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31986  * @cfg {Number} width The width of the element in pixels (defaults to null)
31987  * @cfg {Number} height The height of the element in pixels (defaults to null)
31988  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31989  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31990  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31991  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31992  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31993  * in favor of the handles config option (defaults to false)
31994  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31995  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31996  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31997  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31998  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31999  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32000  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32001  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32002  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32003  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32004  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32005  * @constructor
32006  * Create a new resizable component
32007  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32008  * @param {Object} config configuration options
32009   */
32010 Roo.Resizable = function(el, config)
32011 {
32012     this.el = Roo.get(el);
32013
32014     if(config && config.wrap){
32015         config.resizeChild = this.el;
32016         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32017         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32018         this.el.setStyle("overflow", "hidden");
32019         this.el.setPositioning(config.resizeChild.getPositioning());
32020         config.resizeChild.clearPositioning();
32021         if(!config.width || !config.height){
32022             var csize = config.resizeChild.getSize();
32023             this.el.setSize(csize.width, csize.height);
32024         }
32025         if(config.pinned && !config.adjustments){
32026             config.adjustments = "auto";
32027         }
32028     }
32029
32030     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32031     this.proxy.unselectable();
32032     this.proxy.enableDisplayMode('block');
32033
32034     Roo.apply(this, config);
32035
32036     if(this.pinned){
32037         this.disableTrackOver = true;
32038         this.el.addClass("x-resizable-pinned");
32039     }
32040     // if the element isn't positioned, make it relative
32041     var position = this.el.getStyle("position");
32042     if(position != "absolute" && position != "fixed"){
32043         this.el.setStyle("position", "relative");
32044     }
32045     if(!this.handles){ // no handles passed, must be legacy style
32046         this.handles = 's,e,se';
32047         if(this.multiDirectional){
32048             this.handles += ',n,w';
32049         }
32050     }
32051     if(this.handles == "all"){
32052         this.handles = "n s e w ne nw se sw";
32053     }
32054     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32055     var ps = Roo.Resizable.positions;
32056     for(var i = 0, len = hs.length; i < len; i++){
32057         if(hs[i] && ps[hs[i]]){
32058             var pos = ps[hs[i]];
32059             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32060         }
32061     }
32062     // legacy
32063     this.corner = this.southeast;
32064     
32065     // updateBox = the box can move..
32066     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32067         this.updateBox = true;
32068     }
32069
32070     this.activeHandle = null;
32071
32072     if(this.resizeChild){
32073         if(typeof this.resizeChild == "boolean"){
32074             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32075         }else{
32076             this.resizeChild = Roo.get(this.resizeChild, true);
32077         }
32078     }
32079     
32080     if(this.adjustments == "auto"){
32081         var rc = this.resizeChild;
32082         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32083         if(rc && (hw || hn)){
32084             rc.position("relative");
32085             rc.setLeft(hw ? hw.el.getWidth() : 0);
32086             rc.setTop(hn ? hn.el.getHeight() : 0);
32087         }
32088         this.adjustments = [
32089             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32090             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32091         ];
32092     }
32093
32094     if(this.draggable){
32095         this.dd = this.dynamic ?
32096             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32097         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32098     }
32099
32100     // public events
32101     this.addEvents({
32102         /**
32103          * @event beforeresize
32104          * Fired before resize is allowed. Set enabled to false to cancel resize.
32105          * @param {Roo.Resizable} this
32106          * @param {Roo.EventObject} e The mousedown event
32107          */
32108         "beforeresize" : true,
32109         /**
32110          * @event resizing
32111          * Fired a resizing.
32112          * @param {Roo.Resizable} this
32113          * @param {Number} x The new x position
32114          * @param {Number} y The new y position
32115          * @param {Number} w The new w width
32116          * @param {Number} h The new h hight
32117          * @param {Roo.EventObject} e The mouseup event
32118          */
32119         "resizing" : true,
32120         /**
32121          * @event resize
32122          * Fired after a resize.
32123          * @param {Roo.Resizable} this
32124          * @param {Number} width The new width
32125          * @param {Number} height The new height
32126          * @param {Roo.EventObject} e The mouseup event
32127          */
32128         "resize" : true
32129     });
32130
32131     if(this.width !== null && this.height !== null){
32132         this.resizeTo(this.width, this.height);
32133     }else{
32134         this.updateChildSize();
32135     }
32136     if(Roo.isIE){
32137         this.el.dom.style.zoom = 1;
32138     }
32139     Roo.Resizable.superclass.constructor.call(this);
32140 };
32141
32142 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32143         resizeChild : false,
32144         adjustments : [0, 0],
32145         minWidth : 5,
32146         minHeight : 5,
32147         maxWidth : 10000,
32148         maxHeight : 10000,
32149         enabled : true,
32150         animate : false,
32151         duration : .35,
32152         dynamic : false,
32153         handles : false,
32154         multiDirectional : false,
32155         disableTrackOver : false,
32156         easing : 'easeOutStrong',
32157         widthIncrement : 0,
32158         heightIncrement : 0,
32159         pinned : false,
32160         width : null,
32161         height : null,
32162         preserveRatio : false,
32163         transparent: false,
32164         minX: 0,
32165         minY: 0,
32166         draggable: false,
32167
32168         /**
32169          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32170          */
32171         constrainTo: undefined,
32172         /**
32173          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32174          */
32175         resizeRegion: undefined,
32176
32177
32178     /**
32179      * Perform a manual resize
32180      * @param {Number} width
32181      * @param {Number} height
32182      */
32183     resizeTo : function(width, height){
32184         this.el.setSize(width, height);
32185         this.updateChildSize();
32186         this.fireEvent("resize", this, width, height, null);
32187     },
32188
32189     // private
32190     startSizing : function(e, handle){
32191         this.fireEvent("beforeresize", this, e);
32192         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32193
32194             if(!this.overlay){
32195                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32196                 this.overlay.unselectable();
32197                 this.overlay.enableDisplayMode("block");
32198                 this.overlay.on("mousemove", this.onMouseMove, this);
32199                 this.overlay.on("mouseup", this.onMouseUp, this);
32200             }
32201             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32202
32203             this.resizing = true;
32204             this.startBox = this.el.getBox();
32205             this.startPoint = e.getXY();
32206             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32207                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32208
32209             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32210             this.overlay.show();
32211
32212             if(this.constrainTo) {
32213                 var ct = Roo.get(this.constrainTo);
32214                 this.resizeRegion = ct.getRegion().adjust(
32215                     ct.getFrameWidth('t'),
32216                     ct.getFrameWidth('l'),
32217                     -ct.getFrameWidth('b'),
32218                     -ct.getFrameWidth('r')
32219                 );
32220             }
32221
32222             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32223             this.proxy.show();
32224             this.proxy.setBox(this.startBox);
32225             if(!this.dynamic){
32226                 this.proxy.setStyle('visibility', 'visible');
32227             }
32228         }
32229     },
32230
32231     // private
32232     onMouseDown : function(handle, e){
32233         if(this.enabled){
32234             e.stopEvent();
32235             this.activeHandle = handle;
32236             this.startSizing(e, handle);
32237         }
32238     },
32239
32240     // private
32241     onMouseUp : function(e){
32242         var size = this.resizeElement();
32243         this.resizing = false;
32244         this.handleOut();
32245         this.overlay.hide();
32246         this.proxy.hide();
32247         this.fireEvent("resize", this, size.width, size.height, e);
32248     },
32249
32250     // private
32251     updateChildSize : function(){
32252         
32253         if(this.resizeChild){
32254             var el = this.el;
32255             var child = this.resizeChild;
32256             var adj = this.adjustments;
32257             if(el.dom.offsetWidth){
32258                 var b = el.getSize(true);
32259                 child.setSize(b.width+adj[0], b.height+adj[1]);
32260             }
32261             // Second call here for IE
32262             // The first call enables instant resizing and
32263             // the second call corrects scroll bars if they
32264             // exist
32265             if(Roo.isIE){
32266                 setTimeout(function(){
32267                     if(el.dom.offsetWidth){
32268                         var b = el.getSize(true);
32269                         child.setSize(b.width+adj[0], b.height+adj[1]);
32270                     }
32271                 }, 10);
32272             }
32273         }
32274     },
32275
32276     // private
32277     snap : function(value, inc, min){
32278         if(!inc || !value) {
32279             return value;
32280         }
32281         var newValue = value;
32282         var m = value % inc;
32283         if(m > 0){
32284             if(m > (inc/2)){
32285                 newValue = value + (inc-m);
32286             }else{
32287                 newValue = value - m;
32288             }
32289         }
32290         return Math.max(min, newValue);
32291     },
32292
32293     // private
32294     resizeElement : function(){
32295         var box = this.proxy.getBox();
32296         if(this.updateBox){
32297             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32298         }else{
32299             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32300         }
32301         this.updateChildSize();
32302         if(!this.dynamic){
32303             this.proxy.hide();
32304         }
32305         return box;
32306     },
32307
32308     // private
32309     constrain : function(v, diff, m, mx){
32310         if(v - diff < m){
32311             diff = v - m;
32312         }else if(v - diff > mx){
32313             diff = mx - v;
32314         }
32315         return diff;
32316     },
32317
32318     // private
32319     onMouseMove : function(e){
32320         
32321         if(this.enabled){
32322             try{// try catch so if something goes wrong the user doesn't get hung
32323
32324             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32325                 return;
32326             }
32327
32328             //var curXY = this.startPoint;
32329             var curSize = this.curSize || this.startBox;
32330             var x = this.startBox.x, y = this.startBox.y;
32331             var ox = x, oy = y;
32332             var w = curSize.width, h = curSize.height;
32333             var ow = w, oh = h;
32334             var mw = this.minWidth, mh = this.minHeight;
32335             var mxw = this.maxWidth, mxh = this.maxHeight;
32336             var wi = this.widthIncrement;
32337             var hi = this.heightIncrement;
32338
32339             var eventXY = e.getXY();
32340             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32341             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32342
32343             var pos = this.activeHandle.position;
32344
32345             switch(pos){
32346                 case "east":
32347                     w += diffX;
32348                     w = Math.min(Math.max(mw, w), mxw);
32349                     break;
32350              
32351                 case "south":
32352                     h += diffY;
32353                     h = Math.min(Math.max(mh, h), mxh);
32354                     break;
32355                 case "southeast":
32356                     w += diffX;
32357                     h += diffY;
32358                     w = Math.min(Math.max(mw, w), mxw);
32359                     h = Math.min(Math.max(mh, h), mxh);
32360                     break;
32361                 case "north":
32362                     diffY = this.constrain(h, diffY, mh, mxh);
32363                     y += diffY;
32364                     h -= diffY;
32365                     break;
32366                 case "hdrag":
32367                     
32368                     if (wi) {
32369                         var adiffX = Math.abs(diffX);
32370                         var sub = (adiffX % wi); // how much 
32371                         if (sub > (wi/2)) { // far enough to snap
32372                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32373                         } else {
32374                             // remove difference.. 
32375                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32376                         }
32377                     }
32378                     x += diffX;
32379                     x = Math.max(this.minX, x);
32380                     break;
32381                 case "west":
32382                     diffX = this.constrain(w, diffX, mw, mxw);
32383                     x += diffX;
32384                     w -= diffX;
32385                     break;
32386                 case "northeast":
32387                     w += diffX;
32388                     w = Math.min(Math.max(mw, w), mxw);
32389                     diffY = this.constrain(h, diffY, mh, mxh);
32390                     y += diffY;
32391                     h -= diffY;
32392                     break;
32393                 case "northwest":
32394                     diffX = this.constrain(w, diffX, mw, mxw);
32395                     diffY = this.constrain(h, diffY, mh, mxh);
32396                     y += diffY;
32397                     h -= diffY;
32398                     x += diffX;
32399                     w -= diffX;
32400                     break;
32401                case "southwest":
32402                     diffX = this.constrain(w, diffX, mw, mxw);
32403                     h += diffY;
32404                     h = Math.min(Math.max(mh, h), mxh);
32405                     x += diffX;
32406                     w -= diffX;
32407                     break;
32408             }
32409
32410             var sw = this.snap(w, wi, mw);
32411             var sh = this.snap(h, hi, mh);
32412             if(sw != w || sh != h){
32413                 switch(pos){
32414                     case "northeast":
32415                         y -= sh - h;
32416                     break;
32417                     case "north":
32418                         y -= sh - h;
32419                         break;
32420                     case "southwest":
32421                         x -= sw - w;
32422                     break;
32423                     case "west":
32424                         x -= sw - w;
32425                         break;
32426                     case "northwest":
32427                         x -= sw - w;
32428                         y -= sh - h;
32429                     break;
32430                 }
32431                 w = sw;
32432                 h = sh;
32433             }
32434
32435             if(this.preserveRatio){
32436                 switch(pos){
32437                     case "southeast":
32438                     case "east":
32439                         h = oh * (w/ow);
32440                         h = Math.min(Math.max(mh, h), mxh);
32441                         w = ow * (h/oh);
32442                        break;
32443                     case "south":
32444                         w = ow * (h/oh);
32445                         w = Math.min(Math.max(mw, w), mxw);
32446                         h = oh * (w/ow);
32447                         break;
32448                     case "northeast":
32449                         w = ow * (h/oh);
32450                         w = Math.min(Math.max(mw, w), mxw);
32451                         h = oh * (w/ow);
32452                     break;
32453                     case "north":
32454                         var tw = w;
32455                         w = ow * (h/oh);
32456                         w = Math.min(Math.max(mw, w), mxw);
32457                         h = oh * (w/ow);
32458                         x += (tw - w) / 2;
32459                         break;
32460                     case "southwest":
32461                         h = oh * (w/ow);
32462                         h = Math.min(Math.max(mh, h), mxh);
32463                         var tw = w;
32464                         w = ow * (h/oh);
32465                         x += tw - w;
32466                         break;
32467                     case "west":
32468                         var th = h;
32469                         h = oh * (w/ow);
32470                         h = Math.min(Math.max(mh, h), mxh);
32471                         y += (th - h) / 2;
32472                         var tw = w;
32473                         w = ow * (h/oh);
32474                         x += tw - w;
32475                        break;
32476                     case "northwest":
32477                         var tw = w;
32478                         var th = h;
32479                         h = oh * (w/ow);
32480                         h = Math.min(Math.max(mh, h), mxh);
32481                         w = ow * (h/oh);
32482                         y += th - h;
32483                         x += tw - w;
32484                        break;
32485
32486                 }
32487             }
32488             if (pos == 'hdrag') {
32489                 w = ow;
32490             }
32491             this.proxy.setBounds(x, y, w, h);
32492             if(this.dynamic){
32493                 this.resizeElement();
32494             }
32495             }catch(e){}
32496         }
32497         this.fireEvent("resizing", this, x, y, w, h, e);
32498     },
32499
32500     // private
32501     handleOver : function(){
32502         if(this.enabled){
32503             this.el.addClass("x-resizable-over");
32504         }
32505     },
32506
32507     // private
32508     handleOut : function(){
32509         if(!this.resizing){
32510             this.el.removeClass("x-resizable-over");
32511         }
32512     },
32513
32514     /**
32515      * Returns the element this component is bound to.
32516      * @return {Roo.Element}
32517      */
32518     getEl : function(){
32519         return this.el;
32520     },
32521
32522     /**
32523      * Returns the resizeChild element (or null).
32524      * @return {Roo.Element}
32525      */
32526     getResizeChild : function(){
32527         return this.resizeChild;
32528     },
32529     groupHandler : function()
32530     {
32531         
32532     },
32533     /**
32534      * Destroys this resizable. If the element was wrapped and
32535      * removeEl is not true then the element remains.
32536      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32537      */
32538     destroy : function(removeEl){
32539         this.proxy.remove();
32540         if(this.overlay){
32541             this.overlay.removeAllListeners();
32542             this.overlay.remove();
32543         }
32544         var ps = Roo.Resizable.positions;
32545         for(var k in ps){
32546             if(typeof ps[k] != "function" && this[ps[k]]){
32547                 var h = this[ps[k]];
32548                 h.el.removeAllListeners();
32549                 h.el.remove();
32550             }
32551         }
32552         if(removeEl){
32553             this.el.update("");
32554             this.el.remove();
32555         }
32556     }
32557 });
32558
32559 // private
32560 // hash to map config positions to true positions
32561 Roo.Resizable.positions = {
32562     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32563     hd: "hdrag"
32564 };
32565
32566 // private
32567 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32568     if(!this.tpl){
32569         // only initialize the template if resizable is used
32570         var tpl = Roo.DomHelper.createTemplate(
32571             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32572         );
32573         tpl.compile();
32574         Roo.Resizable.Handle.prototype.tpl = tpl;
32575     }
32576     this.position = pos;
32577     this.rz = rz;
32578     // show north drag fro topdra
32579     var handlepos = pos == 'hdrag' ? 'north' : pos;
32580     
32581     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32582     if (pos == 'hdrag') {
32583         this.el.setStyle('cursor', 'pointer');
32584     }
32585     this.el.unselectable();
32586     if(transparent){
32587         this.el.setOpacity(0);
32588     }
32589     this.el.on("mousedown", this.onMouseDown, this);
32590     if(!disableTrackOver){
32591         this.el.on("mouseover", this.onMouseOver, this);
32592         this.el.on("mouseout", this.onMouseOut, this);
32593     }
32594 };
32595
32596 // private
32597 Roo.Resizable.Handle.prototype = {
32598     afterResize : function(rz){
32599         Roo.log('after?');
32600         // do nothing
32601     },
32602     // private
32603     onMouseDown : function(e){
32604         this.rz.onMouseDown(this, e);
32605     },
32606     // private
32607     onMouseOver : function(e){
32608         this.rz.handleOver(this, e);
32609     },
32610     // private
32611     onMouseOut : function(e){
32612         this.rz.handleOut(this, e);
32613     }
32614 };/*
32615  * Based on:
32616  * Ext JS Library 1.1.1
32617  * Copyright(c) 2006-2007, Ext JS, LLC.
32618  *
32619  * Originally Released Under LGPL - original licence link has changed is not relivant.
32620  *
32621  * Fork - LGPL
32622  * <script type="text/javascript">
32623  */
32624
32625 /**
32626  * @class Roo.Editor
32627  * @extends Roo.Component
32628  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32629  * @constructor
32630  * Create a new Editor
32631  * @param {Roo.form.Field} field The Field object (or descendant)
32632  * @param {Object} config The config object
32633  */
32634 Roo.Editor = function(field, config){
32635     Roo.Editor.superclass.constructor.call(this, config);
32636     this.field = field;
32637     this.addEvents({
32638         /**
32639              * @event beforestartedit
32640              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32641              * false from the handler of this event.
32642              * @param {Editor} this
32643              * @param {Roo.Element} boundEl The underlying element bound to this editor
32644              * @param {Mixed} value The field value being set
32645              */
32646         "beforestartedit" : true,
32647         /**
32648              * @event startedit
32649              * Fires when this editor is displayed
32650              * @param {Roo.Element} boundEl The underlying element bound to this editor
32651              * @param {Mixed} value The starting field value
32652              */
32653         "startedit" : true,
32654         /**
32655              * @event beforecomplete
32656              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32657              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32658              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32659              * event will not fire since no edit actually occurred.
32660              * @param {Editor} this
32661              * @param {Mixed} value The current field value
32662              * @param {Mixed} startValue The original field value
32663              */
32664         "beforecomplete" : true,
32665         /**
32666              * @event complete
32667              * Fires after editing is complete and any changed value has been written to the underlying field.
32668              * @param {Editor} this
32669              * @param {Mixed} value The current field value
32670              * @param {Mixed} startValue The original field value
32671              */
32672         "complete" : true,
32673         /**
32674          * @event specialkey
32675          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32676          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32677          * @param {Roo.form.Field} this
32678          * @param {Roo.EventObject} e The event object
32679          */
32680         "specialkey" : true
32681     });
32682 };
32683
32684 Roo.extend(Roo.Editor, Roo.Component, {
32685     /**
32686      * @cfg {Boolean/String} autosize
32687      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32688      * or "height" to adopt the height only (defaults to false)
32689      */
32690     /**
32691      * @cfg {Boolean} revertInvalid
32692      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32693      * validation fails (defaults to true)
32694      */
32695     /**
32696      * @cfg {Boolean} ignoreNoChange
32697      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32698      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32699      * will never be ignored.
32700      */
32701     /**
32702      * @cfg {Boolean} hideEl
32703      * False to keep the bound element visible while the editor is displayed (defaults to true)
32704      */
32705     /**
32706      * @cfg {Mixed} value
32707      * The data value of the underlying field (defaults to "")
32708      */
32709     value : "",
32710     /**
32711      * @cfg {String} alignment
32712      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32713      */
32714     alignment: "c-c?",
32715     /**
32716      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32717      * for bottom-right shadow (defaults to "frame")
32718      */
32719     shadow : "frame",
32720     /**
32721      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32722      */
32723     constrain : false,
32724     /**
32725      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32726      */
32727     completeOnEnter : false,
32728     /**
32729      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32730      */
32731     cancelOnEsc : false,
32732     /**
32733      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32734      */
32735     updateEl : false,
32736
32737     // private
32738     onRender : function(ct, position){
32739         this.el = new Roo.Layer({
32740             shadow: this.shadow,
32741             cls: "x-editor",
32742             parentEl : ct,
32743             shim : this.shim,
32744             shadowOffset:4,
32745             id: this.id,
32746             constrain: this.constrain
32747         });
32748         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32749         if(this.field.msgTarget != 'title'){
32750             this.field.msgTarget = 'qtip';
32751         }
32752         this.field.render(this.el);
32753         if(Roo.isGecko){
32754             this.field.el.dom.setAttribute('autocomplete', 'off');
32755         }
32756         this.field.on("specialkey", this.onSpecialKey, this);
32757         if(this.swallowKeys){
32758             this.field.el.swallowEvent(['keydown','keypress']);
32759         }
32760         this.field.show();
32761         this.field.on("blur", this.onBlur, this);
32762         if(this.field.grow){
32763             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32764         }
32765     },
32766
32767     onSpecialKey : function(field, e)
32768     {
32769         //Roo.log('editor onSpecialKey');
32770         if(this.completeOnEnter && e.getKey() == e.ENTER){
32771             e.stopEvent();
32772             this.completeEdit();
32773             return;
32774         }
32775         // do not fire special key otherwise it might hide close the editor...
32776         if(e.getKey() == e.ENTER){    
32777             return;
32778         }
32779         if(this.cancelOnEsc && e.getKey() == e.ESC){
32780             this.cancelEdit();
32781             return;
32782         } 
32783         this.fireEvent('specialkey', field, e);
32784     
32785     },
32786
32787     /**
32788      * Starts the editing process and shows the editor.
32789      * @param {String/HTMLElement/Element} el The element to edit
32790      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32791       * to the innerHTML of el.
32792      */
32793     startEdit : function(el, value){
32794         if(this.editing){
32795             this.completeEdit();
32796         }
32797         this.boundEl = Roo.get(el);
32798         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32799         if(!this.rendered){
32800             this.render(this.parentEl || document.body);
32801         }
32802         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32803             return;
32804         }
32805         this.startValue = v;
32806         this.field.setValue(v);
32807         if(this.autoSize){
32808             var sz = this.boundEl.getSize();
32809             switch(this.autoSize){
32810                 case "width":
32811                 this.setSize(sz.width,  "");
32812                 break;
32813                 case "height":
32814                 this.setSize("",  sz.height);
32815                 break;
32816                 default:
32817                 this.setSize(sz.width,  sz.height);
32818             }
32819         }
32820         this.el.alignTo(this.boundEl, this.alignment);
32821         this.editing = true;
32822         if(Roo.QuickTips){
32823             Roo.QuickTips.disable();
32824         }
32825         this.show();
32826     },
32827
32828     /**
32829      * Sets the height and width of this editor.
32830      * @param {Number} width The new width
32831      * @param {Number} height The new height
32832      */
32833     setSize : function(w, h){
32834         this.field.setSize(w, h);
32835         if(this.el){
32836             this.el.sync();
32837         }
32838     },
32839
32840     /**
32841      * Realigns the editor to the bound field based on the current alignment config value.
32842      */
32843     realign : function(){
32844         this.el.alignTo(this.boundEl, this.alignment);
32845     },
32846
32847     /**
32848      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32849      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32850      */
32851     completeEdit : function(remainVisible){
32852         if(!this.editing){
32853             return;
32854         }
32855         var v = this.getValue();
32856         if(this.revertInvalid !== false && !this.field.isValid()){
32857             v = this.startValue;
32858             this.cancelEdit(true);
32859         }
32860         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32861             this.editing = false;
32862             this.hide();
32863             return;
32864         }
32865         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32866             this.editing = false;
32867             if(this.updateEl && this.boundEl){
32868                 this.boundEl.update(v);
32869             }
32870             if(remainVisible !== true){
32871                 this.hide();
32872             }
32873             this.fireEvent("complete", this, v, this.startValue);
32874         }
32875     },
32876
32877     // private
32878     onShow : function(){
32879         this.el.show();
32880         if(this.hideEl !== false){
32881             this.boundEl.hide();
32882         }
32883         this.field.show();
32884         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32885             this.fixIEFocus = true;
32886             this.deferredFocus.defer(50, this);
32887         }else{
32888             this.field.focus();
32889         }
32890         this.fireEvent("startedit", this.boundEl, this.startValue);
32891     },
32892
32893     deferredFocus : function(){
32894         if(this.editing){
32895             this.field.focus();
32896         }
32897     },
32898
32899     /**
32900      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32901      * reverted to the original starting value.
32902      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32903      * cancel (defaults to false)
32904      */
32905     cancelEdit : function(remainVisible){
32906         if(this.editing){
32907             this.setValue(this.startValue);
32908             if(remainVisible !== true){
32909                 this.hide();
32910             }
32911         }
32912     },
32913
32914     // private
32915     onBlur : function(){
32916         if(this.allowBlur !== true && this.editing){
32917             this.completeEdit();
32918         }
32919     },
32920
32921     // private
32922     onHide : function(){
32923         if(this.editing){
32924             this.completeEdit();
32925             return;
32926         }
32927         this.field.blur();
32928         if(this.field.collapse){
32929             this.field.collapse();
32930         }
32931         this.el.hide();
32932         if(this.hideEl !== false){
32933             this.boundEl.show();
32934         }
32935         if(Roo.QuickTips){
32936             Roo.QuickTips.enable();
32937         }
32938     },
32939
32940     /**
32941      * Sets the data value of the editor
32942      * @param {Mixed} value Any valid value supported by the underlying field
32943      */
32944     setValue : function(v){
32945         this.field.setValue(v);
32946     },
32947
32948     /**
32949      * Gets the data value of the editor
32950      * @return {Mixed} The data value
32951      */
32952     getValue : function(){
32953         return this.field.getValue();
32954     }
32955 });/*
32956  * Based on:
32957  * Ext JS Library 1.1.1
32958  * Copyright(c) 2006-2007, Ext JS, LLC.
32959  *
32960  * Originally Released Under LGPL - original licence link has changed is not relivant.
32961  *
32962  * Fork - LGPL
32963  * <script type="text/javascript">
32964  */
32965  
32966 /**
32967  * @class Roo.BasicDialog
32968  * @extends Roo.util.Observable
32969  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32970  * <pre><code>
32971 var dlg = new Roo.BasicDialog("my-dlg", {
32972     height: 200,
32973     width: 300,
32974     minHeight: 100,
32975     minWidth: 150,
32976     modal: true,
32977     proxyDrag: true,
32978     shadow: true
32979 });
32980 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32981 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32982 dlg.addButton('Cancel', dlg.hide, dlg);
32983 dlg.show();
32984 </code></pre>
32985   <b>A Dialog should always be a direct child of the body element.</b>
32986  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32987  * @cfg {String} title Default text to display in the title bar (defaults to null)
32988  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32989  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32990  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32991  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32992  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32993  * (defaults to null with no animation)
32994  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32995  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32996  * property for valid values (defaults to 'all')
32997  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32998  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32999  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33000  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33001  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33002  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33003  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33004  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33005  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33006  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33007  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33008  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33009  * draggable = true (defaults to false)
33010  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33011  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33012  * shadow (defaults to false)
33013  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33014  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33015  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33016  * @cfg {Array} buttons Array of buttons
33017  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33018  * @constructor
33019  * Create a new BasicDialog.
33020  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33021  * @param {Object} config Configuration options
33022  */
33023 Roo.BasicDialog = function(el, config){
33024     this.el = Roo.get(el);
33025     var dh = Roo.DomHelper;
33026     if(!this.el && config && config.autoCreate){
33027         if(typeof config.autoCreate == "object"){
33028             if(!config.autoCreate.id){
33029                 config.autoCreate.id = el;
33030             }
33031             this.el = dh.append(document.body,
33032                         config.autoCreate, true);
33033         }else{
33034             this.el = dh.append(document.body,
33035                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33036         }
33037     }
33038     el = this.el;
33039     el.setDisplayed(true);
33040     el.hide = this.hideAction;
33041     this.id = el.id;
33042     el.addClass("x-dlg");
33043
33044     Roo.apply(this, config);
33045
33046     this.proxy = el.createProxy("x-dlg-proxy");
33047     this.proxy.hide = this.hideAction;
33048     this.proxy.setOpacity(.5);
33049     this.proxy.hide();
33050
33051     if(config.width){
33052         el.setWidth(config.width);
33053     }
33054     if(config.height){
33055         el.setHeight(config.height);
33056     }
33057     this.size = el.getSize();
33058     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33059         this.xy = [config.x,config.y];
33060     }else{
33061         this.xy = el.getCenterXY(true);
33062     }
33063     /** The header element @type Roo.Element */
33064     this.header = el.child("> .x-dlg-hd");
33065     /** The body element @type Roo.Element */
33066     this.body = el.child("> .x-dlg-bd");
33067     /** The footer element @type Roo.Element */
33068     this.footer = el.child("> .x-dlg-ft");
33069
33070     if(!this.header){
33071         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33072     }
33073     if(!this.body){
33074         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33075     }
33076
33077     this.header.unselectable();
33078     if(this.title){
33079         this.header.update(this.title);
33080     }
33081     // this element allows the dialog to be focused for keyboard event
33082     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33083     this.focusEl.swallowEvent("click", true);
33084
33085     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33086
33087     // wrap the body and footer for special rendering
33088     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33089     if(this.footer){
33090         this.bwrap.dom.appendChild(this.footer.dom);
33091     }
33092
33093     this.bg = this.el.createChild({
33094         tag: "div", cls:"x-dlg-bg",
33095         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33096     });
33097     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33098
33099
33100     if(this.autoScroll !== false && !this.autoTabs){
33101         this.body.setStyle("overflow", "auto");
33102     }
33103
33104     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33105
33106     if(this.closable !== false){
33107         this.el.addClass("x-dlg-closable");
33108         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33109         this.close.on("click", this.closeClick, this);
33110         this.close.addClassOnOver("x-dlg-close-over");
33111     }
33112     if(this.collapsible !== false){
33113         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33114         this.collapseBtn.on("click", this.collapseClick, this);
33115         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33116         this.header.on("dblclick", this.collapseClick, this);
33117     }
33118     if(this.resizable !== false){
33119         this.el.addClass("x-dlg-resizable");
33120         this.resizer = new Roo.Resizable(el, {
33121             minWidth: this.minWidth || 80,
33122             minHeight:this.minHeight || 80,
33123             handles: this.resizeHandles || "all",
33124             pinned: true
33125         });
33126         this.resizer.on("beforeresize", this.beforeResize, this);
33127         this.resizer.on("resize", this.onResize, this);
33128     }
33129     if(this.draggable !== false){
33130         el.addClass("x-dlg-draggable");
33131         if (!this.proxyDrag) {
33132             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33133         }
33134         else {
33135             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33136         }
33137         dd.setHandleElId(this.header.id);
33138         dd.endDrag = this.endMove.createDelegate(this);
33139         dd.startDrag = this.startMove.createDelegate(this);
33140         dd.onDrag = this.onDrag.createDelegate(this);
33141         dd.scroll = false;
33142         this.dd = dd;
33143     }
33144     if(this.modal){
33145         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33146         this.mask.enableDisplayMode("block");
33147         this.mask.hide();
33148         this.el.addClass("x-dlg-modal");
33149     }
33150     if(this.shadow){
33151         this.shadow = new Roo.Shadow({
33152             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33153             offset : this.shadowOffset
33154         });
33155     }else{
33156         this.shadowOffset = 0;
33157     }
33158     if(Roo.useShims && this.shim !== false){
33159         this.shim = this.el.createShim();
33160         this.shim.hide = this.hideAction;
33161         this.shim.hide();
33162     }else{
33163         this.shim = false;
33164     }
33165     if(this.autoTabs){
33166         this.initTabs();
33167     }
33168     if (this.buttons) { 
33169         var bts= this.buttons;
33170         this.buttons = [];
33171         Roo.each(bts, function(b) {
33172             this.addButton(b);
33173         }, this);
33174     }
33175     
33176     
33177     this.addEvents({
33178         /**
33179          * @event keydown
33180          * Fires when a key is pressed
33181          * @param {Roo.BasicDialog} this
33182          * @param {Roo.EventObject} e
33183          */
33184         "keydown" : true,
33185         /**
33186          * @event move
33187          * Fires when this dialog is moved by the user.
33188          * @param {Roo.BasicDialog} this
33189          * @param {Number} x The new page X
33190          * @param {Number} y The new page Y
33191          */
33192         "move" : true,
33193         /**
33194          * @event resize
33195          * Fires when this dialog is resized by the user.
33196          * @param {Roo.BasicDialog} this
33197          * @param {Number} width The new width
33198          * @param {Number} height The new height
33199          */
33200         "resize" : true,
33201         /**
33202          * @event beforehide
33203          * Fires before this dialog is hidden.
33204          * @param {Roo.BasicDialog} this
33205          */
33206         "beforehide" : true,
33207         /**
33208          * @event hide
33209          * Fires when this dialog is hidden.
33210          * @param {Roo.BasicDialog} this
33211          */
33212         "hide" : true,
33213         /**
33214          * @event beforeshow
33215          * Fires before this dialog is shown.
33216          * @param {Roo.BasicDialog} this
33217          */
33218         "beforeshow" : true,
33219         /**
33220          * @event show
33221          * Fires when this dialog is shown.
33222          * @param {Roo.BasicDialog} this
33223          */
33224         "show" : true
33225     });
33226     el.on("keydown", this.onKeyDown, this);
33227     el.on("mousedown", this.toFront, this);
33228     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33229     this.el.hide();
33230     Roo.DialogManager.register(this);
33231     Roo.BasicDialog.superclass.constructor.call(this);
33232 };
33233
33234 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33235     shadowOffset: Roo.isIE ? 6 : 5,
33236     minHeight: 80,
33237     minWidth: 200,
33238     minButtonWidth: 75,
33239     defaultButton: null,
33240     buttonAlign: "right",
33241     tabTag: 'div',
33242     firstShow: true,
33243
33244     /**
33245      * Sets the dialog title text
33246      * @param {String} text The title text to display
33247      * @return {Roo.BasicDialog} this
33248      */
33249     setTitle : function(text){
33250         this.header.update(text);
33251         return this;
33252     },
33253
33254     // private
33255     closeClick : function(){
33256         this.hide();
33257     },
33258
33259     // private
33260     collapseClick : function(){
33261         this[this.collapsed ? "expand" : "collapse"]();
33262     },
33263
33264     /**
33265      * Collapses the dialog to its minimized state (only the title bar is visible).
33266      * Equivalent to the user clicking the collapse dialog button.
33267      */
33268     collapse : function(){
33269         if(!this.collapsed){
33270             this.collapsed = true;
33271             this.el.addClass("x-dlg-collapsed");
33272             this.restoreHeight = this.el.getHeight();
33273             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33274         }
33275     },
33276
33277     /**
33278      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33279      * clicking the expand dialog button.
33280      */
33281     expand : function(){
33282         if(this.collapsed){
33283             this.collapsed = false;
33284             this.el.removeClass("x-dlg-collapsed");
33285             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33286         }
33287     },
33288
33289     /**
33290      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33291      * @return {Roo.TabPanel} The tabs component
33292      */
33293     initTabs : function(){
33294         var tabs = this.getTabs();
33295         while(tabs.getTab(0)){
33296             tabs.removeTab(0);
33297         }
33298         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33299             var dom = el.dom;
33300             tabs.addTab(Roo.id(dom), dom.title);
33301             dom.title = "";
33302         });
33303         tabs.activate(0);
33304         return tabs;
33305     },
33306
33307     // private
33308     beforeResize : function(){
33309         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33310     },
33311
33312     // private
33313     onResize : function(){
33314         this.refreshSize();
33315         this.syncBodyHeight();
33316         this.adjustAssets();
33317         this.focus();
33318         this.fireEvent("resize", this, this.size.width, this.size.height);
33319     },
33320
33321     // private
33322     onKeyDown : function(e){
33323         if(this.isVisible()){
33324             this.fireEvent("keydown", this, e);
33325         }
33326     },
33327
33328     /**
33329      * Resizes the dialog.
33330      * @param {Number} width
33331      * @param {Number} height
33332      * @return {Roo.BasicDialog} this
33333      */
33334     resizeTo : function(width, height){
33335         this.el.setSize(width, height);
33336         this.size = {width: width, height: height};
33337         this.syncBodyHeight();
33338         if(this.fixedcenter){
33339             this.center();
33340         }
33341         if(this.isVisible()){
33342             this.constrainXY();
33343             this.adjustAssets();
33344         }
33345         this.fireEvent("resize", this, width, height);
33346         return this;
33347     },
33348
33349
33350     /**
33351      * Resizes the dialog to fit the specified content size.
33352      * @param {Number} width
33353      * @param {Number} height
33354      * @return {Roo.BasicDialog} this
33355      */
33356     setContentSize : function(w, h){
33357         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33358         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33359         //if(!this.el.isBorderBox()){
33360             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33361             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33362         //}
33363         if(this.tabs){
33364             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33365             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33366         }
33367         this.resizeTo(w, h);
33368         return this;
33369     },
33370
33371     /**
33372      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33373      * executed in response to a particular key being pressed while the dialog is active.
33374      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33375      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33376      * @param {Function} fn The function to call
33377      * @param {Object} scope (optional) The scope of the function
33378      * @return {Roo.BasicDialog} this
33379      */
33380     addKeyListener : function(key, fn, scope){
33381         var keyCode, shift, ctrl, alt;
33382         if(typeof key == "object" && !(key instanceof Array)){
33383             keyCode = key["key"];
33384             shift = key["shift"];
33385             ctrl = key["ctrl"];
33386             alt = key["alt"];
33387         }else{
33388             keyCode = key;
33389         }
33390         var handler = function(dlg, e){
33391             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33392                 var k = e.getKey();
33393                 if(keyCode instanceof Array){
33394                     for(var i = 0, len = keyCode.length; i < len; i++){
33395                         if(keyCode[i] == k){
33396                           fn.call(scope || window, dlg, k, e);
33397                           return;
33398                         }
33399                     }
33400                 }else{
33401                     if(k == keyCode){
33402                         fn.call(scope || window, dlg, k, e);
33403                     }
33404                 }
33405             }
33406         };
33407         this.on("keydown", handler);
33408         return this;
33409     },
33410
33411     /**
33412      * Returns the TabPanel component (creates it if it doesn't exist).
33413      * Note: If you wish to simply check for the existence of tabs without creating them,
33414      * check for a null 'tabs' property.
33415      * @return {Roo.TabPanel} The tabs component
33416      */
33417     getTabs : function(){
33418         if(!this.tabs){
33419             this.el.addClass("x-dlg-auto-tabs");
33420             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33421             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33422         }
33423         return this.tabs;
33424     },
33425
33426     /**
33427      * Adds a button to the footer section of the dialog.
33428      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33429      * object or a valid Roo.DomHelper element config
33430      * @param {Function} handler The function called when the button is clicked
33431      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33432      * @return {Roo.Button} The new button
33433      */
33434     addButton : function(config, handler, scope){
33435         var dh = Roo.DomHelper;
33436         if(!this.footer){
33437             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33438         }
33439         if(!this.btnContainer){
33440             var tb = this.footer.createChild({
33441
33442                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33443                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33444             }, null, true);
33445             this.btnContainer = tb.firstChild.firstChild.firstChild;
33446         }
33447         var bconfig = {
33448             handler: handler,
33449             scope: scope,
33450             minWidth: this.minButtonWidth,
33451             hideParent:true
33452         };
33453         if(typeof config == "string"){
33454             bconfig.text = config;
33455         }else{
33456             if(config.tag){
33457                 bconfig.dhconfig = config;
33458             }else{
33459                 Roo.apply(bconfig, config);
33460             }
33461         }
33462         var fc = false;
33463         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33464             bconfig.position = Math.max(0, bconfig.position);
33465             fc = this.btnContainer.childNodes[bconfig.position];
33466         }
33467          
33468         var btn = new Roo.Button(
33469             fc ? 
33470                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33471                 : this.btnContainer.appendChild(document.createElement("td")),
33472             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33473             bconfig
33474         );
33475         this.syncBodyHeight();
33476         if(!this.buttons){
33477             /**
33478              * Array of all the buttons that have been added to this dialog via addButton
33479              * @type Array
33480              */
33481             this.buttons = [];
33482         }
33483         this.buttons.push(btn);
33484         return btn;
33485     },
33486
33487     /**
33488      * Sets the default button to be focused when the dialog is displayed.
33489      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33490      * @return {Roo.BasicDialog} this
33491      */
33492     setDefaultButton : function(btn){
33493         this.defaultButton = btn;
33494         return this;
33495     },
33496
33497     // private
33498     getHeaderFooterHeight : function(safe){
33499         var height = 0;
33500         if(this.header){
33501            height += this.header.getHeight();
33502         }
33503         if(this.footer){
33504            var fm = this.footer.getMargins();
33505             height += (this.footer.getHeight()+fm.top+fm.bottom);
33506         }
33507         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33508         height += this.centerBg.getPadding("tb");
33509         return height;
33510     },
33511
33512     // private
33513     syncBodyHeight : function()
33514     {
33515         var bd = this.body, // the text
33516             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33517             bw = this.bwrap;
33518         var height = this.size.height - this.getHeaderFooterHeight(false);
33519         bd.setHeight(height-bd.getMargins("tb"));
33520         var hh = this.header.getHeight();
33521         var h = this.size.height-hh;
33522         cb.setHeight(h);
33523         
33524         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33525         bw.setHeight(h-cb.getPadding("tb"));
33526         
33527         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33528         bd.setWidth(bw.getWidth(true));
33529         if(this.tabs){
33530             this.tabs.syncHeight();
33531             if(Roo.isIE){
33532                 this.tabs.el.repaint();
33533             }
33534         }
33535     },
33536
33537     /**
33538      * Restores the previous state of the dialog if Roo.state is configured.
33539      * @return {Roo.BasicDialog} this
33540      */
33541     restoreState : function(){
33542         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33543         if(box && box.width){
33544             this.xy = [box.x, box.y];
33545             this.resizeTo(box.width, box.height);
33546         }
33547         return this;
33548     },
33549
33550     // private
33551     beforeShow : function(){
33552         this.expand();
33553         if(this.fixedcenter){
33554             this.xy = this.el.getCenterXY(true);
33555         }
33556         if(this.modal){
33557             Roo.get(document.body).addClass("x-body-masked");
33558             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33559             this.mask.show();
33560         }
33561         this.constrainXY();
33562     },
33563
33564     // private
33565     animShow : function(){
33566         var b = Roo.get(this.animateTarget).getBox();
33567         this.proxy.setSize(b.width, b.height);
33568         this.proxy.setLocation(b.x, b.y);
33569         this.proxy.show();
33570         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33571                     true, .35, this.showEl.createDelegate(this));
33572     },
33573
33574     /**
33575      * Shows the dialog.
33576      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33577      * @return {Roo.BasicDialog} this
33578      */
33579     show : function(animateTarget){
33580         if (this.fireEvent("beforeshow", this) === false){
33581             return;
33582         }
33583         if(this.syncHeightBeforeShow){
33584             this.syncBodyHeight();
33585         }else if(this.firstShow){
33586             this.firstShow = false;
33587             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33588         }
33589         this.animateTarget = animateTarget || this.animateTarget;
33590         if(!this.el.isVisible()){
33591             this.beforeShow();
33592             if(this.animateTarget && Roo.get(this.animateTarget)){
33593                 this.animShow();
33594             }else{
33595                 this.showEl();
33596             }
33597         }
33598         return this;
33599     },
33600
33601     // private
33602     showEl : function(){
33603         this.proxy.hide();
33604         this.el.setXY(this.xy);
33605         this.el.show();
33606         this.adjustAssets(true);
33607         this.toFront();
33608         this.focus();
33609         // IE peekaboo bug - fix found by Dave Fenwick
33610         if(Roo.isIE){
33611             this.el.repaint();
33612         }
33613         this.fireEvent("show", this);
33614     },
33615
33616     /**
33617      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33618      * dialog itself will receive focus.
33619      */
33620     focus : function(){
33621         if(this.defaultButton){
33622             this.defaultButton.focus();
33623         }else{
33624             this.focusEl.focus();
33625         }
33626     },
33627
33628     // private
33629     constrainXY : function(){
33630         if(this.constraintoviewport !== false){
33631             if(!this.viewSize){
33632                 if(this.container){
33633                     var s = this.container.getSize();
33634                     this.viewSize = [s.width, s.height];
33635                 }else{
33636                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33637                 }
33638             }
33639             var s = Roo.get(this.container||document).getScroll();
33640
33641             var x = this.xy[0], y = this.xy[1];
33642             var w = this.size.width, h = this.size.height;
33643             var vw = this.viewSize[0], vh = this.viewSize[1];
33644             // only move it if it needs it
33645             var moved = false;
33646             // first validate right/bottom
33647             if(x + w > vw+s.left){
33648                 x = vw - w;
33649                 moved = true;
33650             }
33651             if(y + h > vh+s.top){
33652                 y = vh - h;
33653                 moved = true;
33654             }
33655             // then make sure top/left isn't negative
33656             if(x < s.left){
33657                 x = s.left;
33658                 moved = true;
33659             }
33660             if(y < s.top){
33661                 y = s.top;
33662                 moved = true;
33663             }
33664             if(moved){
33665                 // cache xy
33666                 this.xy = [x, y];
33667                 if(this.isVisible()){
33668                     this.el.setLocation(x, y);
33669                     this.adjustAssets();
33670                 }
33671             }
33672         }
33673     },
33674
33675     // private
33676     onDrag : function(){
33677         if(!this.proxyDrag){
33678             this.xy = this.el.getXY();
33679             this.adjustAssets();
33680         }
33681     },
33682
33683     // private
33684     adjustAssets : function(doShow){
33685         var x = this.xy[0], y = this.xy[1];
33686         var w = this.size.width, h = this.size.height;
33687         if(doShow === true){
33688             if(this.shadow){
33689                 this.shadow.show(this.el);
33690             }
33691             if(this.shim){
33692                 this.shim.show();
33693             }
33694         }
33695         if(this.shadow && this.shadow.isVisible()){
33696             this.shadow.show(this.el);
33697         }
33698         if(this.shim && this.shim.isVisible()){
33699             this.shim.setBounds(x, y, w, h);
33700         }
33701     },
33702
33703     // private
33704     adjustViewport : function(w, h){
33705         if(!w || !h){
33706             w = Roo.lib.Dom.getViewWidth();
33707             h = Roo.lib.Dom.getViewHeight();
33708         }
33709         // cache the size
33710         this.viewSize = [w, h];
33711         if(this.modal && this.mask.isVisible()){
33712             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33713             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33714         }
33715         if(this.isVisible()){
33716             this.constrainXY();
33717         }
33718     },
33719
33720     /**
33721      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33722      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33723      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33724      */
33725     destroy : function(removeEl){
33726         if(this.isVisible()){
33727             this.animateTarget = null;
33728             this.hide();
33729         }
33730         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33731         if(this.tabs){
33732             this.tabs.destroy(removeEl);
33733         }
33734         Roo.destroy(
33735              this.shim,
33736              this.proxy,
33737              this.resizer,
33738              this.close,
33739              this.mask
33740         );
33741         if(this.dd){
33742             this.dd.unreg();
33743         }
33744         if(this.buttons){
33745            for(var i = 0, len = this.buttons.length; i < len; i++){
33746                this.buttons[i].destroy();
33747            }
33748         }
33749         this.el.removeAllListeners();
33750         if(removeEl === true){
33751             this.el.update("");
33752             this.el.remove();
33753         }
33754         Roo.DialogManager.unregister(this);
33755     },
33756
33757     // private
33758     startMove : function(){
33759         if(this.proxyDrag){
33760             this.proxy.show();
33761         }
33762         if(this.constraintoviewport !== false){
33763             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33764         }
33765     },
33766
33767     // private
33768     endMove : function(){
33769         if(!this.proxyDrag){
33770             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33771         }else{
33772             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33773             this.proxy.hide();
33774         }
33775         this.refreshSize();
33776         this.adjustAssets();
33777         this.focus();
33778         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33779     },
33780
33781     /**
33782      * Brings this dialog to the front of any other visible dialogs
33783      * @return {Roo.BasicDialog} this
33784      */
33785     toFront : function(){
33786         Roo.DialogManager.bringToFront(this);
33787         return this;
33788     },
33789
33790     /**
33791      * Sends this dialog to the back (under) of any other visible dialogs
33792      * @return {Roo.BasicDialog} this
33793      */
33794     toBack : function(){
33795         Roo.DialogManager.sendToBack(this);
33796         return this;
33797     },
33798
33799     /**
33800      * Centers this dialog in the viewport
33801      * @return {Roo.BasicDialog} this
33802      */
33803     center : function(){
33804         var xy = this.el.getCenterXY(true);
33805         this.moveTo(xy[0], xy[1]);
33806         return this;
33807     },
33808
33809     /**
33810      * Moves the dialog's top-left corner to the specified point
33811      * @param {Number} x
33812      * @param {Number} y
33813      * @return {Roo.BasicDialog} this
33814      */
33815     moveTo : function(x, y){
33816         this.xy = [x,y];
33817         if(this.isVisible()){
33818             this.el.setXY(this.xy);
33819             this.adjustAssets();
33820         }
33821         return this;
33822     },
33823
33824     /**
33825      * Aligns the dialog to the specified element
33826      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33827      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33828      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33829      * @return {Roo.BasicDialog} this
33830      */
33831     alignTo : function(element, position, offsets){
33832         this.xy = this.el.getAlignToXY(element, position, offsets);
33833         if(this.isVisible()){
33834             this.el.setXY(this.xy);
33835             this.adjustAssets();
33836         }
33837         return this;
33838     },
33839
33840     /**
33841      * Anchors an element to another element and realigns it when the window is resized.
33842      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33843      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33844      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33845      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33846      * is a number, it is used as the buffer delay (defaults to 50ms).
33847      * @return {Roo.BasicDialog} this
33848      */
33849     anchorTo : function(el, alignment, offsets, monitorScroll){
33850         var action = function(){
33851             this.alignTo(el, alignment, offsets);
33852         };
33853         Roo.EventManager.onWindowResize(action, this);
33854         var tm = typeof monitorScroll;
33855         if(tm != 'undefined'){
33856             Roo.EventManager.on(window, 'scroll', action, this,
33857                 {buffer: tm == 'number' ? monitorScroll : 50});
33858         }
33859         action.call(this);
33860         return this;
33861     },
33862
33863     /**
33864      * Returns true if the dialog is visible
33865      * @return {Boolean}
33866      */
33867     isVisible : function(){
33868         return this.el.isVisible();
33869     },
33870
33871     // private
33872     animHide : function(callback){
33873         var b = Roo.get(this.animateTarget).getBox();
33874         this.proxy.show();
33875         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33876         this.el.hide();
33877         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33878                     this.hideEl.createDelegate(this, [callback]));
33879     },
33880
33881     /**
33882      * Hides the dialog.
33883      * @param {Function} callback (optional) Function to call when the dialog is hidden
33884      * @return {Roo.BasicDialog} this
33885      */
33886     hide : function(callback){
33887         if (this.fireEvent("beforehide", this) === false){
33888             return;
33889         }
33890         if(this.shadow){
33891             this.shadow.hide();
33892         }
33893         if(this.shim) {
33894           this.shim.hide();
33895         }
33896         // sometimes animateTarget seems to get set.. causing problems...
33897         // this just double checks..
33898         if(this.animateTarget && Roo.get(this.animateTarget)) {
33899            this.animHide(callback);
33900         }else{
33901             this.el.hide();
33902             this.hideEl(callback);
33903         }
33904         return this;
33905     },
33906
33907     // private
33908     hideEl : function(callback){
33909         this.proxy.hide();
33910         if(this.modal){
33911             this.mask.hide();
33912             Roo.get(document.body).removeClass("x-body-masked");
33913         }
33914         this.fireEvent("hide", this);
33915         if(typeof callback == "function"){
33916             callback();
33917         }
33918     },
33919
33920     // private
33921     hideAction : function(){
33922         this.setLeft("-10000px");
33923         this.setTop("-10000px");
33924         this.setStyle("visibility", "hidden");
33925     },
33926
33927     // private
33928     refreshSize : function(){
33929         this.size = this.el.getSize();
33930         this.xy = this.el.getXY();
33931         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33932     },
33933
33934     // private
33935     // z-index is managed by the DialogManager and may be overwritten at any time
33936     setZIndex : function(index){
33937         if(this.modal){
33938             this.mask.setStyle("z-index", index);
33939         }
33940         if(this.shim){
33941             this.shim.setStyle("z-index", ++index);
33942         }
33943         if(this.shadow){
33944             this.shadow.setZIndex(++index);
33945         }
33946         this.el.setStyle("z-index", ++index);
33947         if(this.proxy){
33948             this.proxy.setStyle("z-index", ++index);
33949         }
33950         if(this.resizer){
33951             this.resizer.proxy.setStyle("z-index", ++index);
33952         }
33953
33954         this.lastZIndex = index;
33955     },
33956
33957     /**
33958      * Returns the element for this dialog
33959      * @return {Roo.Element} The underlying dialog Element
33960      */
33961     getEl : function(){
33962         return this.el;
33963     }
33964 });
33965
33966 /**
33967  * @class Roo.DialogManager
33968  * Provides global access to BasicDialogs that have been created and
33969  * support for z-indexing (layering) multiple open dialogs.
33970  */
33971 Roo.DialogManager = function(){
33972     var list = {};
33973     var accessList = [];
33974     var front = null;
33975
33976     // private
33977     var sortDialogs = function(d1, d2){
33978         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33979     };
33980
33981     // private
33982     var orderDialogs = function(){
33983         accessList.sort(sortDialogs);
33984         var seed = Roo.DialogManager.zseed;
33985         for(var i = 0, len = accessList.length; i < len; i++){
33986             var dlg = accessList[i];
33987             if(dlg){
33988                 dlg.setZIndex(seed + (i*10));
33989             }
33990         }
33991     };
33992
33993     return {
33994         /**
33995          * The starting z-index for BasicDialogs (defaults to 9000)
33996          * @type Number The z-index value
33997          */
33998         zseed : 9000,
33999
34000         // private
34001         register : function(dlg){
34002             list[dlg.id] = dlg;
34003             accessList.push(dlg);
34004         },
34005
34006         // private
34007         unregister : function(dlg){
34008             delete list[dlg.id];
34009             var i=0;
34010             var len=0;
34011             if(!accessList.indexOf){
34012                 for(  i = 0, len = accessList.length; i < len; i++){
34013                     if(accessList[i] == dlg){
34014                         accessList.splice(i, 1);
34015                         return;
34016                     }
34017                 }
34018             }else{
34019                  i = accessList.indexOf(dlg);
34020                 if(i != -1){
34021                     accessList.splice(i, 1);
34022                 }
34023             }
34024         },
34025
34026         /**
34027          * Gets a registered dialog by id
34028          * @param {String/Object} id The id of the dialog or a dialog
34029          * @return {Roo.BasicDialog} this
34030          */
34031         get : function(id){
34032             return typeof id == "object" ? id : list[id];
34033         },
34034
34035         /**
34036          * Brings the specified dialog to the front
34037          * @param {String/Object} dlg The id of the dialog or a dialog
34038          * @return {Roo.BasicDialog} this
34039          */
34040         bringToFront : function(dlg){
34041             dlg = this.get(dlg);
34042             if(dlg != front){
34043                 front = dlg;
34044                 dlg._lastAccess = new Date().getTime();
34045                 orderDialogs();
34046             }
34047             return dlg;
34048         },
34049
34050         /**
34051          * Sends the specified dialog to the back
34052          * @param {String/Object} dlg The id of the dialog or a dialog
34053          * @return {Roo.BasicDialog} this
34054          */
34055         sendToBack : function(dlg){
34056             dlg = this.get(dlg);
34057             dlg._lastAccess = -(new Date().getTime());
34058             orderDialogs();
34059             return dlg;
34060         },
34061
34062         /**
34063          * Hides all dialogs
34064          */
34065         hideAll : function(){
34066             for(var id in list){
34067                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34068                     list[id].hide();
34069                 }
34070             }
34071         }
34072     };
34073 }();
34074
34075 /**
34076  * @class Roo.LayoutDialog
34077  * @extends Roo.BasicDialog
34078  * @children Roo.ContentPanel
34079  * @parent builder none
34080  * Dialog which provides adjustments for working with a layout in a Dialog.
34081  * Add your necessary layout config options to the dialog's config.<br>
34082  * Example usage (including a nested layout):
34083  * <pre><code>
34084 if(!dialog){
34085     dialog = new Roo.LayoutDialog("download-dlg", {
34086         modal: true,
34087         width:600,
34088         height:450,
34089         shadow:true,
34090         minWidth:500,
34091         minHeight:350,
34092         autoTabs:true,
34093         proxyDrag:true,
34094         // layout config merges with the dialog config
34095         center:{
34096             tabPosition: "top",
34097             alwaysShowTabs: true
34098         }
34099     });
34100     dialog.addKeyListener(27, dialog.hide, dialog);
34101     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34102     dialog.addButton("Build It!", this.getDownload, this);
34103
34104     // we can even add nested layouts
34105     var innerLayout = new Roo.BorderLayout("dl-inner", {
34106         east: {
34107             initialSize: 200,
34108             autoScroll:true,
34109             split:true
34110         },
34111         center: {
34112             autoScroll:true
34113         }
34114     });
34115     innerLayout.beginUpdate();
34116     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34117     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34118     innerLayout.endUpdate(true);
34119
34120     var layout = dialog.getLayout();
34121     layout.beginUpdate();
34122     layout.add("center", new Roo.ContentPanel("standard-panel",
34123                         {title: "Download the Source", fitToFrame:true}));
34124     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34125                {title: "Build your own roo.js"}));
34126     layout.getRegion("center").showPanel(sp);
34127     layout.endUpdate();
34128 }
34129 </code></pre>
34130     * @constructor
34131     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34132     * @param {Object} config configuration options
34133   */
34134 Roo.LayoutDialog = function(el, cfg){
34135     
34136     var config=  cfg;
34137     if (typeof(cfg) == 'undefined') {
34138         config = Roo.apply({}, el);
34139         // not sure why we use documentElement here.. - it should always be body.
34140         // IE7 borks horribly if we use documentElement.
34141         // webkit also does not like documentElement - it creates a body element...
34142         el = Roo.get( document.body || document.documentElement ).createChild();
34143         //config.autoCreate = true;
34144     }
34145     
34146     
34147     config.autoTabs = false;
34148     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34149     this.body.setStyle({overflow:"hidden", position:"relative"});
34150     this.layout = new Roo.BorderLayout(this.body.dom, config);
34151     this.layout.monitorWindowResize = false;
34152     this.el.addClass("x-dlg-auto-layout");
34153     // fix case when center region overwrites center function
34154     this.center = Roo.BasicDialog.prototype.center;
34155     this.on("show", this.layout.layout, this.layout, true);
34156     if (config.items) {
34157         var xitems = config.items;
34158         delete config.items;
34159         Roo.each(xitems, this.addxtype, this);
34160     }
34161     
34162     
34163 };
34164 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34165     
34166     
34167     /**
34168      * @cfg {Roo.LayoutRegion} east  
34169      */
34170     /**
34171      * @cfg {Roo.LayoutRegion} west
34172      */
34173     /**
34174      * @cfg {Roo.LayoutRegion} south
34175      */
34176     /**
34177      * @cfg {Roo.LayoutRegion} north
34178      */
34179     /**
34180      * @cfg {Roo.LayoutRegion} center
34181      */
34182     /**
34183      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34184      */
34185     
34186     
34187     /**
34188      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34189      * @deprecated
34190      */
34191     endUpdate : function(){
34192         this.layout.endUpdate();
34193     },
34194
34195     /**
34196      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34197      *  @deprecated
34198      */
34199     beginUpdate : function(){
34200         this.layout.beginUpdate();
34201     },
34202
34203     /**
34204      * Get the BorderLayout for this dialog
34205      * @return {Roo.BorderLayout}
34206      */
34207     getLayout : function(){
34208         return this.layout;
34209     },
34210
34211     showEl : function(){
34212         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34213         if(Roo.isIE7){
34214             this.layout.layout();
34215         }
34216     },
34217
34218     // private
34219     // Use the syncHeightBeforeShow config option to control this automatically
34220     syncBodyHeight : function(){
34221         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34222         if(this.layout){this.layout.layout();}
34223     },
34224     
34225       /**
34226      * Add an xtype element (actually adds to the layout.)
34227      * @return {Object} xdata xtype object data.
34228      */
34229     
34230     addxtype : function(c) {
34231         return this.layout.addxtype(c);
34232     }
34233 });/*
34234  * Based on:
34235  * Ext JS Library 1.1.1
34236  * Copyright(c) 2006-2007, Ext JS, LLC.
34237  *
34238  * Originally Released Under LGPL - original licence link has changed is not relivant.
34239  *
34240  * Fork - LGPL
34241  * <script type="text/javascript">
34242  */
34243  
34244 /**
34245  * @class Roo.MessageBox
34246  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34247  * Example usage:
34248  *<pre><code>
34249 // Basic alert:
34250 Roo.Msg.alert('Status', 'Changes saved successfully.');
34251
34252 // Prompt for user data:
34253 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34254     if (btn == 'ok'){
34255         // process text value...
34256     }
34257 });
34258
34259 // Show a dialog using config options:
34260 Roo.Msg.show({
34261    title:'Save Changes?',
34262    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34263    buttons: Roo.Msg.YESNOCANCEL,
34264    fn: processResult,
34265    animEl: 'elId'
34266 });
34267 </code></pre>
34268  * @static
34269  */
34270 Roo.MessageBox = function(){
34271     var dlg, opt, mask, waitTimer;
34272     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34273     var buttons, activeTextEl, bwidth;
34274
34275     // private
34276     var handleButton = function(button){
34277         dlg.hide();
34278         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34279     };
34280
34281     // private
34282     var handleHide = function(){
34283         if(opt && opt.cls){
34284             dlg.el.removeClass(opt.cls);
34285         }
34286         if(waitTimer){
34287             Roo.TaskMgr.stop(waitTimer);
34288             waitTimer = null;
34289         }
34290     };
34291
34292     // private
34293     var updateButtons = function(b){
34294         var width = 0;
34295         if(!b){
34296             buttons["ok"].hide();
34297             buttons["cancel"].hide();
34298             buttons["yes"].hide();
34299             buttons["no"].hide();
34300             dlg.footer.dom.style.display = 'none';
34301             return width;
34302         }
34303         dlg.footer.dom.style.display = '';
34304         for(var k in buttons){
34305             if(typeof buttons[k] != "function"){
34306                 if(b[k]){
34307                     buttons[k].show();
34308                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34309                     width += buttons[k].el.getWidth()+15;
34310                 }else{
34311                     buttons[k].hide();
34312                 }
34313             }
34314         }
34315         return width;
34316     };
34317
34318     // private
34319     var handleEsc = function(d, k, e){
34320         if(opt && opt.closable !== false){
34321             dlg.hide();
34322         }
34323         if(e){
34324             e.stopEvent();
34325         }
34326     };
34327
34328     return {
34329         /**
34330          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34331          * @return {Roo.BasicDialog} The BasicDialog element
34332          */
34333         getDialog : function(){
34334            if(!dlg){
34335                 dlg = new Roo.BasicDialog("x-msg-box", {
34336                     autoCreate : true,
34337                     shadow: true,
34338                     draggable: true,
34339                     resizable:false,
34340                     constraintoviewport:false,
34341                     fixedcenter:true,
34342                     collapsible : false,
34343                     shim:true,
34344                     modal: true,
34345                     width:400, height:100,
34346                     buttonAlign:"center",
34347                     closeClick : function(){
34348                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34349                             handleButton("no");
34350                         }else{
34351                             handleButton("cancel");
34352                         }
34353                     }
34354                 });
34355                 dlg.on("hide", handleHide);
34356                 mask = dlg.mask;
34357                 dlg.addKeyListener(27, handleEsc);
34358                 buttons = {};
34359                 var bt = this.buttonText;
34360                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34361                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34362                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34363                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34364                 bodyEl = dlg.body.createChild({
34365
34366                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
34367                 });
34368                 msgEl = bodyEl.dom.firstChild;
34369                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34370                 textboxEl.enableDisplayMode();
34371                 textboxEl.addKeyListener([10,13], function(){
34372                     if(dlg.isVisible() && opt && opt.buttons){
34373                         if(opt.buttons.ok){
34374                             handleButton("ok");
34375                         }else if(opt.buttons.yes){
34376                             handleButton("yes");
34377                         }
34378                     }
34379                 });
34380                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34381                 textareaEl.enableDisplayMode();
34382                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34383                 progressEl.enableDisplayMode();
34384                 var pf = progressEl.dom.firstChild;
34385                 if (pf) {
34386                     pp = Roo.get(pf.firstChild);
34387                     pp.setHeight(pf.offsetHeight);
34388                 }
34389                 
34390             }
34391             return dlg;
34392         },
34393
34394         /**
34395          * Updates the message box body text
34396          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34397          * the XHTML-compliant non-breaking space character '&amp;#160;')
34398          * @return {Roo.MessageBox} This message box
34399          */
34400         updateText : function(text){
34401             if(!dlg.isVisible() && !opt.width){
34402                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34403             }
34404             msgEl.innerHTML = text || '&#160;';
34405       
34406             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34407             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34408             var w = Math.max(
34409                     Math.min(opt.width || cw , this.maxWidth), 
34410                     Math.max(opt.minWidth || this.minWidth, bwidth)
34411             );
34412             if(opt.prompt){
34413                 activeTextEl.setWidth(w);
34414             }
34415             if(dlg.isVisible()){
34416                 dlg.fixedcenter = false;
34417             }
34418             // to big, make it scroll. = But as usual stupid IE does not support
34419             // !important..
34420             
34421             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34422                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34423                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34424             } else {
34425                 bodyEl.dom.style.height = '';
34426                 bodyEl.dom.style.overflowY = '';
34427             }
34428             if (cw > w) {
34429                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34430             } else {
34431                 bodyEl.dom.style.overflowX = '';
34432             }
34433             
34434             dlg.setContentSize(w, bodyEl.getHeight());
34435             if(dlg.isVisible()){
34436                 dlg.fixedcenter = true;
34437             }
34438             return this;
34439         },
34440
34441         /**
34442          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34443          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34444          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34445          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34446          * @return {Roo.MessageBox} This message box
34447          */
34448         updateProgress : function(value, text){
34449             if(text){
34450                 this.updateText(text);
34451             }
34452             if (pp) { // weird bug on my firefox - for some reason this is not defined
34453                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34454             }
34455             return this;
34456         },        
34457
34458         /**
34459          * Returns true if the message box is currently displayed
34460          * @return {Boolean} True if the message box is visible, else false
34461          */
34462         isVisible : function(){
34463             return dlg && dlg.isVisible();  
34464         },
34465
34466         /**
34467          * Hides the message box if it is displayed
34468          */
34469         hide : function(){
34470             if(this.isVisible()){
34471                 dlg.hide();
34472             }  
34473         },
34474
34475         /**
34476          * Displays a new message box, or reinitializes an existing message box, based on the config options
34477          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34478          * The following config object properties are supported:
34479          * <pre>
34480 Property    Type             Description
34481 ----------  ---------------  ------------------------------------------------------------------------------------
34482 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34483                                    closes (defaults to undefined)
34484 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34485                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34486 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34487                                    progress and wait dialogs will ignore this property and always hide the
34488                                    close button as they can only be closed programmatically.
34489 cls               String           A custom CSS class to apply to the message box element
34490 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34491                                    displayed (defaults to 75)
34492 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34493                                    function will be btn (the name of the button that was clicked, if applicable,
34494                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34495                                    Progress and wait dialogs will ignore this option since they do not respond to
34496                                    user actions and can only be closed programmatically, so any required function
34497                                    should be called by the same code after it closes the dialog.
34498 icon              String           A CSS class that provides a background image to be used as an icon for
34499                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34500 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34501 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34502 modal             Boolean          False to allow user interaction with the page while the message box is
34503                                    displayed (defaults to true)
34504 msg               String           A string that will replace the existing message box body text (defaults
34505                                    to the XHTML-compliant non-breaking space character '&#160;')
34506 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34507 progress          Boolean          True to display a progress bar (defaults to false)
34508 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34509 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34510 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34511 title             String           The title text
34512 value             String           The string value to set into the active textbox element if displayed
34513 wait              Boolean          True to display a progress bar (defaults to false)
34514 width             Number           The width of the dialog in pixels
34515 </pre>
34516          *
34517          * Example usage:
34518          * <pre><code>
34519 Roo.Msg.show({
34520    title: 'Address',
34521    msg: 'Please enter your address:',
34522    width: 300,
34523    buttons: Roo.MessageBox.OKCANCEL,
34524    multiline: true,
34525    fn: saveAddress,
34526    animEl: 'addAddressBtn'
34527 });
34528 </code></pre>
34529          * @param {Object} config Configuration options
34530          * @return {Roo.MessageBox} This message box
34531          */
34532         show : function(options)
34533         {
34534             
34535             // this causes nightmares if you show one dialog after another
34536             // especially on callbacks..
34537              
34538             if(this.isVisible()){
34539                 
34540                 this.hide();
34541                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34542                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34543                 Roo.log("New Dialog Message:" +  options.msg )
34544                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34545                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34546                 
34547             }
34548             var d = this.getDialog();
34549             opt = options;
34550             d.setTitle(opt.title || "&#160;");
34551             d.close.setDisplayed(opt.closable !== false);
34552             activeTextEl = textboxEl;
34553             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34554             if(opt.prompt){
34555                 if(opt.multiline){
34556                     textboxEl.hide();
34557                     textareaEl.show();
34558                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34559                         opt.multiline : this.defaultTextHeight);
34560                     activeTextEl = textareaEl;
34561                 }else{
34562                     textboxEl.show();
34563                     textareaEl.hide();
34564                 }
34565             }else{
34566                 textboxEl.hide();
34567                 textareaEl.hide();
34568             }
34569             progressEl.setDisplayed(opt.progress === true);
34570             this.updateProgress(0);
34571             activeTextEl.dom.value = opt.value || "";
34572             if(opt.prompt){
34573                 dlg.setDefaultButton(activeTextEl);
34574             }else{
34575                 var bs = opt.buttons;
34576                 var db = null;
34577                 if(bs && bs.ok){
34578                     db = buttons["ok"];
34579                 }else if(bs && bs.yes){
34580                     db = buttons["yes"];
34581                 }
34582                 dlg.setDefaultButton(db);
34583             }
34584             bwidth = updateButtons(opt.buttons);
34585             this.updateText(opt.msg);
34586             if(opt.cls){
34587                 d.el.addClass(opt.cls);
34588             }
34589             d.proxyDrag = opt.proxyDrag === true;
34590             d.modal = opt.modal !== false;
34591             d.mask = opt.modal !== false ? mask : false;
34592             if(!d.isVisible()){
34593                 // force it to the end of the z-index stack so it gets a cursor in FF
34594                 document.body.appendChild(dlg.el.dom);
34595                 d.animateTarget = null;
34596                 d.show(options.animEl);
34597             }
34598             return this;
34599         },
34600
34601         /**
34602          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34603          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34604          * and closing the message box when the process is complete.
34605          * @param {String} title The title bar text
34606          * @param {String} msg The message box body text
34607          * @return {Roo.MessageBox} This message box
34608          */
34609         progress : function(title, msg){
34610             this.show({
34611                 title : title,
34612                 msg : msg,
34613                 buttons: false,
34614                 progress:true,
34615                 closable:false,
34616                 minWidth: this.minProgressWidth,
34617                 modal : true
34618             });
34619             return this;
34620         },
34621
34622         /**
34623          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34624          * If a callback function is passed it will be called after the user clicks the button, and the
34625          * id of the button that was clicked will be passed as the only parameter to the callback
34626          * (could also be the top-right close button).
34627          * @param {String} title The title bar text
34628          * @param {String} msg The message box body text
34629          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34630          * @param {Object} scope (optional) The scope of the callback function
34631          * @return {Roo.MessageBox} This message box
34632          */
34633         alert : function(title, msg, fn, scope){
34634             this.show({
34635                 title : title,
34636                 msg : msg,
34637                 buttons: this.OK,
34638                 fn: fn,
34639                 scope : scope,
34640                 modal : true
34641             });
34642             return this;
34643         },
34644
34645         /**
34646          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34647          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34648          * You are responsible for closing the message box when the process is complete.
34649          * @param {String} msg The message box body text
34650          * @param {String} title (optional) The title bar text
34651          * @return {Roo.MessageBox} This message box
34652          */
34653         wait : function(msg, title){
34654             this.show({
34655                 title : title,
34656                 msg : msg,
34657                 buttons: false,
34658                 closable:false,
34659                 progress:true,
34660                 modal:true,
34661                 width:300,
34662                 wait:true
34663             });
34664             waitTimer = Roo.TaskMgr.start({
34665                 run: function(i){
34666                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34667                 },
34668                 interval: 1000
34669             });
34670             return this;
34671         },
34672
34673         /**
34674          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34675          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34676          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34677          * @param {String} title The title bar text
34678          * @param {String} msg The message box body text
34679          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34680          * @param {Object} scope (optional) The scope of the callback function
34681          * @return {Roo.MessageBox} This message box
34682          */
34683         confirm : function(title, msg, fn, scope){
34684             this.show({
34685                 title : title,
34686                 msg : msg,
34687                 buttons: this.YESNO,
34688                 fn: fn,
34689                 scope : scope,
34690                 modal : true
34691             });
34692             return this;
34693         },
34694
34695         /**
34696          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34697          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34698          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34699          * (could also be the top-right close button) and the text that was entered will be passed as the two
34700          * parameters to the callback.
34701          * @param {String} title The title bar text
34702          * @param {String} msg The message box body text
34703          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34704          * @param {Object} scope (optional) The scope of the callback function
34705          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34706          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34707          * @return {Roo.MessageBox} This message box
34708          */
34709         prompt : function(title, msg, fn, scope, multiline){
34710             this.show({
34711                 title : title,
34712                 msg : msg,
34713                 buttons: this.OKCANCEL,
34714                 fn: fn,
34715                 minWidth:250,
34716                 scope : scope,
34717                 prompt:true,
34718                 multiline: multiline,
34719                 modal : true
34720             });
34721             return this;
34722         },
34723
34724         /**
34725          * Button config that displays a single OK button
34726          * @type Object
34727          */
34728         OK : {ok:true},
34729         /**
34730          * Button config that displays Yes and No buttons
34731          * @type Object
34732          */
34733         YESNO : {yes:true, no:true},
34734         /**
34735          * Button config that displays OK and Cancel buttons
34736          * @type Object
34737          */
34738         OKCANCEL : {ok:true, cancel:true},
34739         /**
34740          * Button config that displays Yes, No and Cancel buttons
34741          * @type Object
34742          */
34743         YESNOCANCEL : {yes:true, no:true, cancel:true},
34744
34745         /**
34746          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34747          * @type Number
34748          */
34749         defaultTextHeight : 75,
34750         /**
34751          * The maximum width in pixels of the message box (defaults to 600)
34752          * @type Number
34753          */
34754         maxWidth : 600,
34755         /**
34756          * The minimum width in pixels of the message box (defaults to 100)
34757          * @type Number
34758          */
34759         minWidth : 100,
34760         /**
34761          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34762          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34763          * @type Number
34764          */
34765         minProgressWidth : 250,
34766         /**
34767          * An object containing the default button text strings that can be overriden for localized language support.
34768          * Supported properties are: ok, cancel, yes and no.
34769          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34770          * @type Object
34771          */
34772         buttonText : {
34773             ok : "OK",
34774             cancel : "Cancel",
34775             yes : "Yes",
34776             no : "No"
34777         }
34778     };
34779 }();
34780
34781 /**
34782  * Shorthand for {@link Roo.MessageBox}
34783  */
34784 Roo.Msg = Roo.MessageBox;/*
34785  * Based on:
34786  * Ext JS Library 1.1.1
34787  * Copyright(c) 2006-2007, Ext JS, LLC.
34788  *
34789  * Originally Released Under LGPL - original licence link has changed is not relivant.
34790  *
34791  * Fork - LGPL
34792  * <script type="text/javascript">
34793  */
34794 /**
34795  * @class Roo.QuickTips
34796  * Provides attractive and customizable tooltips for any element.
34797  * @static
34798  */
34799 Roo.QuickTips = function(){
34800     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34801     var ce, bd, xy, dd;
34802     var visible = false, disabled = true, inited = false;
34803     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34804     
34805     var onOver = function(e){
34806         if(disabled){
34807             return;
34808         }
34809         var t = e.getTarget();
34810         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34811             return;
34812         }
34813         if(ce && t == ce.el){
34814             clearTimeout(hideProc);
34815             return;
34816         }
34817         if(t && tagEls[t.id]){
34818             tagEls[t.id].el = t;
34819             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34820             return;
34821         }
34822         var ttp, et = Roo.fly(t);
34823         var ns = cfg.namespace;
34824         if(tm.interceptTitles && t.title){
34825             ttp = t.title;
34826             t.qtip = ttp;
34827             t.removeAttribute("title");
34828             e.preventDefault();
34829         }else{
34830             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34831         }
34832         if(ttp){
34833             showProc = show.defer(tm.showDelay, tm, [{
34834                 el: t, 
34835                 text: ttp.replace(/\\n/g,'<br/>'),
34836                 width: et.getAttributeNS(ns, cfg.width),
34837                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34838                 title: et.getAttributeNS(ns, cfg.title),
34839                     cls: et.getAttributeNS(ns, cfg.cls)
34840             }]);
34841         }
34842     };
34843     
34844     var onOut = function(e){
34845         clearTimeout(showProc);
34846         var t = e.getTarget();
34847         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34848             hideProc = setTimeout(hide, tm.hideDelay);
34849         }
34850     };
34851     
34852     var onMove = function(e){
34853         if(disabled){
34854             return;
34855         }
34856         xy = e.getXY();
34857         xy[1] += 18;
34858         if(tm.trackMouse && ce){
34859             el.setXY(xy);
34860         }
34861     };
34862     
34863     var onDown = function(e){
34864         clearTimeout(showProc);
34865         clearTimeout(hideProc);
34866         if(!e.within(el)){
34867             if(tm.hideOnClick){
34868                 hide();
34869                 tm.disable();
34870                 tm.enable.defer(100, tm);
34871             }
34872         }
34873     };
34874     
34875     var getPad = function(){
34876         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34877     };
34878
34879     var show = function(o){
34880         if(disabled){
34881             return;
34882         }
34883         clearTimeout(dismissProc);
34884         ce = o;
34885         if(removeCls){ // in case manually hidden
34886             el.removeClass(removeCls);
34887             removeCls = null;
34888         }
34889         if(ce.cls){
34890             el.addClass(ce.cls);
34891             removeCls = ce.cls;
34892         }
34893         if(ce.title){
34894             tipTitle.update(ce.title);
34895             tipTitle.show();
34896         }else{
34897             tipTitle.update('');
34898             tipTitle.hide();
34899         }
34900         el.dom.style.width  = tm.maxWidth+'px';
34901         //tipBody.dom.style.width = '';
34902         tipBodyText.update(o.text);
34903         var p = getPad(), w = ce.width;
34904         if(!w){
34905             var td = tipBodyText.dom;
34906             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34907             if(aw > tm.maxWidth){
34908                 w = tm.maxWidth;
34909             }else if(aw < tm.minWidth){
34910                 w = tm.minWidth;
34911             }else{
34912                 w = aw;
34913             }
34914         }
34915         //tipBody.setWidth(w);
34916         el.setWidth(parseInt(w, 10) + p);
34917         if(ce.autoHide === false){
34918             close.setDisplayed(true);
34919             if(dd){
34920                 dd.unlock();
34921             }
34922         }else{
34923             close.setDisplayed(false);
34924             if(dd){
34925                 dd.lock();
34926             }
34927         }
34928         if(xy){
34929             el.avoidY = xy[1]-18;
34930             el.setXY(xy);
34931         }
34932         if(tm.animate){
34933             el.setOpacity(.1);
34934             el.setStyle("visibility", "visible");
34935             el.fadeIn({callback: afterShow});
34936         }else{
34937             afterShow();
34938         }
34939     };
34940     
34941     var afterShow = function(){
34942         if(ce){
34943             el.show();
34944             esc.enable();
34945             if(tm.autoDismiss && ce.autoHide !== false){
34946                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34947             }
34948         }
34949     };
34950     
34951     var hide = function(noanim){
34952         clearTimeout(dismissProc);
34953         clearTimeout(hideProc);
34954         ce = null;
34955         if(el.isVisible()){
34956             esc.disable();
34957             if(noanim !== true && tm.animate){
34958                 el.fadeOut({callback: afterHide});
34959             }else{
34960                 afterHide();
34961             } 
34962         }
34963     };
34964     
34965     var afterHide = function(){
34966         el.hide();
34967         if(removeCls){
34968             el.removeClass(removeCls);
34969             removeCls = null;
34970         }
34971     };
34972     
34973     return {
34974         /**
34975         * @cfg {Number} minWidth
34976         * The minimum width of the quick tip (defaults to 40)
34977         */
34978        minWidth : 40,
34979         /**
34980         * @cfg {Number} maxWidth
34981         * The maximum width of the quick tip (defaults to 300)
34982         */
34983        maxWidth : 300,
34984         /**
34985         * @cfg {Boolean} interceptTitles
34986         * True to automatically use the element's DOM title value if available (defaults to false)
34987         */
34988        interceptTitles : false,
34989         /**
34990         * @cfg {Boolean} trackMouse
34991         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34992         */
34993        trackMouse : false,
34994         /**
34995         * @cfg {Boolean} hideOnClick
34996         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34997         */
34998        hideOnClick : true,
34999         /**
35000         * @cfg {Number} showDelay
35001         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35002         */
35003        showDelay : 500,
35004         /**
35005         * @cfg {Number} hideDelay
35006         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35007         */
35008        hideDelay : 200,
35009         /**
35010         * @cfg {Boolean} autoHide
35011         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35012         * Used in conjunction with hideDelay.
35013         */
35014        autoHide : true,
35015         /**
35016         * @cfg {Boolean}
35017         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35018         * (defaults to true).  Used in conjunction with autoDismissDelay.
35019         */
35020        autoDismiss : true,
35021         /**
35022         * @cfg {Number}
35023         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35024         */
35025        autoDismissDelay : 5000,
35026        /**
35027         * @cfg {Boolean} animate
35028         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35029         */
35030        animate : false,
35031
35032        /**
35033         * @cfg {String} title
35034         * Title text to display (defaults to '').  This can be any valid HTML markup.
35035         */
35036         title: '',
35037        /**
35038         * @cfg {String} text
35039         * Body text to display (defaults to '').  This can be any valid HTML markup.
35040         */
35041         text : '',
35042        /**
35043         * @cfg {String} cls
35044         * A CSS class to apply to the base quick tip element (defaults to '').
35045         */
35046         cls : '',
35047        /**
35048         * @cfg {Number} width
35049         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35050         * minWidth or maxWidth.
35051         */
35052         width : null,
35053
35054     /**
35055      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35056      * or display QuickTips in a page.
35057      */
35058        init : function(){
35059           tm = Roo.QuickTips;
35060           cfg = tm.tagConfig;
35061           if(!inited){
35062               if(!Roo.isReady){ // allow calling of init() before onReady
35063                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35064                   return;
35065               }
35066               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35067               el.fxDefaults = {stopFx: true};
35068               // maximum custom styling
35069               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
35070               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
35071               tipTitle = el.child('h3');
35072               tipTitle.enableDisplayMode("block");
35073               tipBody = el.child('div.x-tip-bd');
35074               tipBodyText = el.child('div.x-tip-bd-inner');
35075               //bdLeft = el.child('div.x-tip-bd-left');
35076               //bdRight = el.child('div.x-tip-bd-right');
35077               close = el.child('div.x-tip-close');
35078               close.enableDisplayMode("block");
35079               close.on("click", hide);
35080               var d = Roo.get(document);
35081               d.on("mousedown", onDown);
35082               d.on("mouseover", onOver);
35083               d.on("mouseout", onOut);
35084               d.on("mousemove", onMove);
35085               esc = d.addKeyListener(27, hide);
35086               esc.disable();
35087               if(Roo.dd.DD){
35088                   dd = el.initDD("default", null, {
35089                       onDrag : function(){
35090                           el.sync();  
35091                       }
35092                   });
35093                   dd.setHandleElId(tipTitle.id);
35094                   dd.lock();
35095               }
35096               inited = true;
35097           }
35098           this.enable(); 
35099        },
35100
35101     /**
35102      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35103      * are supported:
35104      * <pre>
35105 Property    Type                   Description
35106 ----------  ---------------------  ------------------------------------------------------------------------
35107 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35108      * </ul>
35109      * @param {Object} config The config object
35110      */
35111        register : function(config){
35112            var cs = config instanceof Array ? config : arguments;
35113            for(var i = 0, len = cs.length; i < len; i++) {
35114                var c = cs[i];
35115                var target = c.target;
35116                if(target){
35117                    if(target instanceof Array){
35118                        for(var j = 0, jlen = target.length; j < jlen; j++){
35119                            tagEls[target[j]] = c;
35120                        }
35121                    }else{
35122                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35123                    }
35124                }
35125            }
35126        },
35127
35128     /**
35129      * Removes this quick tip from its element and destroys it.
35130      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35131      */
35132        unregister : function(el){
35133            delete tagEls[Roo.id(el)];
35134        },
35135
35136     /**
35137      * Enable this quick tip.
35138      */
35139        enable : function(){
35140            if(inited && disabled){
35141                locks.pop();
35142                if(locks.length < 1){
35143                    disabled = false;
35144                }
35145            }
35146        },
35147
35148     /**
35149      * Disable this quick tip.
35150      */
35151        disable : function(){
35152           disabled = true;
35153           clearTimeout(showProc);
35154           clearTimeout(hideProc);
35155           clearTimeout(dismissProc);
35156           if(ce){
35157               hide(true);
35158           }
35159           locks.push(1);
35160        },
35161
35162     /**
35163      * Returns true if the quick tip is enabled, else false.
35164      */
35165        isEnabled : function(){
35166             return !disabled;
35167        },
35168
35169         // private
35170        tagConfig : {
35171            namespace : "roo", // was ext?? this may break..
35172            alt_namespace : "ext",
35173            attribute : "qtip",
35174            width : "width",
35175            target : "target",
35176            title : "qtitle",
35177            hide : "hide",
35178            cls : "qclass"
35179        }
35180    };
35181 }();
35182
35183 // backwards compat
35184 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35185  * Based on:
35186  * Ext JS Library 1.1.1
35187  * Copyright(c) 2006-2007, Ext JS, LLC.
35188  *
35189  * Originally Released Under LGPL - original licence link has changed is not relivant.
35190  *
35191  * Fork - LGPL
35192  * <script type="text/javascript">
35193  */
35194  
35195
35196 /**
35197  * @class Roo.tree.TreePanel
35198  * @extends Roo.data.Tree
35199  * @cfg {Roo.tree.TreeNode} root The root node
35200  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35201  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35202  * @cfg {Boolean} enableDD true to enable drag and drop
35203  * @cfg {Boolean} enableDrag true to enable just drag
35204  * @cfg {Boolean} enableDrop true to enable just drop
35205  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35206  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35207  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35208  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35209  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35210  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35211  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35212  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35213  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35214  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35215  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35216  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35217  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35218  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35219  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
35220  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
35221  * 
35222  * @constructor
35223  * @param {String/HTMLElement/Element} el The container element
35224  * @param {Object} config
35225  */
35226 Roo.tree.TreePanel = function(el, config){
35227     var root = false;
35228     var loader = false;
35229     if (config.root) {
35230         root = config.root;
35231         delete config.root;
35232     }
35233     if (config.loader) {
35234         loader = config.loader;
35235         delete config.loader;
35236     }
35237     
35238     Roo.apply(this, config);
35239     Roo.tree.TreePanel.superclass.constructor.call(this);
35240     this.el = Roo.get(el);
35241     this.el.addClass('x-tree');
35242     //console.log(root);
35243     if (root) {
35244         this.setRootNode( Roo.factory(root, Roo.tree));
35245     }
35246     if (loader) {
35247         this.loader = Roo.factory(loader, Roo.tree);
35248     }
35249    /**
35250     * Read-only. The id of the container element becomes this TreePanel's id.
35251     */
35252     this.id = this.el.id;
35253     this.addEvents({
35254         /**
35255         * @event beforeload
35256         * Fires before a node is loaded, return false to cancel
35257         * @param {Node} node The node being loaded
35258         */
35259         "beforeload" : true,
35260         /**
35261         * @event load
35262         * Fires when a node is loaded
35263         * @param {Node} node The node that was loaded
35264         */
35265         "load" : true,
35266         /**
35267         * @event textchange
35268         * Fires when the text for a node is changed
35269         * @param {Node} node The node
35270         * @param {String} text The new text
35271         * @param {String} oldText The old text
35272         */
35273         "textchange" : true,
35274         /**
35275         * @event beforeexpand
35276         * Fires before a node is expanded, return false to cancel.
35277         * @param {Node} node The node
35278         * @param {Boolean} deep
35279         * @param {Boolean} anim
35280         */
35281         "beforeexpand" : true,
35282         /**
35283         * @event beforecollapse
35284         * Fires before a node is collapsed, return false to cancel.
35285         * @param {Node} node The node
35286         * @param {Boolean} deep
35287         * @param {Boolean} anim
35288         */
35289         "beforecollapse" : true,
35290         /**
35291         * @event expand
35292         * Fires when a node is expanded
35293         * @param {Node} node The node
35294         */
35295         "expand" : true,
35296         /**
35297         * @event disabledchange
35298         * Fires when the disabled status of a node changes
35299         * @param {Node} node The node
35300         * @param {Boolean} disabled
35301         */
35302         "disabledchange" : true,
35303         /**
35304         * @event collapse
35305         * Fires when a node is collapsed
35306         * @param {Node} node The node
35307         */
35308         "collapse" : true,
35309         /**
35310         * @event beforeclick
35311         * Fires before click processing on a node. Return false to cancel the default action.
35312         * @param {Node} node The node
35313         * @param {Roo.EventObject} e The event object
35314         */
35315         "beforeclick":true,
35316         /**
35317         * @event checkchange
35318         * Fires when a node with a checkbox's checked property changes
35319         * @param {Node} this This node
35320         * @param {Boolean} checked
35321         */
35322         "checkchange":true,
35323         /**
35324         * @event click
35325         * Fires when a node is clicked
35326         * @param {Node} node The node
35327         * @param {Roo.EventObject} e The event object
35328         */
35329         "click":true,
35330         /**
35331         * @event dblclick
35332         * Fires when a node is double clicked
35333         * @param {Node} node The node
35334         * @param {Roo.EventObject} e The event object
35335         */
35336         "dblclick":true,
35337         /**
35338         * @event contextmenu
35339         * Fires when a node is right clicked
35340         * @param {Node} node The node
35341         * @param {Roo.EventObject} e The event object
35342         */
35343         "contextmenu":true,
35344         /**
35345         * @event beforechildrenrendered
35346         * Fires right before the child nodes for a node are rendered
35347         * @param {Node} node The node
35348         */
35349         "beforechildrenrendered":true,
35350         /**
35351         * @event startdrag
35352         * Fires when a node starts being dragged
35353         * @param {Roo.tree.TreePanel} this
35354         * @param {Roo.tree.TreeNode} node
35355         * @param {event} e The raw browser event
35356         */ 
35357        "startdrag" : true,
35358        /**
35359         * @event enddrag
35360         * Fires when a drag operation is complete
35361         * @param {Roo.tree.TreePanel} this
35362         * @param {Roo.tree.TreeNode} node
35363         * @param {event} e The raw browser event
35364         */
35365        "enddrag" : true,
35366        /**
35367         * @event dragdrop
35368         * Fires when a dragged node is dropped on a valid DD target
35369         * @param {Roo.tree.TreePanel} this
35370         * @param {Roo.tree.TreeNode} node
35371         * @param {DD} dd The dd it was dropped on
35372         * @param {event} e The raw browser event
35373         */
35374        "dragdrop" : true,
35375        /**
35376         * @event beforenodedrop
35377         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35378         * passed to handlers has the following properties:<br />
35379         * <ul style="padding:5px;padding-left:16px;">
35380         * <li>tree - The TreePanel</li>
35381         * <li>target - The node being targeted for the drop</li>
35382         * <li>data - The drag data from the drag source</li>
35383         * <li>point - The point of the drop - append, above or below</li>
35384         * <li>source - The drag source</li>
35385         * <li>rawEvent - Raw mouse event</li>
35386         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35387         * to be inserted by setting them on this object.</li>
35388         * <li>cancel - Set this to true to cancel the drop.</li>
35389         * </ul>
35390         * @param {Object} dropEvent
35391         */
35392        "beforenodedrop" : true,
35393        /**
35394         * @event nodedrop
35395         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35396         * passed to handlers has the following properties:<br />
35397         * <ul style="padding:5px;padding-left:16px;">
35398         * <li>tree - The TreePanel</li>
35399         * <li>target - The node being targeted for the drop</li>
35400         * <li>data - The drag data from the drag source</li>
35401         * <li>point - The point of the drop - append, above or below</li>
35402         * <li>source - The drag source</li>
35403         * <li>rawEvent - Raw mouse event</li>
35404         * <li>dropNode - Dropped node(s).</li>
35405         * </ul>
35406         * @param {Object} dropEvent
35407         */
35408        "nodedrop" : true,
35409         /**
35410         * @event nodedragover
35411         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35412         * passed to handlers has the following properties:<br />
35413         * <ul style="padding:5px;padding-left:16px;">
35414         * <li>tree - The TreePanel</li>
35415         * <li>target - The node being targeted for the drop</li>
35416         * <li>data - The drag data from the drag source</li>
35417         * <li>point - The point of the drop - append, above or below</li>
35418         * <li>source - The drag source</li>
35419         * <li>rawEvent - Raw mouse event</li>
35420         * <li>dropNode - Drop node(s) provided by the source.</li>
35421         * <li>cancel - Set this to true to signal drop not allowed.</li>
35422         * </ul>
35423         * @param {Object} dragOverEvent
35424         */
35425        "nodedragover" : true,
35426        /**
35427         * @event appendnode
35428         * Fires when append node to the tree
35429         * @param {Roo.tree.TreePanel} this
35430         * @param {Roo.tree.TreeNode} node
35431         * @param {Number} index The index of the newly appended node
35432         */
35433        "appendnode" : true
35434         
35435     });
35436     if(this.singleExpand){
35437        this.on("beforeexpand", this.restrictExpand, this);
35438     }
35439     if (this.editor) {
35440         this.editor.tree = this;
35441         this.editor = Roo.factory(this.editor, Roo.tree);
35442     }
35443     
35444     if (this.selModel) {
35445         this.selModel = Roo.factory(this.selModel, Roo.tree);
35446     }
35447    
35448 };
35449 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35450     rootVisible : true,
35451     animate: Roo.enableFx,
35452     lines : true,
35453     enableDD : false,
35454     hlDrop : Roo.enableFx,
35455   
35456     renderer: false,
35457     
35458     rendererTip: false,
35459     // private
35460     restrictExpand : function(node){
35461         var p = node.parentNode;
35462         if(p){
35463             if(p.expandedChild && p.expandedChild.parentNode == p){
35464                 p.expandedChild.collapse();
35465             }
35466             p.expandedChild = node;
35467         }
35468     },
35469
35470     // private override
35471     setRootNode : function(node){
35472         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35473         if(!this.rootVisible){
35474             node.ui = new Roo.tree.RootTreeNodeUI(node);
35475         }
35476         return node;
35477     },
35478
35479     /**
35480      * Returns the container element for this TreePanel
35481      */
35482     getEl : function(){
35483         return this.el;
35484     },
35485
35486     /**
35487      * Returns the default TreeLoader for this TreePanel
35488      */
35489     getLoader : function(){
35490         return this.loader;
35491     },
35492
35493     /**
35494      * Expand all nodes
35495      */
35496     expandAll : function(){
35497         this.root.expand(true);
35498     },
35499
35500     /**
35501      * Collapse all nodes
35502      */
35503     collapseAll : function(){
35504         this.root.collapse(true);
35505     },
35506
35507     /**
35508      * Returns the selection model used by this TreePanel
35509      */
35510     getSelectionModel : function(){
35511         if(!this.selModel){
35512             this.selModel = new Roo.tree.DefaultSelectionModel();
35513         }
35514         return this.selModel;
35515     },
35516
35517     /**
35518      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35519      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35520      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35521      * @return {Array}
35522      */
35523     getChecked : function(a, startNode){
35524         startNode = startNode || this.root;
35525         var r = [];
35526         var f = function(){
35527             if(this.attributes.checked){
35528                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35529             }
35530         }
35531         startNode.cascade(f);
35532         return r;
35533     },
35534
35535     /**
35536      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35537      * @param {String} path
35538      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35539      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35540      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35541      */
35542     expandPath : function(path, attr, callback){
35543         attr = attr || "id";
35544         var keys = path.split(this.pathSeparator);
35545         var curNode = this.root;
35546         if(curNode.attributes[attr] != keys[1]){ // invalid root
35547             if(callback){
35548                 callback(false, null);
35549             }
35550             return;
35551         }
35552         var index = 1;
35553         var f = function(){
35554             if(++index == keys.length){
35555                 if(callback){
35556                     callback(true, curNode);
35557                 }
35558                 return;
35559             }
35560             var c = curNode.findChild(attr, keys[index]);
35561             if(!c){
35562                 if(callback){
35563                     callback(false, curNode);
35564                 }
35565                 return;
35566             }
35567             curNode = c;
35568             c.expand(false, false, f);
35569         };
35570         curNode.expand(false, false, f);
35571     },
35572
35573     /**
35574      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35575      * @param {String} path
35576      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35577      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35578      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35579      */
35580     selectPath : function(path, attr, callback){
35581         attr = attr || "id";
35582         var keys = path.split(this.pathSeparator);
35583         var v = keys.pop();
35584         if(keys.length > 0){
35585             var f = function(success, node){
35586                 if(success && node){
35587                     var n = node.findChild(attr, v);
35588                     if(n){
35589                         n.select();
35590                         if(callback){
35591                             callback(true, n);
35592                         }
35593                     }else if(callback){
35594                         callback(false, n);
35595                     }
35596                 }else{
35597                     if(callback){
35598                         callback(false, n);
35599                     }
35600                 }
35601             };
35602             this.expandPath(keys.join(this.pathSeparator), attr, f);
35603         }else{
35604             this.root.select();
35605             if(callback){
35606                 callback(true, this.root);
35607             }
35608         }
35609     },
35610
35611     getTreeEl : function(){
35612         return this.el;
35613     },
35614
35615     /**
35616      * Trigger rendering of this TreePanel
35617      */
35618     render : function(){
35619         if (this.innerCt) {
35620             return this; // stop it rendering more than once!!
35621         }
35622         
35623         this.innerCt = this.el.createChild({tag:"ul",
35624                cls:"x-tree-root-ct " +
35625                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35626
35627         if(this.containerScroll){
35628             Roo.dd.ScrollManager.register(this.el);
35629         }
35630         if((this.enableDD || this.enableDrop) && !this.dropZone){
35631            /**
35632             * The dropZone used by this tree if drop is enabled
35633             * @type Roo.tree.TreeDropZone
35634             */
35635              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35636                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35637            });
35638         }
35639         if((this.enableDD || this.enableDrag) && !this.dragZone){
35640            /**
35641             * The dragZone used by this tree if drag is enabled
35642             * @type Roo.tree.TreeDragZone
35643             */
35644             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35645                ddGroup: this.ddGroup || "TreeDD",
35646                scroll: this.ddScroll
35647            });
35648         }
35649         this.getSelectionModel().init(this);
35650         if (!this.root) {
35651             Roo.log("ROOT not set in tree");
35652             return this;
35653         }
35654         this.root.render();
35655         if(!this.rootVisible){
35656             this.root.renderChildren();
35657         }
35658         return this;
35659     }
35660 });/*
35661  * Based on:
35662  * Ext JS Library 1.1.1
35663  * Copyright(c) 2006-2007, Ext JS, LLC.
35664  *
35665  * Originally Released Under LGPL - original licence link has changed is not relivant.
35666  *
35667  * Fork - LGPL
35668  * <script type="text/javascript">
35669  */
35670  
35671
35672 /**
35673  * @class Roo.tree.DefaultSelectionModel
35674  * @extends Roo.util.Observable
35675  * The default single selection for a TreePanel.
35676  * @param {Object} cfg Configuration
35677  */
35678 Roo.tree.DefaultSelectionModel = function(cfg){
35679    this.selNode = null;
35680    
35681    
35682    
35683    this.addEvents({
35684        /**
35685         * @event selectionchange
35686         * Fires when the selected node changes
35687         * @param {DefaultSelectionModel} this
35688         * @param {TreeNode} node the new selection
35689         */
35690        "selectionchange" : true,
35691
35692        /**
35693         * @event beforeselect
35694         * Fires before the selected node changes, return false to cancel the change
35695         * @param {DefaultSelectionModel} this
35696         * @param {TreeNode} node the new selection
35697         * @param {TreeNode} node the old selection
35698         */
35699        "beforeselect" : true
35700    });
35701    
35702     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35703 };
35704
35705 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35706     init : function(tree){
35707         this.tree = tree;
35708         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35709         tree.on("click", this.onNodeClick, this);
35710     },
35711     
35712     onNodeClick : function(node, e){
35713         if (e.ctrlKey && this.selNode == node)  {
35714             this.unselect(node);
35715             return;
35716         }
35717         this.select(node);
35718     },
35719     
35720     /**
35721      * Select a node.
35722      * @param {TreeNode} node The node to select
35723      * @return {TreeNode} The selected node
35724      */
35725     select : function(node){
35726         var last = this.selNode;
35727         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35728             if(last){
35729                 last.ui.onSelectedChange(false);
35730             }
35731             this.selNode = node;
35732             node.ui.onSelectedChange(true);
35733             this.fireEvent("selectionchange", this, node, last);
35734         }
35735         return node;
35736     },
35737     
35738     /**
35739      * Deselect a node.
35740      * @param {TreeNode} node The node to unselect
35741      */
35742     unselect : function(node){
35743         if(this.selNode == node){
35744             this.clearSelections();
35745         }    
35746     },
35747     
35748     /**
35749      * Clear all selections
35750      */
35751     clearSelections : function(){
35752         var n = this.selNode;
35753         if(n){
35754             n.ui.onSelectedChange(false);
35755             this.selNode = null;
35756             this.fireEvent("selectionchange", this, null);
35757         }
35758         return n;
35759     },
35760     
35761     /**
35762      * Get the selected node
35763      * @return {TreeNode} The selected node
35764      */
35765     getSelectedNode : function(){
35766         return this.selNode;    
35767     },
35768     
35769     /**
35770      * Returns true if the node is selected
35771      * @param {TreeNode} node The node to check
35772      * @return {Boolean}
35773      */
35774     isSelected : function(node){
35775         return this.selNode == node;  
35776     },
35777
35778     /**
35779      * Selects the node above the selected node in the tree, intelligently walking the nodes
35780      * @return TreeNode The new selection
35781      */
35782     selectPrevious : function(){
35783         var s = this.selNode || this.lastSelNode;
35784         if(!s){
35785             return null;
35786         }
35787         var ps = s.previousSibling;
35788         if(ps){
35789             if(!ps.isExpanded() || ps.childNodes.length < 1){
35790                 return this.select(ps);
35791             } else{
35792                 var lc = ps.lastChild;
35793                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35794                     lc = lc.lastChild;
35795                 }
35796                 return this.select(lc);
35797             }
35798         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35799             return this.select(s.parentNode);
35800         }
35801         return null;
35802     },
35803
35804     /**
35805      * Selects the node above the selected node in the tree, intelligently walking the nodes
35806      * @return TreeNode The new selection
35807      */
35808     selectNext : function(){
35809         var s = this.selNode || this.lastSelNode;
35810         if(!s){
35811             return null;
35812         }
35813         if(s.firstChild && s.isExpanded()){
35814              return this.select(s.firstChild);
35815          }else if(s.nextSibling){
35816              return this.select(s.nextSibling);
35817          }else if(s.parentNode){
35818             var newS = null;
35819             s.parentNode.bubble(function(){
35820                 if(this.nextSibling){
35821                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35822                     return false;
35823                 }
35824             });
35825             return newS;
35826          }
35827         return null;
35828     },
35829
35830     onKeyDown : function(e){
35831         var s = this.selNode || this.lastSelNode;
35832         // undesirable, but required
35833         var sm = this;
35834         if(!s){
35835             return;
35836         }
35837         var k = e.getKey();
35838         switch(k){
35839              case e.DOWN:
35840                  e.stopEvent();
35841                  this.selectNext();
35842              break;
35843              case e.UP:
35844                  e.stopEvent();
35845                  this.selectPrevious();
35846              break;
35847              case e.RIGHT:
35848                  e.preventDefault();
35849                  if(s.hasChildNodes()){
35850                      if(!s.isExpanded()){
35851                          s.expand();
35852                      }else if(s.firstChild){
35853                          this.select(s.firstChild, e);
35854                      }
35855                  }
35856              break;
35857              case e.LEFT:
35858                  e.preventDefault();
35859                  if(s.hasChildNodes() && s.isExpanded()){
35860                      s.collapse();
35861                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35862                      this.select(s.parentNode, e);
35863                  }
35864              break;
35865         };
35866     }
35867 });
35868
35869 /**
35870  * @class Roo.tree.MultiSelectionModel
35871  * @extends Roo.util.Observable
35872  * Multi selection for a TreePanel.
35873  * @param {Object} cfg Configuration
35874  */
35875 Roo.tree.MultiSelectionModel = function(){
35876    this.selNodes = [];
35877    this.selMap = {};
35878    this.addEvents({
35879        /**
35880         * @event selectionchange
35881         * Fires when the selected nodes change
35882         * @param {MultiSelectionModel} this
35883         * @param {Array} nodes Array of the selected nodes
35884         */
35885        "selectionchange" : true
35886    });
35887    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35888    
35889 };
35890
35891 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35892     init : function(tree){
35893         this.tree = tree;
35894         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35895         tree.on("click", this.onNodeClick, this);
35896     },
35897     
35898     onNodeClick : function(node, e){
35899         this.select(node, e, e.ctrlKey);
35900     },
35901     
35902     /**
35903      * Select a node.
35904      * @param {TreeNode} node The node to select
35905      * @param {EventObject} e (optional) An event associated with the selection
35906      * @param {Boolean} keepExisting True to retain existing selections
35907      * @return {TreeNode} The selected node
35908      */
35909     select : function(node, e, keepExisting){
35910         if(keepExisting !== true){
35911             this.clearSelections(true);
35912         }
35913         if(this.isSelected(node)){
35914             this.lastSelNode = node;
35915             return node;
35916         }
35917         this.selNodes.push(node);
35918         this.selMap[node.id] = node;
35919         this.lastSelNode = node;
35920         node.ui.onSelectedChange(true);
35921         this.fireEvent("selectionchange", this, this.selNodes);
35922         return node;
35923     },
35924     
35925     /**
35926      * Deselect a node.
35927      * @param {TreeNode} node The node to unselect
35928      */
35929     unselect : function(node){
35930         if(this.selMap[node.id]){
35931             node.ui.onSelectedChange(false);
35932             var sn = this.selNodes;
35933             var index = -1;
35934             if(sn.indexOf){
35935                 index = sn.indexOf(node);
35936             }else{
35937                 for(var i = 0, len = sn.length; i < len; i++){
35938                     if(sn[i] == node){
35939                         index = i;
35940                         break;
35941                     }
35942                 }
35943             }
35944             if(index != -1){
35945                 this.selNodes.splice(index, 1);
35946             }
35947             delete this.selMap[node.id];
35948             this.fireEvent("selectionchange", this, this.selNodes);
35949         }
35950     },
35951     
35952     /**
35953      * Clear all selections
35954      */
35955     clearSelections : function(suppressEvent){
35956         var sn = this.selNodes;
35957         if(sn.length > 0){
35958             for(var i = 0, len = sn.length; i < len; i++){
35959                 sn[i].ui.onSelectedChange(false);
35960             }
35961             this.selNodes = [];
35962             this.selMap = {};
35963             if(suppressEvent !== true){
35964                 this.fireEvent("selectionchange", this, this.selNodes);
35965             }
35966         }
35967     },
35968     
35969     /**
35970      * Returns true if the node is selected
35971      * @param {TreeNode} node The node to check
35972      * @return {Boolean}
35973      */
35974     isSelected : function(node){
35975         return this.selMap[node.id] ? true : false;  
35976     },
35977     
35978     /**
35979      * Returns an array of the selected nodes
35980      * @return {Array}
35981      */
35982     getSelectedNodes : function(){
35983         return this.selNodes;    
35984     },
35985
35986     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35987
35988     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35989
35990     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35991 });/*
35992  * Based on:
35993  * Ext JS Library 1.1.1
35994  * Copyright(c) 2006-2007, Ext JS, LLC.
35995  *
35996  * Originally Released Under LGPL - original licence link has changed is not relivant.
35997  *
35998  * Fork - LGPL
35999  * <script type="text/javascript">
36000  */
36001  
36002 /**
36003  * @class Roo.tree.TreeNode
36004  * @extends Roo.data.Node
36005  * @cfg {String} text The text for this node
36006  * @cfg {Boolean} expanded true to start the node expanded
36007  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36008  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36009  * @cfg {Boolean} disabled true to start the node disabled
36010  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36011  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36012  * @cfg {String} cls A css class to be added to the node
36013  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36014  * @cfg {String} href URL of the link used for the node (defaults to #)
36015  * @cfg {String} hrefTarget target frame for the link
36016  * @cfg {String} qtip An Ext QuickTip for the node
36017  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36018  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36019  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36020  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36021  * (defaults to undefined with no checkbox rendered)
36022  * @constructor
36023  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36024  */
36025 Roo.tree.TreeNode = function(attributes){
36026     attributes = attributes || {};
36027     if(typeof attributes == "string"){
36028         attributes = {text: attributes};
36029     }
36030     this.childrenRendered = false;
36031     this.rendered = false;
36032     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36033     this.expanded = attributes.expanded === true;
36034     this.isTarget = attributes.isTarget !== false;
36035     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36036     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36037
36038     /**
36039      * Read-only. The text for this node. To change it use setText().
36040      * @type String
36041      */
36042     this.text = attributes.text;
36043     /**
36044      * True if this node is disabled.
36045      * @type Boolean
36046      */
36047     this.disabled = attributes.disabled === true;
36048
36049     this.addEvents({
36050         /**
36051         * @event textchange
36052         * Fires when the text for this node is changed
36053         * @param {Node} this This node
36054         * @param {String} text The new text
36055         * @param {String} oldText The old text
36056         */
36057         "textchange" : true,
36058         /**
36059         * @event beforeexpand
36060         * Fires before this node is expanded, return false to cancel.
36061         * @param {Node} this This node
36062         * @param {Boolean} deep
36063         * @param {Boolean} anim
36064         */
36065         "beforeexpand" : true,
36066         /**
36067         * @event beforecollapse
36068         * Fires before this node is collapsed, return false to cancel.
36069         * @param {Node} this This node
36070         * @param {Boolean} deep
36071         * @param {Boolean} anim
36072         */
36073         "beforecollapse" : true,
36074         /**
36075         * @event expand
36076         * Fires when this node is expanded
36077         * @param {Node} this This node
36078         */
36079         "expand" : true,
36080         /**
36081         * @event disabledchange
36082         * Fires when the disabled status of this node changes
36083         * @param {Node} this This node
36084         * @param {Boolean} disabled
36085         */
36086         "disabledchange" : true,
36087         /**
36088         * @event collapse
36089         * Fires when this node is collapsed
36090         * @param {Node} this This node
36091         */
36092         "collapse" : true,
36093         /**
36094         * @event beforeclick
36095         * Fires before click processing. Return false to cancel the default action.
36096         * @param {Node} this This node
36097         * @param {Roo.EventObject} e The event object
36098         */
36099         "beforeclick":true,
36100         /**
36101         * @event checkchange
36102         * Fires when a node with a checkbox's checked property changes
36103         * @param {Node} this This node
36104         * @param {Boolean} checked
36105         */
36106         "checkchange":true,
36107         /**
36108         * @event click
36109         * Fires when this node is clicked
36110         * @param {Node} this This node
36111         * @param {Roo.EventObject} e The event object
36112         */
36113         "click":true,
36114         /**
36115         * @event dblclick
36116         * Fires when this node is double clicked
36117         * @param {Node} this This node
36118         * @param {Roo.EventObject} e The event object
36119         */
36120         "dblclick":true,
36121         /**
36122         * @event contextmenu
36123         * Fires when this node is right clicked
36124         * @param {Node} this This node
36125         * @param {Roo.EventObject} e The event object
36126         */
36127         "contextmenu":true,
36128         /**
36129         * @event beforechildrenrendered
36130         * Fires right before the child nodes for this node are rendered
36131         * @param {Node} this This node
36132         */
36133         "beforechildrenrendered":true
36134     });
36135
36136     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36137
36138     /**
36139      * Read-only. The UI for this node
36140      * @type TreeNodeUI
36141      */
36142     this.ui = new uiClass(this);
36143     
36144     // finally support items[]
36145     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36146         return;
36147     }
36148     
36149     
36150     Roo.each(this.attributes.items, function(c) {
36151         this.appendChild(Roo.factory(c,Roo.Tree));
36152     }, this);
36153     delete this.attributes.items;
36154     
36155     
36156     
36157 };
36158 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36159     preventHScroll: true,
36160     /**
36161      * Returns true if this node is expanded
36162      * @return {Boolean}
36163      */
36164     isExpanded : function(){
36165         return this.expanded;
36166     },
36167
36168     /**
36169      * Returns the UI object for this node
36170      * @return {TreeNodeUI}
36171      */
36172     getUI : function(){
36173         return this.ui;
36174     },
36175
36176     // private override
36177     setFirstChild : function(node){
36178         var of = this.firstChild;
36179         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36180         if(this.childrenRendered && of && node != of){
36181             of.renderIndent(true, true);
36182         }
36183         if(this.rendered){
36184             this.renderIndent(true, true);
36185         }
36186     },
36187
36188     // private override
36189     setLastChild : function(node){
36190         var ol = this.lastChild;
36191         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36192         if(this.childrenRendered && ol && node != ol){
36193             ol.renderIndent(true, true);
36194         }
36195         if(this.rendered){
36196             this.renderIndent(true, true);
36197         }
36198     },
36199
36200     // these methods are overridden to provide lazy rendering support
36201     // private override
36202     appendChild : function()
36203     {
36204         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36205         if(node && this.childrenRendered){
36206             node.render();
36207         }
36208         this.ui.updateExpandIcon();
36209         return node;
36210     },
36211
36212     // private override
36213     removeChild : function(node){
36214         this.ownerTree.getSelectionModel().unselect(node);
36215         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36216         // if it's been rendered remove dom node
36217         if(this.childrenRendered){
36218             node.ui.remove();
36219         }
36220         if(this.childNodes.length < 1){
36221             this.collapse(false, false);
36222         }else{
36223             this.ui.updateExpandIcon();
36224         }
36225         if(!this.firstChild) {
36226             this.childrenRendered = false;
36227         }
36228         return node;
36229     },
36230
36231     // private override
36232     insertBefore : function(node, refNode){
36233         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36234         if(newNode && refNode && this.childrenRendered){
36235             node.render();
36236         }
36237         this.ui.updateExpandIcon();
36238         return newNode;
36239     },
36240
36241     /**
36242      * Sets the text for this node
36243      * @param {String} text
36244      */
36245     setText : function(text){
36246         var oldText = this.text;
36247         this.text = text;
36248         this.attributes.text = text;
36249         if(this.rendered){ // event without subscribing
36250             this.ui.onTextChange(this, text, oldText);
36251         }
36252         this.fireEvent("textchange", this, text, oldText);
36253     },
36254
36255     /**
36256      * Triggers selection of this node
36257      */
36258     select : function(){
36259         this.getOwnerTree().getSelectionModel().select(this);
36260     },
36261
36262     /**
36263      * Triggers deselection of this node
36264      */
36265     unselect : function(){
36266         this.getOwnerTree().getSelectionModel().unselect(this);
36267     },
36268
36269     /**
36270      * Returns true if this node is selected
36271      * @return {Boolean}
36272      */
36273     isSelected : function(){
36274         return this.getOwnerTree().getSelectionModel().isSelected(this);
36275     },
36276
36277     /**
36278      * Expand this node.
36279      * @param {Boolean} deep (optional) True to expand all children as well
36280      * @param {Boolean} anim (optional) false to cancel the default animation
36281      * @param {Function} callback (optional) A callback to be called when
36282      * expanding this node completes (does not wait for deep expand to complete).
36283      * Called with 1 parameter, this node.
36284      */
36285     expand : function(deep, anim, callback){
36286         if(!this.expanded){
36287             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36288                 return;
36289             }
36290             if(!this.childrenRendered){
36291                 this.renderChildren();
36292             }
36293             this.expanded = true;
36294             
36295             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36296                 this.ui.animExpand(function(){
36297                     this.fireEvent("expand", this);
36298                     if(typeof callback == "function"){
36299                         callback(this);
36300                     }
36301                     if(deep === true){
36302                         this.expandChildNodes(true);
36303                     }
36304                 }.createDelegate(this));
36305                 return;
36306             }else{
36307                 this.ui.expand();
36308                 this.fireEvent("expand", this);
36309                 if(typeof callback == "function"){
36310                     callback(this);
36311                 }
36312             }
36313         }else{
36314            if(typeof callback == "function"){
36315                callback(this);
36316            }
36317         }
36318         if(deep === true){
36319             this.expandChildNodes(true);
36320         }
36321     },
36322
36323     isHiddenRoot : function(){
36324         return this.isRoot && !this.getOwnerTree().rootVisible;
36325     },
36326
36327     /**
36328      * Collapse this node.
36329      * @param {Boolean} deep (optional) True to collapse all children as well
36330      * @param {Boolean} anim (optional) false to cancel the default animation
36331      */
36332     collapse : function(deep, anim){
36333         if(this.expanded && !this.isHiddenRoot()){
36334             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36335                 return;
36336             }
36337             this.expanded = false;
36338             if((this.getOwnerTree().animate && anim !== false) || anim){
36339                 this.ui.animCollapse(function(){
36340                     this.fireEvent("collapse", this);
36341                     if(deep === true){
36342                         this.collapseChildNodes(true);
36343                     }
36344                 }.createDelegate(this));
36345                 return;
36346             }else{
36347                 this.ui.collapse();
36348                 this.fireEvent("collapse", this);
36349             }
36350         }
36351         if(deep === true){
36352             var cs = this.childNodes;
36353             for(var i = 0, len = cs.length; i < len; i++) {
36354                 cs[i].collapse(true, false);
36355             }
36356         }
36357     },
36358
36359     // private
36360     delayedExpand : function(delay){
36361         if(!this.expandProcId){
36362             this.expandProcId = this.expand.defer(delay, this);
36363         }
36364     },
36365
36366     // private
36367     cancelExpand : function(){
36368         if(this.expandProcId){
36369             clearTimeout(this.expandProcId);
36370         }
36371         this.expandProcId = false;
36372     },
36373
36374     /**
36375      * Toggles expanded/collapsed state of the node
36376      */
36377     toggle : function(){
36378         if(this.expanded){
36379             this.collapse();
36380         }else{
36381             this.expand();
36382         }
36383     },
36384
36385     /**
36386      * Ensures all parent nodes are expanded
36387      */
36388     ensureVisible : function(callback){
36389         var tree = this.getOwnerTree();
36390         tree.expandPath(this.parentNode.getPath(), false, function(){
36391             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36392             Roo.callback(callback);
36393         }.createDelegate(this));
36394     },
36395
36396     /**
36397      * Expand all child nodes
36398      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36399      */
36400     expandChildNodes : function(deep){
36401         var cs = this.childNodes;
36402         for(var i = 0, len = cs.length; i < len; i++) {
36403                 cs[i].expand(deep);
36404         }
36405     },
36406
36407     /**
36408      * Collapse all child nodes
36409      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36410      */
36411     collapseChildNodes : function(deep){
36412         var cs = this.childNodes;
36413         for(var i = 0, len = cs.length; i < len; i++) {
36414                 cs[i].collapse(deep);
36415         }
36416     },
36417
36418     /**
36419      * Disables this node
36420      */
36421     disable : function(){
36422         this.disabled = true;
36423         this.unselect();
36424         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36425             this.ui.onDisableChange(this, true);
36426         }
36427         this.fireEvent("disabledchange", this, true);
36428     },
36429
36430     /**
36431      * Enables this node
36432      */
36433     enable : function(){
36434         this.disabled = false;
36435         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36436             this.ui.onDisableChange(this, false);
36437         }
36438         this.fireEvent("disabledchange", this, false);
36439     },
36440
36441     // private
36442     renderChildren : function(suppressEvent){
36443         if(suppressEvent !== false){
36444             this.fireEvent("beforechildrenrendered", this);
36445         }
36446         var cs = this.childNodes;
36447         for(var i = 0, len = cs.length; i < len; i++){
36448             cs[i].render(true);
36449         }
36450         this.childrenRendered = true;
36451     },
36452
36453     // private
36454     sort : function(fn, scope){
36455         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36456         if(this.childrenRendered){
36457             var cs = this.childNodes;
36458             for(var i = 0, len = cs.length; i < len; i++){
36459                 cs[i].render(true);
36460             }
36461         }
36462     },
36463
36464     // private
36465     render : function(bulkRender){
36466         this.ui.render(bulkRender);
36467         if(!this.rendered){
36468             this.rendered = true;
36469             if(this.expanded){
36470                 this.expanded = false;
36471                 this.expand(false, false);
36472             }
36473         }
36474     },
36475
36476     // private
36477     renderIndent : function(deep, refresh){
36478         if(refresh){
36479             this.ui.childIndent = null;
36480         }
36481         this.ui.renderIndent();
36482         if(deep === true && this.childrenRendered){
36483             var cs = this.childNodes;
36484             for(var i = 0, len = cs.length; i < len; i++){
36485                 cs[i].renderIndent(true, refresh);
36486             }
36487         }
36488     }
36489 });/*
36490  * Based on:
36491  * Ext JS Library 1.1.1
36492  * Copyright(c) 2006-2007, Ext JS, LLC.
36493  *
36494  * Originally Released Under LGPL - original licence link has changed is not relivant.
36495  *
36496  * Fork - LGPL
36497  * <script type="text/javascript">
36498  */
36499  
36500 /**
36501  * @class Roo.tree.AsyncTreeNode
36502  * @extends Roo.tree.TreeNode
36503  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36504  * @constructor
36505  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36506  */
36507  Roo.tree.AsyncTreeNode = function(config){
36508     this.loaded = false;
36509     this.loading = false;
36510     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36511     /**
36512     * @event beforeload
36513     * Fires before this node is loaded, return false to cancel
36514     * @param {Node} this This node
36515     */
36516     this.addEvents({'beforeload':true, 'load': true});
36517     /**
36518     * @event load
36519     * Fires when this node is loaded
36520     * @param {Node} this This node
36521     */
36522     /**
36523      * The loader used by this node (defaults to using the tree's defined loader)
36524      * @type TreeLoader
36525      * @property loader
36526      */
36527 };
36528 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36529     expand : function(deep, anim, callback){
36530         if(this.loading){ // if an async load is already running, waiting til it's done
36531             var timer;
36532             var f = function(){
36533                 if(!this.loading){ // done loading
36534                     clearInterval(timer);
36535                     this.expand(deep, anim, callback);
36536                 }
36537             }.createDelegate(this);
36538             timer = setInterval(f, 200);
36539             return;
36540         }
36541         if(!this.loaded){
36542             if(this.fireEvent("beforeload", this) === false){
36543                 return;
36544             }
36545             this.loading = true;
36546             this.ui.beforeLoad(this);
36547             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36548             if(loader){
36549                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36550                 return;
36551             }
36552         }
36553         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36554     },
36555     
36556     /**
36557      * Returns true if this node is currently loading
36558      * @return {Boolean}
36559      */
36560     isLoading : function(){
36561         return this.loading;  
36562     },
36563     
36564     loadComplete : function(deep, anim, callback){
36565         this.loading = false;
36566         this.loaded = true;
36567         this.ui.afterLoad(this);
36568         this.fireEvent("load", this);
36569         this.expand(deep, anim, callback);
36570     },
36571     
36572     /**
36573      * Returns true if this node has been loaded
36574      * @return {Boolean}
36575      */
36576     isLoaded : function(){
36577         return this.loaded;
36578     },
36579     
36580     hasChildNodes : function(){
36581         if(!this.isLeaf() && !this.loaded){
36582             return true;
36583         }else{
36584             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36585         }
36586     },
36587
36588     /**
36589      * Trigger a reload for this node
36590      * @param {Function} callback
36591      */
36592     reload : function(callback){
36593         this.collapse(false, false);
36594         while(this.firstChild){
36595             this.removeChild(this.firstChild);
36596         }
36597         this.childrenRendered = false;
36598         this.loaded = false;
36599         if(this.isHiddenRoot()){
36600             this.expanded = false;
36601         }
36602         this.expand(false, false, callback);
36603     }
36604 });/*
36605  * Based on:
36606  * Ext JS Library 1.1.1
36607  * Copyright(c) 2006-2007, Ext JS, LLC.
36608  *
36609  * Originally Released Under LGPL - original licence link has changed is not relivant.
36610  *
36611  * Fork - LGPL
36612  * <script type="text/javascript">
36613  */
36614  
36615 /**
36616  * @class Roo.tree.TreeNodeUI
36617  * @constructor
36618  * @param {Object} node The node to render
36619  * The TreeNode UI implementation is separate from the
36620  * tree implementation. Unless you are customizing the tree UI,
36621  * you should never have to use this directly.
36622  */
36623 Roo.tree.TreeNodeUI = function(node){
36624     this.node = node;
36625     this.rendered = false;
36626     this.animating = false;
36627     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36628 };
36629
36630 Roo.tree.TreeNodeUI.prototype = {
36631     removeChild : function(node){
36632         if(this.rendered){
36633             this.ctNode.removeChild(node.ui.getEl());
36634         }
36635     },
36636
36637     beforeLoad : function(){
36638          this.addClass("x-tree-node-loading");
36639     },
36640
36641     afterLoad : function(){
36642          this.removeClass("x-tree-node-loading");
36643     },
36644
36645     onTextChange : function(node, text, oldText){
36646         if(this.rendered){
36647             this.textNode.innerHTML = text;
36648         }
36649     },
36650
36651     onDisableChange : function(node, state){
36652         this.disabled = state;
36653         if(state){
36654             this.addClass("x-tree-node-disabled");
36655         }else{
36656             this.removeClass("x-tree-node-disabled");
36657         }
36658     },
36659
36660     onSelectedChange : function(state){
36661         if(state){
36662             this.focus();
36663             this.addClass("x-tree-selected");
36664         }else{
36665             //this.blur();
36666             this.removeClass("x-tree-selected");
36667         }
36668     },
36669
36670     onMove : function(tree, node, oldParent, newParent, index, refNode){
36671         this.childIndent = null;
36672         if(this.rendered){
36673             var targetNode = newParent.ui.getContainer();
36674             if(!targetNode){//target not rendered
36675                 this.holder = document.createElement("div");
36676                 this.holder.appendChild(this.wrap);
36677                 return;
36678             }
36679             var insertBefore = refNode ? refNode.ui.getEl() : null;
36680             if(insertBefore){
36681                 targetNode.insertBefore(this.wrap, insertBefore);
36682             }else{
36683                 targetNode.appendChild(this.wrap);
36684             }
36685             this.node.renderIndent(true);
36686         }
36687     },
36688
36689     addClass : function(cls){
36690         if(this.elNode){
36691             Roo.fly(this.elNode).addClass(cls);
36692         }
36693     },
36694
36695     removeClass : function(cls){
36696         if(this.elNode){
36697             Roo.fly(this.elNode).removeClass(cls);
36698         }
36699     },
36700
36701     remove : function(){
36702         if(this.rendered){
36703             this.holder = document.createElement("div");
36704             this.holder.appendChild(this.wrap);
36705         }
36706     },
36707
36708     fireEvent : function(){
36709         return this.node.fireEvent.apply(this.node, arguments);
36710     },
36711
36712     initEvents : function(){
36713         this.node.on("move", this.onMove, this);
36714         var E = Roo.EventManager;
36715         var a = this.anchor;
36716
36717         var el = Roo.fly(a, '_treeui');
36718
36719         if(Roo.isOpera){ // opera render bug ignores the CSS
36720             el.setStyle("text-decoration", "none");
36721         }
36722
36723         el.on("click", this.onClick, this);
36724         el.on("dblclick", this.onDblClick, this);
36725
36726         if(this.checkbox){
36727             Roo.EventManager.on(this.checkbox,
36728                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36729         }
36730
36731         el.on("contextmenu", this.onContextMenu, this);
36732
36733         var icon = Roo.fly(this.iconNode);
36734         icon.on("click", this.onClick, this);
36735         icon.on("dblclick", this.onDblClick, this);
36736         icon.on("contextmenu", this.onContextMenu, this);
36737         E.on(this.ecNode, "click", this.ecClick, this, true);
36738
36739         if(this.node.disabled){
36740             this.addClass("x-tree-node-disabled");
36741         }
36742         if(this.node.hidden){
36743             this.addClass("x-tree-node-disabled");
36744         }
36745         var ot = this.node.getOwnerTree();
36746         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36747         if(dd && (!this.node.isRoot || ot.rootVisible)){
36748             Roo.dd.Registry.register(this.elNode, {
36749                 node: this.node,
36750                 handles: this.getDDHandles(),
36751                 isHandle: false
36752             });
36753         }
36754     },
36755
36756     getDDHandles : function(){
36757         return [this.iconNode, this.textNode];
36758     },
36759
36760     hide : function(){
36761         if(this.rendered){
36762             this.wrap.style.display = "none";
36763         }
36764     },
36765
36766     show : function(){
36767         if(this.rendered){
36768             this.wrap.style.display = "";
36769         }
36770     },
36771
36772     onContextMenu : function(e){
36773         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36774             e.preventDefault();
36775             this.focus();
36776             this.fireEvent("contextmenu", this.node, e);
36777         }
36778     },
36779
36780     onClick : function(e){
36781         if(this.dropping){
36782             e.stopEvent();
36783             return;
36784         }
36785         if(this.fireEvent("beforeclick", this.node, e) !== false){
36786             if(!this.disabled && this.node.attributes.href){
36787                 this.fireEvent("click", this.node, e);
36788                 return;
36789             }
36790             e.preventDefault();
36791             if(this.disabled){
36792                 return;
36793             }
36794
36795             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36796                 this.node.toggle();
36797             }
36798
36799             this.fireEvent("click", this.node, e);
36800         }else{
36801             e.stopEvent();
36802         }
36803     },
36804
36805     onDblClick : function(e){
36806         e.preventDefault();
36807         if(this.disabled){
36808             return;
36809         }
36810         if(this.checkbox){
36811             this.toggleCheck();
36812         }
36813         if(!this.animating && this.node.hasChildNodes()){
36814             this.node.toggle();
36815         }
36816         this.fireEvent("dblclick", this.node, e);
36817     },
36818
36819     onCheckChange : function(){
36820         var checked = this.checkbox.checked;
36821         this.node.attributes.checked = checked;
36822         this.fireEvent('checkchange', this.node, checked);
36823     },
36824
36825     ecClick : function(e){
36826         if(!this.animating && this.node.hasChildNodes()){
36827             this.node.toggle();
36828         }
36829     },
36830
36831     startDrop : function(){
36832         this.dropping = true;
36833     },
36834
36835     // delayed drop so the click event doesn't get fired on a drop
36836     endDrop : function(){
36837        setTimeout(function(){
36838            this.dropping = false;
36839        }.createDelegate(this), 50);
36840     },
36841
36842     expand : function(){
36843         this.updateExpandIcon();
36844         this.ctNode.style.display = "";
36845     },
36846
36847     focus : function(){
36848         if(!this.node.preventHScroll){
36849             try{this.anchor.focus();
36850             }catch(e){}
36851         }else if(!Roo.isIE){
36852             try{
36853                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36854                 var l = noscroll.scrollLeft;
36855                 this.anchor.focus();
36856                 noscroll.scrollLeft = l;
36857             }catch(e){}
36858         }
36859     },
36860
36861     toggleCheck : function(value){
36862         var cb = this.checkbox;
36863         if(cb){
36864             cb.checked = (value === undefined ? !cb.checked : value);
36865         }
36866     },
36867
36868     blur : function(){
36869         try{
36870             this.anchor.blur();
36871         }catch(e){}
36872     },
36873
36874     animExpand : function(callback){
36875         var ct = Roo.get(this.ctNode);
36876         ct.stopFx();
36877         if(!this.node.hasChildNodes()){
36878             this.updateExpandIcon();
36879             this.ctNode.style.display = "";
36880             Roo.callback(callback);
36881             return;
36882         }
36883         this.animating = true;
36884         this.updateExpandIcon();
36885
36886         ct.slideIn('t', {
36887            callback : function(){
36888                this.animating = false;
36889                Roo.callback(callback);
36890             },
36891             scope: this,
36892             duration: this.node.ownerTree.duration || .25
36893         });
36894     },
36895
36896     highlight : function(){
36897         var tree = this.node.getOwnerTree();
36898         Roo.fly(this.wrap).highlight(
36899             tree.hlColor || "C3DAF9",
36900             {endColor: tree.hlBaseColor}
36901         );
36902     },
36903
36904     collapse : function(){
36905         this.updateExpandIcon();
36906         this.ctNode.style.display = "none";
36907     },
36908
36909     animCollapse : function(callback){
36910         var ct = Roo.get(this.ctNode);
36911         ct.enableDisplayMode('block');
36912         ct.stopFx();
36913
36914         this.animating = true;
36915         this.updateExpandIcon();
36916
36917         ct.slideOut('t', {
36918             callback : function(){
36919                this.animating = false;
36920                Roo.callback(callback);
36921             },
36922             scope: this,
36923             duration: this.node.ownerTree.duration || .25
36924         });
36925     },
36926
36927     getContainer : function(){
36928         return this.ctNode;
36929     },
36930
36931     getEl : function(){
36932         return this.wrap;
36933     },
36934
36935     appendDDGhost : function(ghostNode){
36936         ghostNode.appendChild(this.elNode.cloneNode(true));
36937     },
36938
36939     getDDRepairXY : function(){
36940         return Roo.lib.Dom.getXY(this.iconNode);
36941     },
36942
36943     onRender : function(){
36944         this.render();
36945     },
36946
36947     render : function(bulkRender){
36948         var n = this.node, a = n.attributes;
36949         var targetNode = n.parentNode ?
36950               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36951
36952         if(!this.rendered){
36953             this.rendered = true;
36954
36955             this.renderElements(n, a, targetNode, bulkRender);
36956
36957             if(a.qtip){
36958                if(this.textNode.setAttributeNS){
36959                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36960                    if(a.qtipTitle){
36961                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36962                    }
36963                }else{
36964                    this.textNode.setAttribute("ext:qtip", a.qtip);
36965                    if(a.qtipTitle){
36966                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36967                    }
36968                }
36969             }else if(a.qtipCfg){
36970                 a.qtipCfg.target = Roo.id(this.textNode);
36971                 Roo.QuickTips.register(a.qtipCfg);
36972             }
36973             this.initEvents();
36974             if(!this.node.expanded){
36975                 this.updateExpandIcon();
36976             }
36977         }else{
36978             if(bulkRender === true) {
36979                 targetNode.appendChild(this.wrap);
36980             }
36981         }
36982     },
36983
36984     renderElements : function(n, a, targetNode, bulkRender)
36985     {
36986         // add some indent caching, this helps performance when rendering a large tree
36987         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36988         var t = n.getOwnerTree();
36989         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36990         if (typeof(n.attributes.html) != 'undefined') {
36991             txt = n.attributes.html;
36992         }
36993         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36994         var cb = typeof a.checked == 'boolean';
36995         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36996         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36997             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36998             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36999             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37000             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37001             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37002              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37003                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37004             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37005             "</li>"];
37006
37007         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37008             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37009                                 n.nextSibling.ui.getEl(), buf.join(""));
37010         }else{
37011             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37012         }
37013
37014         this.elNode = this.wrap.childNodes[0];
37015         this.ctNode = this.wrap.childNodes[1];
37016         var cs = this.elNode.childNodes;
37017         this.indentNode = cs[0];
37018         this.ecNode = cs[1];
37019         this.iconNode = cs[2];
37020         var index = 3;
37021         if(cb){
37022             this.checkbox = cs[3];
37023             index++;
37024         }
37025         this.anchor = cs[index];
37026         this.textNode = cs[index].firstChild;
37027     },
37028
37029     getAnchor : function(){
37030         return this.anchor;
37031     },
37032
37033     getTextEl : function(){
37034         return this.textNode;
37035     },
37036
37037     getIconEl : function(){
37038         return this.iconNode;
37039     },
37040
37041     isChecked : function(){
37042         return this.checkbox ? this.checkbox.checked : false;
37043     },
37044
37045     updateExpandIcon : function(){
37046         if(this.rendered){
37047             var n = this.node, c1, c2;
37048             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37049             var hasChild = n.hasChildNodes();
37050             if(hasChild){
37051                 if(n.expanded){
37052                     cls += "-minus";
37053                     c1 = "x-tree-node-collapsed";
37054                     c2 = "x-tree-node-expanded";
37055                 }else{
37056                     cls += "-plus";
37057                     c1 = "x-tree-node-expanded";
37058                     c2 = "x-tree-node-collapsed";
37059                 }
37060                 if(this.wasLeaf){
37061                     this.removeClass("x-tree-node-leaf");
37062                     this.wasLeaf = false;
37063                 }
37064                 if(this.c1 != c1 || this.c2 != c2){
37065                     Roo.fly(this.elNode).replaceClass(c1, c2);
37066                     this.c1 = c1; this.c2 = c2;
37067                 }
37068             }else{
37069                 // this changes non-leafs into leafs if they have no children.
37070                 // it's not very rational behaviour..
37071                 
37072                 if(!this.wasLeaf && this.node.leaf){
37073                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37074                     delete this.c1;
37075                     delete this.c2;
37076                     this.wasLeaf = true;
37077                 }
37078             }
37079             var ecc = "x-tree-ec-icon "+cls;
37080             if(this.ecc != ecc){
37081                 this.ecNode.className = ecc;
37082                 this.ecc = ecc;
37083             }
37084         }
37085     },
37086
37087     getChildIndent : function(){
37088         if(!this.childIndent){
37089             var buf = [];
37090             var p = this.node;
37091             while(p){
37092                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37093                     if(!p.isLast()) {
37094                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37095                     } else {
37096                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37097                     }
37098                 }
37099                 p = p.parentNode;
37100             }
37101             this.childIndent = buf.join("");
37102         }
37103         return this.childIndent;
37104     },
37105
37106     renderIndent : function(){
37107         if(this.rendered){
37108             var indent = "";
37109             var p = this.node.parentNode;
37110             if(p){
37111                 indent = p.ui.getChildIndent();
37112             }
37113             if(this.indentMarkup != indent){ // don't rerender if not required
37114                 this.indentNode.innerHTML = indent;
37115                 this.indentMarkup = indent;
37116             }
37117             this.updateExpandIcon();
37118         }
37119     }
37120 };
37121
37122 Roo.tree.RootTreeNodeUI = function(){
37123     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37124 };
37125 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37126     render : function(){
37127         if(!this.rendered){
37128             var targetNode = this.node.ownerTree.innerCt.dom;
37129             this.node.expanded = true;
37130             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37131             this.wrap = this.ctNode = targetNode.firstChild;
37132         }
37133     },
37134     collapse : function(){
37135     },
37136     expand : function(){
37137     }
37138 });/*
37139  * Based on:
37140  * Ext JS Library 1.1.1
37141  * Copyright(c) 2006-2007, Ext JS, LLC.
37142  *
37143  * Originally Released Under LGPL - original licence link has changed is not relivant.
37144  *
37145  * Fork - LGPL
37146  * <script type="text/javascript">
37147  */
37148 /**
37149  * @class Roo.tree.TreeLoader
37150  * @extends Roo.util.Observable
37151  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37152  * nodes from a specified URL. The response must be a javascript Array definition
37153  * who's elements are node definition objects. eg:
37154  * <pre><code>
37155 {  success : true,
37156    data :      [
37157    
37158     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37159     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37160     ]
37161 }
37162
37163
37164 </code></pre>
37165  * <br><br>
37166  * The old style respose with just an array is still supported, but not recommended.
37167  * <br><br>
37168  *
37169  * A server request is sent, and child nodes are loaded only when a node is expanded.
37170  * The loading node's id is passed to the server under the parameter name "node" to
37171  * enable the server to produce the correct child nodes.
37172  * <br><br>
37173  * To pass extra parameters, an event handler may be attached to the "beforeload"
37174  * event, and the parameters specified in the TreeLoader's baseParams property:
37175  * <pre><code>
37176     myTreeLoader.on("beforeload", function(treeLoader, node) {
37177         this.baseParams.category = node.attributes.category;
37178     }, this);
37179     
37180 </code></pre>
37181  *
37182  * This would pass an HTTP parameter called "category" to the server containing
37183  * the value of the Node's "category" attribute.
37184  * @constructor
37185  * Creates a new Treeloader.
37186  * @param {Object} config A config object containing config properties.
37187  */
37188 Roo.tree.TreeLoader = function(config){
37189     this.baseParams = {};
37190     this.requestMethod = "POST";
37191     Roo.apply(this, config);
37192
37193     this.addEvents({
37194     
37195         /**
37196          * @event beforeload
37197          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37198          * @param {Object} This TreeLoader object.
37199          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37200          * @param {Object} callback The callback function specified in the {@link #load} call.
37201          */
37202         beforeload : true,
37203         /**
37204          * @event load
37205          * Fires when the node has been successfuly loaded.
37206          * @param {Object} This TreeLoader object.
37207          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37208          * @param {Object} response The response object containing the data from the server.
37209          */
37210         load : true,
37211         /**
37212          * @event loadexception
37213          * Fires if the network request failed.
37214          * @param {Object} This TreeLoader object.
37215          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37216          * @param {Object} response The response object containing the data from the server.
37217          */
37218         loadexception : true,
37219         /**
37220          * @event create
37221          * Fires before a node is created, enabling you to return custom Node types 
37222          * @param {Object} This TreeLoader object.
37223          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37224          */
37225         create : true
37226     });
37227
37228     Roo.tree.TreeLoader.superclass.constructor.call(this);
37229 };
37230
37231 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37232     /**
37233     * @cfg {String} dataUrl The URL from which to request a Json string which
37234     * specifies an array of node definition object representing the child nodes
37235     * to be loaded.
37236     */
37237     /**
37238     * @cfg {String} requestMethod either GET or POST
37239     * defaults to POST (due to BC)
37240     * to be loaded.
37241     */
37242     /**
37243     * @cfg {Object} baseParams (optional) An object containing properties which
37244     * specify HTTP parameters to be passed to each request for child nodes.
37245     */
37246     /**
37247     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37248     * created by this loader. If the attributes sent by the server have an attribute in this object,
37249     * they take priority.
37250     */
37251     /**
37252     * @cfg {Object} uiProviders (optional) An object containing properties which
37253     * 
37254     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37255     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37256     * <i>uiProvider</i> attribute of a returned child node is a string rather
37257     * than a reference to a TreeNodeUI implementation, this that string value
37258     * is used as a property name in the uiProviders object. You can define the provider named
37259     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37260     */
37261     uiProviders : {},
37262
37263     /**
37264     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37265     * child nodes before loading.
37266     */
37267     clearOnLoad : true,
37268
37269     /**
37270     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37271     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37272     * Grid query { data : [ .....] }
37273     */
37274     
37275     root : false,
37276      /**
37277     * @cfg {String} queryParam (optional) 
37278     * Name of the query as it will be passed on the querystring (defaults to 'node')
37279     * eg. the request will be ?node=[id]
37280     */
37281     
37282     
37283     queryParam: false,
37284     
37285     /**
37286      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37287      * This is called automatically when a node is expanded, but may be used to reload
37288      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37289      * @param {Roo.tree.TreeNode} node
37290      * @param {Function} callback
37291      */
37292     load : function(node, callback){
37293         if(this.clearOnLoad){
37294             while(node.firstChild){
37295                 node.removeChild(node.firstChild);
37296             }
37297         }
37298         if(node.attributes.children){ // preloaded json children
37299             var cs = node.attributes.children;
37300             for(var i = 0, len = cs.length; i < len; i++){
37301                 node.appendChild(this.createNode(cs[i]));
37302             }
37303             if(typeof callback == "function"){
37304                 callback();
37305             }
37306         }else if(this.dataUrl){
37307             this.requestData(node, callback);
37308         }
37309     },
37310
37311     getParams: function(node){
37312         var buf = [], bp = this.baseParams;
37313         for(var key in bp){
37314             if(typeof bp[key] != "function"){
37315                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37316             }
37317         }
37318         var n = this.queryParam === false ? 'node' : this.queryParam;
37319         buf.push(n + "=", encodeURIComponent(node.id));
37320         return buf.join("");
37321     },
37322
37323     requestData : function(node, callback){
37324         if(this.fireEvent("beforeload", this, node, callback) !== false){
37325             this.transId = Roo.Ajax.request({
37326                 method:this.requestMethod,
37327                 url: this.dataUrl||this.url,
37328                 success: this.handleResponse,
37329                 failure: this.handleFailure,
37330                 scope: this,
37331                 argument: {callback: callback, node: node},
37332                 params: this.getParams(node)
37333             });
37334         }else{
37335             // if the load is cancelled, make sure we notify
37336             // the node that we are done
37337             if(typeof callback == "function"){
37338                 callback();
37339             }
37340         }
37341     },
37342
37343     isLoading : function(){
37344         return this.transId ? true : false;
37345     },
37346
37347     abort : function(){
37348         if(this.isLoading()){
37349             Roo.Ajax.abort(this.transId);
37350         }
37351     },
37352
37353     // private
37354     createNode : function(attr)
37355     {
37356         // apply baseAttrs, nice idea Corey!
37357         if(this.baseAttrs){
37358             Roo.applyIf(attr, this.baseAttrs);
37359         }
37360         if(this.applyLoader !== false){
37361             attr.loader = this;
37362         }
37363         // uiProvider = depreciated..
37364         
37365         if(typeof(attr.uiProvider) == 'string'){
37366            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37367                 /**  eval:var:attr */ eval(attr.uiProvider);
37368         }
37369         if(typeof(this.uiProviders['default']) != 'undefined') {
37370             attr.uiProvider = this.uiProviders['default'];
37371         }
37372         
37373         this.fireEvent('create', this, attr);
37374         
37375         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37376         return(attr.leaf ?
37377                         new Roo.tree.TreeNode(attr) :
37378                         new Roo.tree.AsyncTreeNode(attr));
37379     },
37380
37381     processResponse : function(response, node, callback)
37382     {
37383         var json = response.responseText;
37384         try {
37385             
37386             var o = Roo.decode(json);
37387             
37388             if (this.root === false && typeof(o.success) != undefined) {
37389                 this.root = 'data'; // the default behaviour for list like data..
37390                 }
37391                 
37392             if (this.root !== false &&  !o.success) {
37393                 // it's a failure condition.
37394                 var a = response.argument;
37395                 this.fireEvent("loadexception", this, a.node, response);
37396                 Roo.log("Load failed - should have a handler really");
37397                 return;
37398             }
37399             
37400             
37401             
37402             if (this.root !== false) {
37403                  o = o[this.root];
37404             }
37405             
37406             for(var i = 0, len = o.length; i < len; i++){
37407                 var n = this.createNode(o[i]);
37408                 if(n){
37409                     node.appendChild(n);
37410                 }
37411             }
37412             if(typeof callback == "function"){
37413                 callback(this, node);
37414             }
37415         }catch(e){
37416             this.handleFailure(response);
37417         }
37418     },
37419
37420     handleResponse : function(response){
37421         this.transId = false;
37422         var a = response.argument;
37423         this.processResponse(response, a.node, a.callback);
37424         this.fireEvent("load", this, a.node, response);
37425     },
37426
37427     handleFailure : function(response)
37428     {
37429         // should handle failure better..
37430         this.transId = false;
37431         var a = response.argument;
37432         this.fireEvent("loadexception", this, a.node, response);
37433         if(typeof a.callback == "function"){
37434             a.callback(this, a.node);
37435         }
37436     }
37437 });/*
37438  * Based on:
37439  * Ext JS Library 1.1.1
37440  * Copyright(c) 2006-2007, Ext JS, LLC.
37441  *
37442  * Originally Released Under LGPL - original licence link has changed is not relivant.
37443  *
37444  * Fork - LGPL
37445  * <script type="text/javascript">
37446  */
37447
37448 /**
37449 * @class Roo.tree.TreeFilter
37450 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37451 * @param {TreePanel} tree
37452 * @param {Object} config (optional)
37453  */
37454 Roo.tree.TreeFilter = function(tree, config){
37455     this.tree = tree;
37456     this.filtered = {};
37457     Roo.apply(this, config);
37458 };
37459
37460 Roo.tree.TreeFilter.prototype = {
37461     clearBlank:false,
37462     reverse:false,
37463     autoClear:false,
37464     remove:false,
37465
37466      /**
37467      * Filter the data by a specific attribute.
37468      * @param {String/RegExp} value Either string that the attribute value
37469      * should start with or a RegExp to test against the attribute
37470      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37471      * @param {TreeNode} startNode (optional) The node to start the filter at.
37472      */
37473     filter : function(value, attr, startNode){
37474         attr = attr || "text";
37475         var f;
37476         if(typeof value == "string"){
37477             var vlen = value.length;
37478             // auto clear empty filter
37479             if(vlen == 0 && this.clearBlank){
37480                 this.clear();
37481                 return;
37482             }
37483             value = value.toLowerCase();
37484             f = function(n){
37485                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37486             };
37487         }else if(value.exec){ // regex?
37488             f = function(n){
37489                 return value.test(n.attributes[attr]);
37490             };
37491         }else{
37492             throw 'Illegal filter type, must be string or regex';
37493         }
37494         this.filterBy(f, null, startNode);
37495         },
37496
37497     /**
37498      * Filter by a function. The passed function will be called with each
37499      * node in the tree (or from the startNode). If the function returns true, the node is kept
37500      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37501      * @param {Function} fn The filter function
37502      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37503      */
37504     filterBy : function(fn, scope, startNode){
37505         startNode = startNode || this.tree.root;
37506         if(this.autoClear){
37507             this.clear();
37508         }
37509         var af = this.filtered, rv = this.reverse;
37510         var f = function(n){
37511             if(n == startNode){
37512                 return true;
37513             }
37514             if(af[n.id]){
37515                 return false;
37516             }
37517             var m = fn.call(scope || n, n);
37518             if(!m || rv){
37519                 af[n.id] = n;
37520                 n.ui.hide();
37521                 return false;
37522             }
37523             return true;
37524         };
37525         startNode.cascade(f);
37526         if(this.remove){
37527            for(var id in af){
37528                if(typeof id != "function"){
37529                    var n = af[id];
37530                    if(n && n.parentNode){
37531                        n.parentNode.removeChild(n);
37532                    }
37533                }
37534            }
37535         }
37536     },
37537
37538     /**
37539      * Clears the current filter. Note: with the "remove" option
37540      * set a filter cannot be cleared.
37541      */
37542     clear : function(){
37543         var t = this.tree;
37544         var af = this.filtered;
37545         for(var id in af){
37546             if(typeof id != "function"){
37547                 var n = af[id];
37548                 if(n){
37549                     n.ui.show();
37550                 }
37551             }
37552         }
37553         this.filtered = {};
37554     }
37555 };
37556 /*
37557  * Based on:
37558  * Ext JS Library 1.1.1
37559  * Copyright(c) 2006-2007, Ext JS, LLC.
37560  *
37561  * Originally Released Under LGPL - original licence link has changed is not relivant.
37562  *
37563  * Fork - LGPL
37564  * <script type="text/javascript">
37565  */
37566  
37567
37568 /**
37569  * @class Roo.tree.TreeSorter
37570  * Provides sorting of nodes in a TreePanel
37571  * 
37572  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37573  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37574  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37575  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37576  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37577  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37578  * @constructor
37579  * @param {TreePanel} tree
37580  * @param {Object} config
37581  */
37582 Roo.tree.TreeSorter = function(tree, config){
37583     Roo.apply(this, config);
37584     tree.on("beforechildrenrendered", this.doSort, this);
37585     tree.on("append", this.updateSort, this);
37586     tree.on("insert", this.updateSort, this);
37587     
37588     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37589     var p = this.property || "text";
37590     var sortType = this.sortType;
37591     var fs = this.folderSort;
37592     var cs = this.caseSensitive === true;
37593     var leafAttr = this.leafAttr || 'leaf';
37594
37595     this.sortFn = function(n1, n2){
37596         if(fs){
37597             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37598                 return 1;
37599             }
37600             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37601                 return -1;
37602             }
37603         }
37604         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37605         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37606         if(v1 < v2){
37607                         return dsc ? +1 : -1;
37608                 }else if(v1 > v2){
37609                         return dsc ? -1 : +1;
37610         }else{
37611                 return 0;
37612         }
37613     };
37614 };
37615
37616 Roo.tree.TreeSorter.prototype = {
37617     doSort : function(node){
37618         node.sort(this.sortFn);
37619     },
37620     
37621     compareNodes : function(n1, n2){
37622         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37623     },
37624     
37625     updateSort : function(tree, node){
37626         if(node.childrenRendered){
37627             this.doSort.defer(1, this, [node]);
37628         }
37629     }
37630 };/*
37631  * Based on:
37632  * Ext JS Library 1.1.1
37633  * Copyright(c) 2006-2007, Ext JS, LLC.
37634  *
37635  * Originally Released Under LGPL - original licence link has changed is not relivant.
37636  *
37637  * Fork - LGPL
37638  * <script type="text/javascript">
37639  */
37640
37641 if(Roo.dd.DropZone){
37642     
37643 Roo.tree.TreeDropZone = function(tree, config){
37644     this.allowParentInsert = false;
37645     this.allowContainerDrop = false;
37646     this.appendOnly = false;
37647     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37648     this.tree = tree;
37649     this.lastInsertClass = "x-tree-no-status";
37650     this.dragOverData = {};
37651 };
37652
37653 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37654     ddGroup : "TreeDD",
37655     scroll:  true,
37656     
37657     expandDelay : 1000,
37658     
37659     expandNode : function(node){
37660         if(node.hasChildNodes() && !node.isExpanded()){
37661             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37662         }
37663     },
37664     
37665     queueExpand : function(node){
37666         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37667     },
37668     
37669     cancelExpand : function(){
37670         if(this.expandProcId){
37671             clearTimeout(this.expandProcId);
37672             this.expandProcId = false;
37673         }
37674     },
37675     
37676     isValidDropPoint : function(n, pt, dd, e, data){
37677         if(!n || !data){ return false; }
37678         var targetNode = n.node;
37679         var dropNode = data.node;
37680         // default drop rules
37681         if(!(targetNode && targetNode.isTarget && pt)){
37682             return false;
37683         }
37684         if(pt == "append" && targetNode.allowChildren === false){
37685             return false;
37686         }
37687         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37688             return false;
37689         }
37690         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37691             return false;
37692         }
37693         // reuse the object
37694         var overEvent = this.dragOverData;
37695         overEvent.tree = this.tree;
37696         overEvent.target = targetNode;
37697         overEvent.data = data;
37698         overEvent.point = pt;
37699         overEvent.source = dd;
37700         overEvent.rawEvent = e;
37701         overEvent.dropNode = dropNode;
37702         overEvent.cancel = false;  
37703         var result = this.tree.fireEvent("nodedragover", overEvent);
37704         return overEvent.cancel === false && result !== false;
37705     },
37706     
37707     getDropPoint : function(e, n, dd)
37708     {
37709         var tn = n.node;
37710         if(tn.isRoot){
37711             return tn.allowChildren !== false ? "append" : false; // always append for root
37712         }
37713         var dragEl = n.ddel;
37714         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37715         var y = Roo.lib.Event.getPageY(e);
37716         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37717         
37718         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37719         var noAppend = tn.allowChildren === false;
37720         if(this.appendOnly || tn.parentNode.allowChildren === false){
37721             return noAppend ? false : "append";
37722         }
37723         var noBelow = false;
37724         if(!this.allowParentInsert){
37725             noBelow = tn.hasChildNodes() && tn.isExpanded();
37726         }
37727         var q = (b - t) / (noAppend ? 2 : 3);
37728         if(y >= t && y < (t + q)){
37729             return "above";
37730         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37731             return "below";
37732         }else{
37733             return "append";
37734         }
37735     },
37736     
37737     onNodeEnter : function(n, dd, e, data)
37738     {
37739         this.cancelExpand();
37740     },
37741     
37742     onNodeOver : function(n, dd, e, data)
37743     {
37744        
37745         var pt = this.getDropPoint(e, n, dd);
37746         var node = n.node;
37747         
37748         // auto node expand check
37749         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37750             this.queueExpand(node);
37751         }else if(pt != "append"){
37752             this.cancelExpand();
37753         }
37754         
37755         // set the insert point style on the target node
37756         var returnCls = this.dropNotAllowed;
37757         if(this.isValidDropPoint(n, pt, dd, e, data)){
37758            if(pt){
37759                var el = n.ddel;
37760                var cls;
37761                if(pt == "above"){
37762                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37763                    cls = "x-tree-drag-insert-above";
37764                }else if(pt == "below"){
37765                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37766                    cls = "x-tree-drag-insert-below";
37767                }else{
37768                    returnCls = "x-tree-drop-ok-append";
37769                    cls = "x-tree-drag-append";
37770                }
37771                if(this.lastInsertClass != cls){
37772                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37773                    this.lastInsertClass = cls;
37774                }
37775            }
37776        }
37777        return returnCls;
37778     },
37779     
37780     onNodeOut : function(n, dd, e, data){
37781         
37782         this.cancelExpand();
37783         this.removeDropIndicators(n);
37784     },
37785     
37786     onNodeDrop : function(n, dd, e, data){
37787         var point = this.getDropPoint(e, n, dd);
37788         var targetNode = n.node;
37789         targetNode.ui.startDrop();
37790         if(!this.isValidDropPoint(n, point, dd, e, data)){
37791             targetNode.ui.endDrop();
37792             return false;
37793         }
37794         // first try to find the drop node
37795         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37796         var dropEvent = {
37797             tree : this.tree,
37798             target: targetNode,
37799             data: data,
37800             point: point,
37801             source: dd,
37802             rawEvent: e,
37803             dropNode: dropNode,
37804             cancel: !dropNode   
37805         };
37806         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37807         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37808             targetNode.ui.endDrop();
37809             return false;
37810         }
37811         // allow target changing
37812         targetNode = dropEvent.target;
37813         if(point == "append" && !targetNode.isExpanded()){
37814             targetNode.expand(false, null, function(){
37815                 this.completeDrop(dropEvent);
37816             }.createDelegate(this));
37817         }else{
37818             this.completeDrop(dropEvent);
37819         }
37820         return true;
37821     },
37822     
37823     completeDrop : function(de){
37824         var ns = de.dropNode, p = de.point, t = de.target;
37825         if(!(ns instanceof Array)){
37826             ns = [ns];
37827         }
37828         var n;
37829         for(var i = 0, len = ns.length; i < len; i++){
37830             n = ns[i];
37831             if(p == "above"){
37832                 t.parentNode.insertBefore(n, t);
37833             }else if(p == "below"){
37834                 t.parentNode.insertBefore(n, t.nextSibling);
37835             }else{
37836                 t.appendChild(n);
37837             }
37838         }
37839         n.ui.focus();
37840         if(this.tree.hlDrop){
37841             n.ui.highlight();
37842         }
37843         t.ui.endDrop();
37844         this.tree.fireEvent("nodedrop", de);
37845     },
37846     
37847     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37848         if(this.tree.hlDrop){
37849             dropNode.ui.focus();
37850             dropNode.ui.highlight();
37851         }
37852         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37853     },
37854     
37855     getTree : function(){
37856         return this.tree;
37857     },
37858     
37859     removeDropIndicators : function(n){
37860         if(n && n.ddel){
37861             var el = n.ddel;
37862             Roo.fly(el).removeClass([
37863                     "x-tree-drag-insert-above",
37864                     "x-tree-drag-insert-below",
37865                     "x-tree-drag-append"]);
37866             this.lastInsertClass = "_noclass";
37867         }
37868     },
37869     
37870     beforeDragDrop : function(target, e, id){
37871         this.cancelExpand();
37872         return true;
37873     },
37874     
37875     afterRepair : function(data){
37876         if(data && Roo.enableFx){
37877             data.node.ui.highlight();
37878         }
37879         this.hideProxy();
37880     } 
37881     
37882 });
37883
37884 }
37885 /*
37886  * Based on:
37887  * Ext JS Library 1.1.1
37888  * Copyright(c) 2006-2007, Ext JS, LLC.
37889  *
37890  * Originally Released Under LGPL - original licence link has changed is not relivant.
37891  *
37892  * Fork - LGPL
37893  * <script type="text/javascript">
37894  */
37895  
37896
37897 if(Roo.dd.DragZone){
37898 Roo.tree.TreeDragZone = function(tree, config){
37899     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37900     this.tree = tree;
37901 };
37902
37903 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37904     ddGroup : "TreeDD",
37905    
37906     onBeforeDrag : function(data, e){
37907         var n = data.node;
37908         return n && n.draggable && !n.disabled;
37909     },
37910      
37911     
37912     onInitDrag : function(e){
37913         var data = this.dragData;
37914         this.tree.getSelectionModel().select(data.node);
37915         this.proxy.update("");
37916         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37917         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37918     },
37919     
37920     getRepairXY : function(e, data){
37921         return data.node.ui.getDDRepairXY();
37922     },
37923     
37924     onEndDrag : function(data, e){
37925         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37926         
37927         
37928     },
37929     
37930     onValidDrop : function(dd, e, id){
37931         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37932         this.hideProxy();
37933     },
37934     
37935     beforeInvalidDrop : function(e, id){
37936         // this scrolls the original position back into view
37937         var sm = this.tree.getSelectionModel();
37938         sm.clearSelections();
37939         sm.select(this.dragData.node);
37940     }
37941 });
37942 }/*
37943  * Based on:
37944  * Ext JS Library 1.1.1
37945  * Copyright(c) 2006-2007, Ext JS, LLC.
37946  *
37947  * Originally Released Under LGPL - original licence link has changed is not relivant.
37948  *
37949  * Fork - LGPL
37950  * <script type="text/javascript">
37951  */
37952 /**
37953  * @class Roo.tree.TreeEditor
37954  * @extends Roo.Editor
37955  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37956  * as the editor field.
37957  * @constructor
37958  * @param {Object} config (used to be the tree panel.)
37959  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37960  * 
37961  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37962  * @cfg {Roo.form.TextField} field [required] The field configuration
37963  *
37964  * 
37965  */
37966 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37967     var tree = config;
37968     var field;
37969     if (oldconfig) { // old style..
37970         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37971     } else {
37972         // new style..
37973         tree = config.tree;
37974         config.field = config.field  || {};
37975         config.field.xtype = 'TextField';
37976         field = Roo.factory(config.field, Roo.form);
37977     }
37978     config = config || {};
37979     
37980     
37981     this.addEvents({
37982         /**
37983          * @event beforenodeedit
37984          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37985          * false from the handler of this event.
37986          * @param {Editor} this
37987          * @param {Roo.tree.Node} node 
37988          */
37989         "beforenodeedit" : true
37990     });
37991     
37992     //Roo.log(config);
37993     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37994
37995     this.tree = tree;
37996
37997     tree.on('beforeclick', this.beforeNodeClick, this);
37998     tree.getTreeEl().on('mousedown', this.hide, this);
37999     this.on('complete', this.updateNode, this);
38000     this.on('beforestartedit', this.fitToTree, this);
38001     this.on('startedit', this.bindScroll, this, {delay:10});
38002     this.on('specialkey', this.onSpecialKey, this);
38003 };
38004
38005 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38006     /**
38007      * @cfg {String} alignment
38008      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38009      */
38010     alignment: "l-l",
38011     // inherit
38012     autoSize: false,
38013     /**
38014      * @cfg {Boolean} hideEl
38015      * True to hide the bound element while the editor is displayed (defaults to false)
38016      */
38017     hideEl : false,
38018     /**
38019      * @cfg {String} cls
38020      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38021      */
38022     cls: "x-small-editor x-tree-editor",
38023     /**
38024      * @cfg {Boolean} shim
38025      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38026      */
38027     shim:false,
38028     // inherit
38029     shadow:"frame",
38030     /**
38031      * @cfg {Number} maxWidth
38032      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38033      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38034      * scroll and client offsets into account prior to each edit.
38035      */
38036     maxWidth: 250,
38037
38038     editDelay : 350,
38039
38040     // private
38041     fitToTree : function(ed, el){
38042         var td = this.tree.getTreeEl().dom, nd = el.dom;
38043         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38044             td.scrollLeft = nd.offsetLeft;
38045         }
38046         var w = Math.min(
38047                 this.maxWidth,
38048                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38049         this.setSize(w, '');
38050         
38051         return this.fireEvent('beforenodeedit', this, this.editNode);
38052         
38053     },
38054
38055     // private
38056     triggerEdit : function(node){
38057         this.completeEdit();
38058         this.editNode = node;
38059         this.startEdit(node.ui.textNode, node.text);
38060     },
38061
38062     // private
38063     bindScroll : function(){
38064         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38065     },
38066
38067     // private
38068     beforeNodeClick : function(node, e){
38069         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38070         this.lastClick = new Date();
38071         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38072             e.stopEvent();
38073             this.triggerEdit(node);
38074             return false;
38075         }
38076         return true;
38077     },
38078
38079     // private
38080     updateNode : function(ed, value){
38081         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38082         this.editNode.setText(value);
38083     },
38084
38085     // private
38086     onHide : function(){
38087         Roo.tree.TreeEditor.superclass.onHide.call(this);
38088         if(this.editNode){
38089             this.editNode.ui.focus();
38090         }
38091     },
38092
38093     // private
38094     onSpecialKey : function(field, e){
38095         var k = e.getKey();
38096         if(k == e.ESC){
38097             e.stopEvent();
38098             this.cancelEdit();
38099         }else if(k == e.ENTER && !e.hasModifier()){
38100             e.stopEvent();
38101             this.completeEdit();
38102         }
38103     }
38104 });//<Script type="text/javascript">
38105 /*
38106  * Based on:
38107  * Ext JS Library 1.1.1
38108  * Copyright(c) 2006-2007, Ext JS, LLC.
38109  *
38110  * Originally Released Under LGPL - original licence link has changed is not relivant.
38111  *
38112  * Fork - LGPL
38113  * <script type="text/javascript">
38114  */
38115  
38116 /**
38117  * Not documented??? - probably should be...
38118  */
38119
38120 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38121     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38122     
38123     renderElements : function(n, a, targetNode, bulkRender){
38124         //consel.log("renderElements?");
38125         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38126
38127         var t = n.getOwnerTree();
38128         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38129         
38130         var cols = t.columns;
38131         var bw = t.borderWidth;
38132         var c = cols[0];
38133         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38134          var cb = typeof a.checked == "boolean";
38135         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38136         var colcls = 'x-t-' + tid + '-c0';
38137         var buf = [
38138             '<li class="x-tree-node">',
38139             
38140                 
38141                 '<div class="x-tree-node-el ', a.cls,'">',
38142                     // extran...
38143                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38144                 
38145                 
38146                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38147                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38148                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38149                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38150                            (a.iconCls ? ' '+a.iconCls : ''),
38151                            '" unselectable="on" />',
38152                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38153                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38154                              
38155                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38156                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38157                             '<span unselectable="on" qtip="' + tx + '">',
38158                              tx,
38159                              '</span></a>' ,
38160                     '</div>',
38161                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38162                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38163                  ];
38164         for(var i = 1, len = cols.length; i < len; i++){
38165             c = cols[i];
38166             colcls = 'x-t-' + tid + '-c' +i;
38167             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38168             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38169                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38170                       "</div>");
38171          }
38172          
38173          buf.push(
38174             '</a>',
38175             '<div class="x-clear"></div></div>',
38176             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38177             "</li>");
38178         
38179         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38180             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38181                                 n.nextSibling.ui.getEl(), buf.join(""));
38182         }else{
38183             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38184         }
38185         var el = this.wrap.firstChild;
38186         this.elRow = el;
38187         this.elNode = el.firstChild;
38188         this.ranchor = el.childNodes[1];
38189         this.ctNode = this.wrap.childNodes[1];
38190         var cs = el.firstChild.childNodes;
38191         this.indentNode = cs[0];
38192         this.ecNode = cs[1];
38193         this.iconNode = cs[2];
38194         var index = 3;
38195         if(cb){
38196             this.checkbox = cs[3];
38197             index++;
38198         }
38199         this.anchor = cs[index];
38200         
38201         this.textNode = cs[index].firstChild;
38202         
38203         //el.on("click", this.onClick, this);
38204         //el.on("dblclick", this.onDblClick, this);
38205         
38206         
38207        // console.log(this);
38208     },
38209     initEvents : function(){
38210         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38211         
38212             
38213         var a = this.ranchor;
38214
38215         var el = Roo.get(a);
38216
38217         if(Roo.isOpera){ // opera render bug ignores the CSS
38218             el.setStyle("text-decoration", "none");
38219         }
38220
38221         el.on("click", this.onClick, this);
38222         el.on("dblclick", this.onDblClick, this);
38223         el.on("contextmenu", this.onContextMenu, this);
38224         
38225     },
38226     
38227     /*onSelectedChange : function(state){
38228         if(state){
38229             this.focus();
38230             this.addClass("x-tree-selected");
38231         }else{
38232             //this.blur();
38233             this.removeClass("x-tree-selected");
38234         }
38235     },*/
38236     addClass : function(cls){
38237         if(this.elRow){
38238             Roo.fly(this.elRow).addClass(cls);
38239         }
38240         
38241     },
38242     
38243     
38244     removeClass : function(cls){
38245         if(this.elRow){
38246             Roo.fly(this.elRow).removeClass(cls);
38247         }
38248     }
38249
38250     
38251     
38252 });//<Script type="text/javascript">
38253
38254 /*
38255  * Based on:
38256  * Ext JS Library 1.1.1
38257  * Copyright(c) 2006-2007, Ext JS, LLC.
38258  *
38259  * Originally Released Under LGPL - original licence link has changed is not relivant.
38260  *
38261  * Fork - LGPL
38262  * <script type="text/javascript">
38263  */
38264  
38265
38266 /**
38267  * @class Roo.tree.ColumnTree
38268  * @extends Roo.tree.TreePanel
38269  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38270  * @cfg {int} borderWidth  compined right/left border allowance
38271  * @constructor
38272  * @param {String/HTMLElement/Element} el The container element
38273  * @param {Object} config
38274  */
38275 Roo.tree.ColumnTree =  function(el, config)
38276 {
38277    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38278    this.addEvents({
38279         /**
38280         * @event resize
38281         * Fire this event on a container when it resizes
38282         * @param {int} w Width
38283         * @param {int} h Height
38284         */
38285        "resize" : true
38286     });
38287     this.on('resize', this.onResize, this);
38288 };
38289
38290 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38291     //lines:false,
38292     
38293     
38294     borderWidth: Roo.isBorderBox ? 0 : 2, 
38295     headEls : false,
38296     
38297     render : function(){
38298         // add the header.....
38299        
38300         Roo.tree.ColumnTree.superclass.render.apply(this);
38301         
38302         this.el.addClass('x-column-tree');
38303         
38304         this.headers = this.el.createChild(
38305             {cls:'x-tree-headers'},this.innerCt.dom);
38306    
38307         var cols = this.columns, c;
38308         var totalWidth = 0;
38309         this.headEls = [];
38310         var  len = cols.length;
38311         for(var i = 0; i < len; i++){
38312              c = cols[i];
38313              totalWidth += c.width;
38314             this.headEls.push(this.headers.createChild({
38315                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38316                  cn: {
38317                      cls:'x-tree-hd-text',
38318                      html: c.header
38319                  },
38320                  style:'width:'+(c.width-this.borderWidth)+'px;'
38321              }));
38322         }
38323         this.headers.createChild({cls:'x-clear'});
38324         // prevent floats from wrapping when clipped
38325         this.headers.setWidth(totalWidth);
38326         //this.innerCt.setWidth(totalWidth);
38327         this.innerCt.setStyle({ overflow: 'auto' });
38328         this.onResize(this.width, this.height);
38329              
38330         
38331     },
38332     onResize : function(w,h)
38333     {
38334         this.height = h;
38335         this.width = w;
38336         // resize cols..
38337         this.innerCt.setWidth(this.width);
38338         this.innerCt.setHeight(this.height-20);
38339         
38340         // headers...
38341         var cols = this.columns, c;
38342         var totalWidth = 0;
38343         var expEl = false;
38344         var len = cols.length;
38345         for(var i = 0; i < len; i++){
38346             c = cols[i];
38347             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38348                 // it's the expander..
38349                 expEl  = this.headEls[i];
38350                 continue;
38351             }
38352             totalWidth += c.width;
38353             
38354         }
38355         if (expEl) {
38356             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38357         }
38358         this.headers.setWidth(w-20);
38359
38360         
38361         
38362         
38363     }
38364 });
38365 /*
38366  * Based on:
38367  * Ext JS Library 1.1.1
38368  * Copyright(c) 2006-2007, Ext JS, LLC.
38369  *
38370  * Originally Released Under LGPL - original licence link has changed is not relivant.
38371  *
38372  * Fork - LGPL
38373  * <script type="text/javascript">
38374  */
38375  
38376 /**
38377  * @class Roo.menu.Menu
38378  * @extends Roo.util.Observable
38379  * @children Roo.menu.BaseItem
38380  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38381  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38382  * @constructor
38383  * Creates a new Menu
38384  * @param {Object} config Configuration options
38385  */
38386 Roo.menu.Menu = function(config){
38387     
38388     Roo.menu.Menu.superclass.constructor.call(this, config);
38389     
38390     this.id = this.id || Roo.id();
38391     this.addEvents({
38392         /**
38393          * @event beforeshow
38394          * Fires before this menu is displayed
38395          * @param {Roo.menu.Menu} this
38396          */
38397         beforeshow : true,
38398         /**
38399          * @event beforehide
38400          * Fires before this menu is hidden
38401          * @param {Roo.menu.Menu} this
38402          */
38403         beforehide : true,
38404         /**
38405          * @event show
38406          * Fires after this menu is displayed
38407          * @param {Roo.menu.Menu} this
38408          */
38409         show : true,
38410         /**
38411          * @event hide
38412          * Fires after this menu is hidden
38413          * @param {Roo.menu.Menu} this
38414          */
38415         hide : true,
38416         /**
38417          * @event click
38418          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38419          * @param {Roo.menu.Menu} this
38420          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38421          * @param {Roo.EventObject} e
38422          */
38423         click : true,
38424         /**
38425          * @event mouseover
38426          * Fires when the mouse is hovering over this menu
38427          * @param {Roo.menu.Menu} this
38428          * @param {Roo.EventObject} e
38429          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38430          */
38431         mouseover : true,
38432         /**
38433          * @event mouseout
38434          * Fires when the mouse exits this menu
38435          * @param {Roo.menu.Menu} this
38436          * @param {Roo.EventObject} e
38437          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38438          */
38439         mouseout : true,
38440         /**
38441          * @event itemclick
38442          * Fires when a menu item contained in this menu is clicked
38443          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38444          * @param {Roo.EventObject} e
38445          */
38446         itemclick: true
38447     });
38448     if (this.registerMenu) {
38449         Roo.menu.MenuMgr.register(this);
38450     }
38451     
38452     var mis = this.items;
38453     this.items = new Roo.util.MixedCollection();
38454     if(mis){
38455         this.add.apply(this, mis);
38456     }
38457 };
38458
38459 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38460     /**
38461      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38462      */
38463     minWidth : 120,
38464     /**
38465      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38466      * for bottom-right shadow (defaults to "sides")
38467      */
38468     shadow : "sides",
38469     /**
38470      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38471      * this menu (defaults to "tl-tr?")
38472      */
38473     subMenuAlign : "tl-tr?",
38474     /**
38475      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38476      * relative to its element of origin (defaults to "tl-bl?")
38477      */
38478     defaultAlign : "tl-bl?",
38479     /**
38480      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38481      */
38482     allowOtherMenus : false,
38483     /**
38484      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38485      */
38486     registerMenu : true,
38487
38488     hidden:true,
38489
38490     // private
38491     render : function(){
38492         if(this.el){
38493             return;
38494         }
38495         var el = this.el = new Roo.Layer({
38496             cls: "x-menu",
38497             shadow:this.shadow,
38498             constrain: false,
38499             parentEl: this.parentEl || document.body,
38500             zindex:15000
38501         });
38502
38503         this.keyNav = new Roo.menu.MenuNav(this);
38504
38505         if(this.plain){
38506             el.addClass("x-menu-plain");
38507         }
38508         if(this.cls){
38509             el.addClass(this.cls);
38510         }
38511         // generic focus element
38512         this.focusEl = el.createChild({
38513             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38514         });
38515         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38516         //disabling touch- as it's causing issues ..
38517         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38518         ul.on('click'   , this.onClick, this);
38519         
38520         
38521         ul.on("mouseover", this.onMouseOver, this);
38522         ul.on("mouseout", this.onMouseOut, this);
38523         this.items.each(function(item){
38524             if (item.hidden) {
38525                 return;
38526             }
38527             
38528             var li = document.createElement("li");
38529             li.className = "x-menu-list-item";
38530             ul.dom.appendChild(li);
38531             item.render(li, this);
38532         }, this);
38533         this.ul = ul;
38534         this.autoWidth();
38535     },
38536
38537     // private
38538     autoWidth : function(){
38539         var el = this.el, ul = this.ul;
38540         if(!el){
38541             return;
38542         }
38543         var w = this.width;
38544         if(w){
38545             el.setWidth(w);
38546         }else if(Roo.isIE){
38547             el.setWidth(this.minWidth);
38548             var t = el.dom.offsetWidth; // force recalc
38549             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38550         }
38551     },
38552
38553     // private
38554     delayAutoWidth : function(){
38555         if(this.rendered){
38556             if(!this.awTask){
38557                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38558             }
38559             this.awTask.delay(20);
38560         }
38561     },
38562
38563     // private
38564     findTargetItem : function(e){
38565         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38566         if(t && t.menuItemId){
38567             return this.items.get(t.menuItemId);
38568         }
38569     },
38570
38571     // private
38572     onClick : function(e){
38573         Roo.log("menu.onClick");
38574         var t = this.findTargetItem(e);
38575         if(!t){
38576             return;
38577         }
38578         Roo.log(e);
38579         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38580             if(t == this.activeItem && t.shouldDeactivate(e)){
38581                 this.activeItem.deactivate();
38582                 delete this.activeItem;
38583                 return;
38584             }
38585             if(t.canActivate){
38586                 this.setActiveItem(t, true);
38587             }
38588             return;
38589             
38590             
38591         }
38592         
38593         t.onClick(e);
38594         this.fireEvent("click", this, t, e);
38595     },
38596
38597     // private
38598     setActiveItem : function(item, autoExpand){
38599         if(item != this.activeItem){
38600             if(this.activeItem){
38601                 this.activeItem.deactivate();
38602             }
38603             this.activeItem = item;
38604             item.activate(autoExpand);
38605         }else if(autoExpand){
38606             item.expandMenu();
38607         }
38608     },
38609
38610     // private
38611     tryActivate : function(start, step){
38612         var items = this.items;
38613         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38614             var item = items.get(i);
38615             if(!item.disabled && item.canActivate){
38616                 this.setActiveItem(item, false);
38617                 return item;
38618             }
38619         }
38620         return false;
38621     },
38622
38623     // private
38624     onMouseOver : function(e){
38625         var t;
38626         if(t = this.findTargetItem(e)){
38627             if(t.canActivate && !t.disabled){
38628                 this.setActiveItem(t, true);
38629             }
38630         }
38631         this.fireEvent("mouseover", this, e, t);
38632     },
38633
38634     // private
38635     onMouseOut : function(e){
38636         var t;
38637         if(t = this.findTargetItem(e)){
38638             if(t == this.activeItem && t.shouldDeactivate(e)){
38639                 this.activeItem.deactivate();
38640                 delete this.activeItem;
38641             }
38642         }
38643         this.fireEvent("mouseout", this, e, t);
38644     },
38645
38646     /**
38647      * Read-only.  Returns true if the menu is currently displayed, else false.
38648      * @type Boolean
38649      */
38650     isVisible : function(){
38651         return this.el && !this.hidden;
38652     },
38653
38654     /**
38655      * Displays this menu relative to another element
38656      * @param {String/HTMLElement/Roo.Element} element The element to align to
38657      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38658      * the element (defaults to this.defaultAlign)
38659      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38660      */
38661     show : function(el, pos, parentMenu){
38662         this.parentMenu = parentMenu;
38663         if(!this.el){
38664             this.render();
38665         }
38666         this.fireEvent("beforeshow", this);
38667         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38668     },
38669
38670     /**
38671      * Displays this menu at a specific xy position
38672      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38673      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38674      */
38675     showAt : function(xy, parentMenu, /* private: */_e){
38676         this.parentMenu = parentMenu;
38677         if(!this.el){
38678             this.render();
38679         }
38680         if(_e !== false){
38681             this.fireEvent("beforeshow", this);
38682             xy = this.el.adjustForConstraints(xy);
38683         }
38684         this.el.setXY(xy);
38685         this.el.show();
38686         this.hidden = false;
38687         this.focus();
38688         this.fireEvent("show", this);
38689     },
38690
38691     focus : function(){
38692         if(!this.hidden){
38693             this.doFocus.defer(50, this);
38694         }
38695     },
38696
38697     doFocus : function(){
38698         if(!this.hidden){
38699             this.focusEl.focus();
38700         }
38701     },
38702
38703     /**
38704      * Hides this menu and optionally all parent menus
38705      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38706      */
38707     hide : function(deep){
38708         if(this.el && this.isVisible()){
38709             this.fireEvent("beforehide", this);
38710             if(this.activeItem){
38711                 this.activeItem.deactivate();
38712                 this.activeItem = null;
38713             }
38714             this.el.hide();
38715             this.hidden = true;
38716             this.fireEvent("hide", this);
38717         }
38718         if(deep === true && this.parentMenu){
38719             this.parentMenu.hide(true);
38720         }
38721     },
38722
38723     /**
38724      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38725      * Any of the following are valid:
38726      * <ul>
38727      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38728      * <li>An HTMLElement object which will be converted to a menu item</li>
38729      * <li>A menu item config object that will be created as a new menu item</li>
38730      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38731      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38732      * </ul>
38733      * Usage:
38734      * <pre><code>
38735 // Create the menu
38736 var menu = new Roo.menu.Menu();
38737
38738 // Create a menu item to add by reference
38739 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38740
38741 // Add a bunch of items at once using different methods.
38742 // Only the last item added will be returned.
38743 var item = menu.add(
38744     menuItem,                // add existing item by ref
38745     'Dynamic Item',          // new TextItem
38746     '-',                     // new separator
38747     { text: 'Config Item' }  // new item by config
38748 );
38749 </code></pre>
38750      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38751      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38752      */
38753     add : function(){
38754         var a = arguments, l = a.length, item;
38755         for(var i = 0; i < l; i++){
38756             var el = a[i];
38757             if ((typeof(el) == "object") && el.xtype && el.xns) {
38758                 el = Roo.factory(el, Roo.menu);
38759             }
38760             
38761             if(el.render){ // some kind of Item
38762                 item = this.addItem(el);
38763             }else if(typeof el == "string"){ // string
38764                 if(el == "separator" || el == "-"){
38765                     item = this.addSeparator();
38766                 }else{
38767                     item = this.addText(el);
38768                 }
38769             }else if(el.tagName || el.el){ // element
38770                 item = this.addElement(el);
38771             }else if(typeof el == "object"){ // must be menu item config?
38772                 item = this.addMenuItem(el);
38773             }
38774         }
38775         return item;
38776     },
38777
38778     /**
38779      * Returns this menu's underlying {@link Roo.Element} object
38780      * @return {Roo.Element} The element
38781      */
38782     getEl : function(){
38783         if(!this.el){
38784             this.render();
38785         }
38786         return this.el;
38787     },
38788
38789     /**
38790      * Adds a separator bar to the menu
38791      * @return {Roo.menu.Item} The menu item that was added
38792      */
38793     addSeparator : function(){
38794         return this.addItem(new Roo.menu.Separator());
38795     },
38796
38797     /**
38798      * Adds an {@link Roo.Element} object to the menu
38799      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38800      * @return {Roo.menu.Item} The menu item that was added
38801      */
38802     addElement : function(el){
38803         return this.addItem(new Roo.menu.BaseItem(el));
38804     },
38805
38806     /**
38807      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38808      * @param {Roo.menu.Item} item The menu item to add
38809      * @return {Roo.menu.Item} The menu item that was added
38810      */
38811     addItem : function(item){
38812         this.items.add(item);
38813         if(this.ul){
38814             var li = document.createElement("li");
38815             li.className = "x-menu-list-item";
38816             this.ul.dom.appendChild(li);
38817             item.render(li, this);
38818             this.delayAutoWidth();
38819         }
38820         return item;
38821     },
38822
38823     /**
38824      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38825      * @param {Object} config A MenuItem config object
38826      * @return {Roo.menu.Item} The menu item that was added
38827      */
38828     addMenuItem : function(config){
38829         if(!(config instanceof Roo.menu.Item)){
38830             if(typeof config.checked == "boolean"){ // must be check menu item config?
38831                 config = new Roo.menu.CheckItem(config);
38832             }else{
38833                 config = new Roo.menu.Item(config);
38834             }
38835         }
38836         return this.addItem(config);
38837     },
38838
38839     /**
38840      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38841      * @param {String} text The text to display in the menu item
38842      * @return {Roo.menu.Item} The menu item that was added
38843      */
38844     addText : function(text){
38845         return this.addItem(new Roo.menu.TextItem({ text : text }));
38846     },
38847
38848     /**
38849      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38850      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38851      * @param {Roo.menu.Item} item The menu item to add
38852      * @return {Roo.menu.Item} The menu item that was added
38853      */
38854     insert : function(index, item){
38855         this.items.insert(index, item);
38856         if(this.ul){
38857             var li = document.createElement("li");
38858             li.className = "x-menu-list-item";
38859             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38860             item.render(li, this);
38861             this.delayAutoWidth();
38862         }
38863         return item;
38864     },
38865
38866     /**
38867      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38868      * @param {Roo.menu.Item} item The menu item to remove
38869      */
38870     remove : function(item){
38871         this.items.removeKey(item.id);
38872         item.destroy();
38873     },
38874
38875     /**
38876      * Removes and destroys all items in the menu
38877      */
38878     removeAll : function(){
38879         var f;
38880         while(f = this.items.first()){
38881             this.remove(f);
38882         }
38883     }
38884 });
38885
38886 // MenuNav is a private utility class used internally by the Menu
38887 Roo.menu.MenuNav = function(menu){
38888     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38889     this.scope = this.menu = menu;
38890 };
38891
38892 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38893     doRelay : function(e, h){
38894         var k = e.getKey();
38895         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38896             this.menu.tryActivate(0, 1);
38897             return false;
38898         }
38899         return h.call(this.scope || this, e, this.menu);
38900     },
38901
38902     up : function(e, m){
38903         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38904             m.tryActivate(m.items.length-1, -1);
38905         }
38906     },
38907
38908     down : function(e, m){
38909         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38910             m.tryActivate(0, 1);
38911         }
38912     },
38913
38914     right : function(e, m){
38915         if(m.activeItem){
38916             m.activeItem.expandMenu(true);
38917         }
38918     },
38919
38920     left : function(e, m){
38921         m.hide();
38922         if(m.parentMenu && m.parentMenu.activeItem){
38923             m.parentMenu.activeItem.activate();
38924         }
38925     },
38926
38927     enter : function(e, m){
38928         if(m.activeItem){
38929             e.stopPropagation();
38930             m.activeItem.onClick(e);
38931             m.fireEvent("click", this, m.activeItem);
38932             return true;
38933         }
38934     }
38935 });/*
38936  * Based on:
38937  * Ext JS Library 1.1.1
38938  * Copyright(c) 2006-2007, Ext JS, LLC.
38939  *
38940  * Originally Released Under LGPL - original licence link has changed is not relivant.
38941  *
38942  * Fork - LGPL
38943  * <script type="text/javascript">
38944  */
38945  
38946 /**
38947  * @class Roo.menu.MenuMgr
38948  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38949  * @static
38950  */
38951 Roo.menu.MenuMgr = function(){
38952    var menus, active, groups = {}, attached = false, lastShow = new Date();
38953
38954    // private - called when first menu is created
38955    function init(){
38956        menus = {};
38957        active = new Roo.util.MixedCollection();
38958        Roo.get(document).addKeyListener(27, function(){
38959            if(active.length > 0){
38960                hideAll();
38961            }
38962        });
38963    }
38964
38965    // private
38966    function hideAll(){
38967        if(active && active.length > 0){
38968            var c = active.clone();
38969            c.each(function(m){
38970                m.hide();
38971            });
38972        }
38973    }
38974
38975    // private
38976    function onHide(m){
38977        active.remove(m);
38978        if(active.length < 1){
38979            Roo.get(document).un("mousedown", onMouseDown);
38980            attached = false;
38981        }
38982    }
38983
38984    // private
38985    function onShow(m){
38986        var last = active.last();
38987        lastShow = new Date();
38988        active.add(m);
38989        if(!attached){
38990            Roo.get(document).on("mousedown", onMouseDown);
38991            attached = true;
38992        }
38993        if(m.parentMenu){
38994           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38995           m.parentMenu.activeChild = m;
38996        }else if(last && last.isVisible()){
38997           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38998        }
38999    }
39000
39001    // private
39002    function onBeforeHide(m){
39003        if(m.activeChild){
39004            m.activeChild.hide();
39005        }
39006        if(m.autoHideTimer){
39007            clearTimeout(m.autoHideTimer);
39008            delete m.autoHideTimer;
39009        }
39010    }
39011
39012    // private
39013    function onBeforeShow(m){
39014        var pm = m.parentMenu;
39015        if(!pm && !m.allowOtherMenus){
39016            hideAll();
39017        }else if(pm && pm.activeChild && active != m){
39018            pm.activeChild.hide();
39019        }
39020    }
39021
39022    // private
39023    function onMouseDown(e){
39024        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39025            hideAll();
39026        }
39027    }
39028
39029    // private
39030    function onBeforeCheck(mi, state){
39031        if(state){
39032            var g = groups[mi.group];
39033            for(var i = 0, l = g.length; i < l; i++){
39034                if(g[i] != mi){
39035                    g[i].setChecked(false);
39036                }
39037            }
39038        }
39039    }
39040
39041    return {
39042
39043        /**
39044         * Hides all menus that are currently visible
39045         */
39046        hideAll : function(){
39047             hideAll();  
39048        },
39049
39050        // private
39051        register : function(menu){
39052            if(!menus){
39053                init();
39054            }
39055            menus[menu.id] = menu;
39056            menu.on("beforehide", onBeforeHide);
39057            menu.on("hide", onHide);
39058            menu.on("beforeshow", onBeforeShow);
39059            menu.on("show", onShow);
39060            var g = menu.group;
39061            if(g && menu.events["checkchange"]){
39062                if(!groups[g]){
39063                    groups[g] = [];
39064                }
39065                groups[g].push(menu);
39066                menu.on("checkchange", onCheck);
39067            }
39068        },
39069
39070         /**
39071          * Returns a {@link Roo.menu.Menu} object
39072          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39073          * be used to generate and return a new Menu instance.
39074          */
39075        get : function(menu){
39076            if(typeof menu == "string"){ // menu id
39077                return menus[menu];
39078            }else if(menu.events){  // menu instance
39079                return menu;
39080            }else if(typeof menu.length == 'number'){ // array of menu items?
39081                return new Roo.menu.Menu({items:menu});
39082            }else{ // otherwise, must be a config
39083                return new Roo.menu.Menu(menu);
39084            }
39085        },
39086
39087        // private
39088        unregister : function(menu){
39089            delete menus[menu.id];
39090            menu.un("beforehide", onBeforeHide);
39091            menu.un("hide", onHide);
39092            menu.un("beforeshow", onBeforeShow);
39093            menu.un("show", onShow);
39094            var g = menu.group;
39095            if(g && menu.events["checkchange"]){
39096                groups[g].remove(menu);
39097                menu.un("checkchange", onCheck);
39098            }
39099        },
39100
39101        // private
39102        registerCheckable : function(menuItem){
39103            var g = menuItem.group;
39104            if(g){
39105                if(!groups[g]){
39106                    groups[g] = [];
39107                }
39108                groups[g].push(menuItem);
39109                menuItem.on("beforecheckchange", onBeforeCheck);
39110            }
39111        },
39112
39113        // private
39114        unregisterCheckable : function(menuItem){
39115            var g = menuItem.group;
39116            if(g){
39117                groups[g].remove(menuItem);
39118                menuItem.un("beforecheckchange", onBeforeCheck);
39119            }
39120        }
39121    };
39122 }();/*
39123  * Based on:
39124  * Ext JS Library 1.1.1
39125  * Copyright(c) 2006-2007, Ext JS, LLC.
39126  *
39127  * Originally Released Under LGPL - original licence link has changed is not relivant.
39128  *
39129  * Fork - LGPL
39130  * <script type="text/javascript">
39131  */
39132  
39133
39134 /**
39135  * @class Roo.menu.BaseItem
39136  * @extends Roo.Component
39137  * @abstract
39138  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39139  * management and base configuration options shared by all menu components.
39140  * @constructor
39141  * Creates a new BaseItem
39142  * @param {Object} config Configuration options
39143  */
39144 Roo.menu.BaseItem = function(config){
39145     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39146
39147     this.addEvents({
39148         /**
39149          * @event click
39150          * Fires when this item is clicked
39151          * @param {Roo.menu.BaseItem} this
39152          * @param {Roo.EventObject} e
39153          */
39154         click: true,
39155         /**
39156          * @event activate
39157          * Fires when this item is activated
39158          * @param {Roo.menu.BaseItem} this
39159          */
39160         activate : true,
39161         /**
39162          * @event deactivate
39163          * Fires when this item is deactivated
39164          * @param {Roo.menu.BaseItem} this
39165          */
39166         deactivate : true
39167     });
39168
39169     if(this.handler){
39170         this.on("click", this.handler, this.scope, true);
39171     }
39172 };
39173
39174 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39175     /**
39176      * @cfg {Function} handler
39177      * A function that will handle the click event of this menu item (defaults to undefined)
39178      */
39179     /**
39180      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39181      */
39182     canActivate : false,
39183     
39184      /**
39185      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39186      */
39187     hidden: false,
39188     
39189     /**
39190      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39191      */
39192     activeClass : "x-menu-item-active",
39193     /**
39194      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39195      */
39196     hideOnClick : true,
39197     /**
39198      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39199      */
39200     hideDelay : 100,
39201
39202     // private
39203     ctype: "Roo.menu.BaseItem",
39204
39205     // private
39206     actionMode : "container",
39207
39208     // private
39209     render : function(container, parentMenu){
39210         this.parentMenu = parentMenu;
39211         Roo.menu.BaseItem.superclass.render.call(this, container);
39212         this.container.menuItemId = this.id;
39213     },
39214
39215     // private
39216     onRender : function(container, position){
39217         this.el = Roo.get(this.el);
39218         container.dom.appendChild(this.el.dom);
39219     },
39220
39221     // private
39222     onClick : function(e){
39223         if(!this.disabled && this.fireEvent("click", this, e) !== false
39224                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39225             this.handleClick(e);
39226         }else{
39227             e.stopEvent();
39228         }
39229     },
39230
39231     // private
39232     activate : function(){
39233         if(this.disabled){
39234             return false;
39235         }
39236         var li = this.container;
39237         li.addClass(this.activeClass);
39238         this.region = li.getRegion().adjust(2, 2, -2, -2);
39239         this.fireEvent("activate", this);
39240         return true;
39241     },
39242
39243     // private
39244     deactivate : function(){
39245         this.container.removeClass(this.activeClass);
39246         this.fireEvent("deactivate", this);
39247     },
39248
39249     // private
39250     shouldDeactivate : function(e){
39251         return !this.region || !this.region.contains(e.getPoint());
39252     },
39253
39254     // private
39255     handleClick : function(e){
39256         if(this.hideOnClick){
39257             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39258         }
39259     },
39260
39261     // private
39262     expandMenu : function(autoActivate){
39263         // do nothing
39264     },
39265
39266     // private
39267     hideMenu : function(){
39268         // do nothing
39269     }
39270 });/*
39271  * Based on:
39272  * Ext JS Library 1.1.1
39273  * Copyright(c) 2006-2007, Ext JS, LLC.
39274  *
39275  * Originally Released Under LGPL - original licence link has changed is not relivant.
39276  *
39277  * Fork - LGPL
39278  * <script type="text/javascript">
39279  */
39280  
39281 /**
39282  * @class Roo.menu.Adapter
39283  * @extends Roo.menu.BaseItem
39284  * @abstract
39285  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
39286  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39287  * @constructor
39288  * Creates a new Adapter
39289  * @param {Object} config Configuration options
39290  */
39291 Roo.menu.Adapter = function(component, config){
39292     Roo.menu.Adapter.superclass.constructor.call(this, config);
39293     this.component = component;
39294 };
39295 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39296     // private
39297     canActivate : true,
39298
39299     // private
39300     onRender : function(container, position){
39301         this.component.render(container);
39302         this.el = this.component.getEl();
39303     },
39304
39305     // private
39306     activate : function(){
39307         if(this.disabled){
39308             return false;
39309         }
39310         this.component.focus();
39311         this.fireEvent("activate", this);
39312         return true;
39313     },
39314
39315     // private
39316     deactivate : function(){
39317         this.fireEvent("deactivate", this);
39318     },
39319
39320     // private
39321     disable : function(){
39322         this.component.disable();
39323         Roo.menu.Adapter.superclass.disable.call(this);
39324     },
39325
39326     // private
39327     enable : function(){
39328         this.component.enable();
39329         Roo.menu.Adapter.superclass.enable.call(this);
39330     }
39331 });/*
39332  * Based on:
39333  * Ext JS Library 1.1.1
39334  * Copyright(c) 2006-2007, Ext JS, LLC.
39335  *
39336  * Originally Released Under LGPL - original licence link has changed is not relivant.
39337  *
39338  * Fork - LGPL
39339  * <script type="text/javascript">
39340  */
39341
39342 /**
39343  * @class Roo.menu.TextItem
39344  * @extends Roo.menu.BaseItem
39345  * Adds a static text string to a menu, usually used as either a heading or group separator.
39346  * Note: old style constructor with text is still supported.
39347  * 
39348  * @constructor
39349  * Creates a new TextItem
39350  * @param {Object} cfg Configuration
39351  */
39352 Roo.menu.TextItem = function(cfg){
39353     if (typeof(cfg) == 'string') {
39354         this.text = cfg;
39355     } else {
39356         Roo.apply(this,cfg);
39357     }
39358     
39359     Roo.menu.TextItem.superclass.constructor.call(this);
39360 };
39361
39362 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39363     /**
39364      * @cfg {String} text Text to show on item.
39365      */
39366     text : '',
39367     
39368     /**
39369      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39370      */
39371     hideOnClick : false,
39372     /**
39373      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39374      */
39375     itemCls : "x-menu-text",
39376
39377     // private
39378     onRender : function(){
39379         var s = document.createElement("span");
39380         s.className = this.itemCls;
39381         s.innerHTML = this.text;
39382         this.el = s;
39383         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39384     }
39385 });/*
39386  * Based on:
39387  * Ext JS Library 1.1.1
39388  * Copyright(c) 2006-2007, Ext JS, LLC.
39389  *
39390  * Originally Released Under LGPL - original licence link has changed is not relivant.
39391  *
39392  * Fork - LGPL
39393  * <script type="text/javascript">
39394  */
39395
39396 /**
39397  * @class Roo.menu.Separator
39398  * @extends Roo.menu.BaseItem
39399  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39400  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39401  * @constructor
39402  * @param {Object} config Configuration options
39403  */
39404 Roo.menu.Separator = function(config){
39405     Roo.menu.Separator.superclass.constructor.call(this, config);
39406 };
39407
39408 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39409     /**
39410      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39411      */
39412     itemCls : "x-menu-sep",
39413     /**
39414      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39415      */
39416     hideOnClick : false,
39417
39418     // private
39419     onRender : function(li){
39420         var s = document.createElement("span");
39421         s.className = this.itemCls;
39422         s.innerHTML = "&#160;";
39423         this.el = s;
39424         li.addClass("x-menu-sep-li");
39425         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39426     }
39427 });/*
39428  * Based on:
39429  * Ext JS Library 1.1.1
39430  * Copyright(c) 2006-2007, Ext JS, LLC.
39431  *
39432  * Originally Released Under LGPL - original licence link has changed is not relivant.
39433  *
39434  * Fork - LGPL
39435  * <script type="text/javascript">
39436  */
39437 /**
39438  * @class Roo.menu.Item
39439  * @extends Roo.menu.BaseItem
39440  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39441  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39442  * activation and click handling.
39443  * @constructor
39444  * Creates a new Item
39445  * @param {Object} config Configuration options
39446  */
39447 Roo.menu.Item = function(config){
39448     Roo.menu.Item.superclass.constructor.call(this, config);
39449     if(this.menu){
39450         this.menu = Roo.menu.MenuMgr.get(this.menu);
39451     }
39452 };
39453 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39454     /**
39455      * @cfg {Roo.menu.Menu} menu
39456      * A Sub menu
39457      */
39458     /**
39459      * @cfg {String} text
39460      * The text to show on the menu item.
39461      */
39462     text: '',
39463      /**
39464      * @cfg {String} HTML to render in menu
39465      * The text to show on the menu item (HTML version).
39466      */
39467     html: '',
39468     /**
39469      * @cfg {String} icon
39470      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39471      */
39472     icon: undefined,
39473     /**
39474      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39475      */
39476     itemCls : "x-menu-item",
39477     /**
39478      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39479      */
39480     canActivate : true,
39481     /**
39482      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39483      */
39484     showDelay: 200,
39485     // doc'd in BaseItem
39486     hideDelay: 200,
39487
39488     // private
39489     ctype: "Roo.menu.Item",
39490     
39491     // private
39492     onRender : function(container, position){
39493         var el = document.createElement("a");
39494         el.hideFocus = true;
39495         el.unselectable = "on";
39496         el.href = this.href || "#";
39497         if(this.hrefTarget){
39498             el.target = this.hrefTarget;
39499         }
39500         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39501         
39502         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39503         
39504         el.innerHTML = String.format(
39505                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39506                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39507         this.el = el;
39508         Roo.menu.Item.superclass.onRender.call(this, container, position);
39509     },
39510
39511     /**
39512      * Sets the text to display in this menu item
39513      * @param {String} text The text to display
39514      * @param {Boolean} isHTML true to indicate text is pure html.
39515      */
39516     setText : function(text, isHTML){
39517         if (isHTML) {
39518             this.html = text;
39519         } else {
39520             this.text = text;
39521             this.html = '';
39522         }
39523         if(this.rendered){
39524             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39525      
39526             this.el.update(String.format(
39527                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39528                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39529             this.parentMenu.autoWidth();
39530         }
39531     },
39532
39533     // private
39534     handleClick : function(e){
39535         if(!this.href){ // if no link defined, stop the event automatically
39536             e.stopEvent();
39537         }
39538         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39539     },
39540
39541     // private
39542     activate : function(autoExpand){
39543         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39544             this.focus();
39545             if(autoExpand){
39546                 this.expandMenu();
39547             }
39548         }
39549         return true;
39550     },
39551
39552     // private
39553     shouldDeactivate : function(e){
39554         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39555             if(this.menu && this.menu.isVisible()){
39556                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39557             }
39558             return true;
39559         }
39560         return false;
39561     },
39562
39563     // private
39564     deactivate : function(){
39565         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39566         this.hideMenu();
39567     },
39568
39569     // private
39570     expandMenu : function(autoActivate){
39571         if(!this.disabled && this.menu){
39572             clearTimeout(this.hideTimer);
39573             delete this.hideTimer;
39574             if(!this.menu.isVisible() && !this.showTimer){
39575                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39576             }else if (this.menu.isVisible() && autoActivate){
39577                 this.menu.tryActivate(0, 1);
39578             }
39579         }
39580     },
39581
39582     // private
39583     deferExpand : function(autoActivate){
39584         delete this.showTimer;
39585         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39586         if(autoActivate){
39587             this.menu.tryActivate(0, 1);
39588         }
39589     },
39590
39591     // private
39592     hideMenu : function(){
39593         clearTimeout(this.showTimer);
39594         delete this.showTimer;
39595         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39596             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39597         }
39598     },
39599
39600     // private
39601     deferHide : function(){
39602         delete this.hideTimer;
39603         this.menu.hide();
39604     }
39605 });/*
39606  * Based on:
39607  * Ext JS Library 1.1.1
39608  * Copyright(c) 2006-2007, Ext JS, LLC.
39609  *
39610  * Originally Released Under LGPL - original licence link has changed is not relivant.
39611  *
39612  * Fork - LGPL
39613  * <script type="text/javascript">
39614  */
39615  
39616 /**
39617  * @class Roo.menu.CheckItem
39618  * @extends Roo.menu.Item
39619  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39620  * @constructor
39621  * Creates a new CheckItem
39622  * @param {Object} config Configuration options
39623  */
39624 Roo.menu.CheckItem = function(config){
39625     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39626     this.addEvents({
39627         /**
39628          * @event beforecheckchange
39629          * Fires before the checked value is set, providing an opportunity to cancel if needed
39630          * @param {Roo.menu.CheckItem} this
39631          * @param {Boolean} checked The new checked value that will be set
39632          */
39633         "beforecheckchange" : true,
39634         /**
39635          * @event checkchange
39636          * Fires after the checked value has been set
39637          * @param {Roo.menu.CheckItem} this
39638          * @param {Boolean} checked The checked value that was set
39639          */
39640         "checkchange" : true
39641     });
39642     if(this.checkHandler){
39643         this.on('checkchange', this.checkHandler, this.scope);
39644     }
39645 };
39646 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39647     /**
39648      * @cfg {String} group
39649      * All check items with the same group name will automatically be grouped into a single-select
39650      * radio button group (defaults to '')
39651      */
39652     /**
39653      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39654      */
39655     itemCls : "x-menu-item x-menu-check-item",
39656     /**
39657      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39658      */
39659     groupClass : "x-menu-group-item",
39660
39661     /**
39662      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39663      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39664      * initialized with checked = true will be rendered as checked.
39665      */
39666     checked: false,
39667
39668     // private
39669     ctype: "Roo.menu.CheckItem",
39670
39671     // private
39672     onRender : function(c){
39673         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39674         if(this.group){
39675             this.el.addClass(this.groupClass);
39676         }
39677         Roo.menu.MenuMgr.registerCheckable(this);
39678         if(this.checked){
39679             this.checked = false;
39680             this.setChecked(true, true);
39681         }
39682     },
39683
39684     // private
39685     destroy : function(){
39686         if(this.rendered){
39687             Roo.menu.MenuMgr.unregisterCheckable(this);
39688         }
39689         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39690     },
39691
39692     /**
39693      * Set the checked state of this item
39694      * @param {Boolean} checked The new checked value
39695      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39696      */
39697     setChecked : function(state, suppressEvent){
39698         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39699             if(this.container){
39700                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39701             }
39702             this.checked = state;
39703             if(suppressEvent !== true){
39704                 this.fireEvent("checkchange", this, state);
39705             }
39706         }
39707     },
39708
39709     // private
39710     handleClick : function(e){
39711        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39712            this.setChecked(!this.checked);
39713        }
39714        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39715     }
39716 });/*
39717  * Based on:
39718  * Ext JS Library 1.1.1
39719  * Copyright(c) 2006-2007, Ext JS, LLC.
39720  *
39721  * Originally Released Under LGPL - original licence link has changed is not relivant.
39722  *
39723  * Fork - LGPL
39724  * <script type="text/javascript">
39725  */
39726  
39727 /**
39728  * @class Roo.menu.DateItem
39729  * @extends Roo.menu.Adapter
39730  * A menu item that wraps the {@link Roo.DatPicker} component.
39731  * @constructor
39732  * Creates a new DateItem
39733  * @param {Object} config Configuration options
39734  */
39735 Roo.menu.DateItem = function(config){
39736     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39737     /** The Roo.DatePicker object @type Roo.DatePicker */
39738     this.picker = this.component;
39739     this.addEvents({select: true});
39740     
39741     this.picker.on("render", function(picker){
39742         picker.getEl().swallowEvent("click");
39743         picker.container.addClass("x-menu-date-item");
39744     });
39745
39746     this.picker.on("select", this.onSelect, this);
39747 };
39748
39749 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39750     // private
39751     onSelect : function(picker, date){
39752         this.fireEvent("select", this, date, picker);
39753         Roo.menu.DateItem.superclass.handleClick.call(this);
39754     }
39755 });/*
39756  * Based on:
39757  * Ext JS Library 1.1.1
39758  * Copyright(c) 2006-2007, Ext JS, LLC.
39759  *
39760  * Originally Released Under LGPL - original licence link has changed is not relivant.
39761  *
39762  * Fork - LGPL
39763  * <script type="text/javascript">
39764  */
39765  
39766 /**
39767  * @class Roo.menu.ColorItem
39768  * @extends Roo.menu.Adapter
39769  * A menu item that wraps the {@link Roo.ColorPalette} component.
39770  * @constructor
39771  * Creates a new ColorItem
39772  * @param {Object} config Configuration options
39773  */
39774 Roo.menu.ColorItem = function(config){
39775     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39776     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39777     this.palette = this.component;
39778     this.relayEvents(this.palette, ["select"]);
39779     if(this.selectHandler){
39780         this.on('select', this.selectHandler, this.scope);
39781     }
39782 };
39783 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39784  * Based on:
39785  * Ext JS Library 1.1.1
39786  * Copyright(c) 2006-2007, Ext JS, LLC.
39787  *
39788  * Originally Released Under LGPL - original licence link has changed is not relivant.
39789  *
39790  * Fork - LGPL
39791  * <script type="text/javascript">
39792  */
39793  
39794
39795 /**
39796  * @class Roo.menu.DateMenu
39797  * @extends Roo.menu.Menu
39798  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39799  * @constructor
39800  * Creates a new DateMenu
39801  * @param {Object} config Configuration options
39802  */
39803 Roo.menu.DateMenu = function(config){
39804     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39805     this.plain = true;
39806     var di = new Roo.menu.DateItem(config);
39807     this.add(di);
39808     /**
39809      * The {@link Roo.DatePicker} instance for this DateMenu
39810      * @type DatePicker
39811      */
39812     this.picker = di.picker;
39813     /**
39814      * @event select
39815      * @param {DatePicker} picker
39816      * @param {Date} date
39817      */
39818     this.relayEvents(di, ["select"]);
39819     this.on('beforeshow', function(){
39820         if(this.picker){
39821             this.picker.hideMonthPicker(false);
39822         }
39823     }, this);
39824 };
39825 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39826     cls:'x-date-menu'
39827 });/*
39828  * Based on:
39829  * Ext JS Library 1.1.1
39830  * Copyright(c) 2006-2007, Ext JS, LLC.
39831  *
39832  * Originally Released Under LGPL - original licence link has changed is not relivant.
39833  *
39834  * Fork - LGPL
39835  * <script type="text/javascript">
39836  */
39837  
39838
39839 /**
39840  * @class Roo.menu.ColorMenu
39841  * @extends Roo.menu.Menu
39842  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39843  * @constructor
39844  * Creates a new ColorMenu
39845  * @param {Object} config Configuration options
39846  */
39847 Roo.menu.ColorMenu = function(config){
39848     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39849     this.plain = true;
39850     var ci = new Roo.menu.ColorItem(config);
39851     this.add(ci);
39852     /**
39853      * The {@link Roo.ColorPalette} instance for this ColorMenu
39854      * @type ColorPalette
39855      */
39856     this.palette = ci.palette;
39857     /**
39858      * @event select
39859      * @param {ColorPalette} palette
39860      * @param {String} color
39861      */
39862     this.relayEvents(ci, ["select"]);
39863 };
39864 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39865  * Based on:
39866  * Ext JS Library 1.1.1
39867  * Copyright(c) 2006-2007, Ext JS, LLC.
39868  *
39869  * Originally Released Under LGPL - original licence link has changed is not relivant.
39870  *
39871  * Fork - LGPL
39872  * <script type="text/javascript">
39873  */
39874  
39875 /**
39876  * @class Roo.form.TextItem
39877  * @extends Roo.BoxComponent
39878  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39879  * @constructor
39880  * Creates a new TextItem
39881  * @param {Object} config Configuration options
39882  */
39883 Roo.form.TextItem = function(config){
39884     Roo.form.TextItem.superclass.constructor.call(this, config);
39885 };
39886
39887 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39888     
39889     /**
39890      * @cfg {String} tag the tag for this item (default div)
39891      */
39892     tag : 'div',
39893     /**
39894      * @cfg {String} html the content for this item
39895      */
39896     html : '',
39897     
39898     getAutoCreate : function()
39899     {
39900         var cfg = {
39901             id: this.id,
39902             tag: this.tag,
39903             html: this.html,
39904             cls: 'x-form-item'
39905         };
39906         
39907         return cfg;
39908         
39909     },
39910     
39911     onRender : function(ct, position)
39912     {
39913         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39914         
39915         if(!this.el){
39916             var cfg = this.getAutoCreate();
39917             if(!cfg.name){
39918                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39919             }
39920             if (!cfg.name.length) {
39921                 delete cfg.name;
39922             }
39923             this.el = ct.createChild(cfg, position);
39924         }
39925     },
39926     /*
39927      * setHTML
39928      * @param {String} html update the Contents of the element.
39929      */
39930     setHTML : function(html)
39931     {
39932         this.fieldEl.dom.innerHTML = html;
39933     }
39934     
39935 });/*
39936  * Based on:
39937  * Ext JS Library 1.1.1
39938  * Copyright(c) 2006-2007, Ext JS, LLC.
39939  *
39940  * Originally Released Under LGPL - original licence link has changed is not relivant.
39941  *
39942  * Fork - LGPL
39943  * <script type="text/javascript">
39944  */
39945  
39946 /**
39947  * @class Roo.form.Field
39948  * @extends Roo.BoxComponent
39949  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39950  * @constructor
39951  * Creates a new Field
39952  * @param {Object} config Configuration options
39953  */
39954 Roo.form.Field = function(config){
39955     Roo.form.Field.superclass.constructor.call(this, config);
39956 };
39957
39958 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39959     /**
39960      * @cfg {String} fieldLabel Label to use when rendering a form.
39961      */
39962        /**
39963      * @cfg {String} qtip Mouse over tip
39964      */
39965      
39966     /**
39967      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39968      */
39969     invalidClass : "x-form-invalid",
39970     /**
39971      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
39972      */
39973     invalidText : "The value in this field is invalid",
39974     /**
39975      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39976      */
39977     focusClass : "x-form-focus",
39978     /**
39979      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39980       automatic validation (defaults to "keyup").
39981      */
39982     validationEvent : "keyup",
39983     /**
39984      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39985      */
39986     validateOnBlur : true,
39987     /**
39988      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39989      */
39990     validationDelay : 250,
39991     /**
39992      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39993      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39994      */
39995     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39996     /**
39997      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39998      */
39999     fieldClass : "x-form-field",
40000     /**
40001      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40002      *<pre>
40003 Value         Description
40004 -----------   ----------------------------------------------------------------------
40005 qtip          Display a quick tip when the user hovers over the field
40006 title         Display a default browser title attribute popup
40007 under         Add a block div beneath the field containing the error text
40008 side          Add an error icon to the right of the field with a popup on hover
40009 [element id]  Add the error text directly to the innerHTML of the specified element
40010 </pre>
40011      */
40012     msgTarget : 'qtip',
40013     /**
40014      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40015      */
40016     msgFx : 'normal',
40017
40018     /**
40019      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
40020      */
40021     readOnly : false,
40022
40023     /**
40024      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40025      */
40026     disabled : false,
40027
40028     /**
40029      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40030      */
40031     inputType : undefined,
40032     
40033     /**
40034      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
40035          */
40036         tabIndex : undefined,
40037         
40038     // private
40039     isFormField : true,
40040
40041     // private
40042     hasFocus : false,
40043     /**
40044      * @property {Roo.Element} fieldEl
40045      * Element Containing the rendered Field (with label etc.)
40046      */
40047     /**
40048      * @cfg {Mixed} value A value to initialize this field with.
40049      */
40050     value : undefined,
40051
40052     /**
40053      * @cfg {String} name The field's HTML name attribute.
40054      */
40055     /**
40056      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40057      */
40058     // private
40059     loadedValue : false,
40060      
40061      
40062         // private ??
40063         initComponent : function(){
40064         Roo.form.Field.superclass.initComponent.call(this);
40065         this.addEvents({
40066             /**
40067              * @event focus
40068              * Fires when this field receives input focus.
40069              * @param {Roo.form.Field} this
40070              */
40071             focus : true,
40072             /**
40073              * @event blur
40074              * Fires when this field loses input focus.
40075              * @param {Roo.form.Field} this
40076              */
40077             blur : true,
40078             /**
40079              * @event specialkey
40080              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40081              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40082              * @param {Roo.form.Field} this
40083              * @param {Roo.EventObject} e The event object
40084              */
40085             specialkey : true,
40086             /**
40087              * @event change
40088              * Fires just before the field blurs if the field value has changed.
40089              * @param {Roo.form.Field} this
40090              * @param {Mixed} newValue The new value
40091              * @param {Mixed} oldValue The original value
40092              */
40093             change : true,
40094             /**
40095              * @event invalid
40096              * Fires after the field has been marked as invalid.
40097              * @param {Roo.form.Field} this
40098              * @param {String} msg The validation message
40099              */
40100             invalid : true,
40101             /**
40102              * @event valid
40103              * Fires after the field has been validated with no errors.
40104              * @param {Roo.form.Field} this
40105              */
40106             valid : true,
40107              /**
40108              * @event keyup
40109              * Fires after the key up
40110              * @param {Roo.form.Field} this
40111              * @param {Roo.EventObject}  e The event Object
40112              */
40113             keyup : true
40114         });
40115     },
40116
40117     /**
40118      * Returns the name attribute of the field if available
40119      * @return {String} name The field name
40120      */
40121     getName: function(){
40122          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40123     },
40124
40125     // private
40126     onRender : function(ct, position){
40127         Roo.form.Field.superclass.onRender.call(this, ct, position);
40128         if(!this.el){
40129             var cfg = this.getAutoCreate();
40130             if(!cfg.name){
40131                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40132             }
40133             if (!cfg.name.length) {
40134                 delete cfg.name;
40135             }
40136             if(this.inputType){
40137                 cfg.type = this.inputType;
40138             }
40139             this.el = ct.createChild(cfg, position);
40140         }
40141         var type = this.el.dom.type;
40142         if(type){
40143             if(type == 'password'){
40144                 type = 'text';
40145             }
40146             this.el.addClass('x-form-'+type);
40147         }
40148         if(this.readOnly){
40149             this.el.dom.readOnly = true;
40150         }
40151         if(this.tabIndex !== undefined){
40152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40153         }
40154
40155         this.el.addClass([this.fieldClass, this.cls]);
40156         this.initValue();
40157     },
40158
40159     /**
40160      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40161      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40162      * @return {Roo.form.Field} this
40163      */
40164     applyTo : function(target){
40165         this.allowDomMove = false;
40166         this.el = Roo.get(target);
40167         this.render(this.el.dom.parentNode);
40168         return this;
40169     },
40170
40171     // private
40172     initValue : function(){
40173         if(this.value !== undefined){
40174             this.setValue(this.value);
40175         }else if(this.el.dom.value.length > 0){
40176             this.setValue(this.el.dom.value);
40177         }
40178     },
40179
40180     /**
40181      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40182      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40183      */
40184     isDirty : function() {
40185         if(this.disabled) {
40186             return false;
40187         }
40188         return String(this.getValue()) !== String(this.originalValue);
40189     },
40190
40191     /**
40192      * stores the current value in loadedValue
40193      */
40194     resetHasChanged : function()
40195     {
40196         this.loadedValue = String(this.getValue());
40197     },
40198     /**
40199      * checks the current value against the 'loaded' value.
40200      * Note - will return false if 'resetHasChanged' has not been called first.
40201      */
40202     hasChanged : function()
40203     {
40204         if(this.disabled || this.readOnly) {
40205             return false;
40206         }
40207         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40208     },
40209     
40210     
40211     
40212     // private
40213     afterRender : function(){
40214         Roo.form.Field.superclass.afterRender.call(this);
40215         this.initEvents();
40216     },
40217
40218     // private
40219     fireKey : function(e){
40220         //Roo.log('field ' + e.getKey());
40221         if(e.isNavKeyPress()){
40222             this.fireEvent("specialkey", this, e);
40223         }
40224     },
40225
40226     /**
40227      * Resets the current field value to the originally loaded value and clears any validation messages
40228      */
40229     reset : function(){
40230         this.setValue(this.resetValue);
40231         this.originalValue = this.getValue();
40232         this.clearInvalid();
40233     },
40234
40235     // private
40236     initEvents : function(){
40237         // safari killled keypress - so keydown is now used..
40238         this.el.on("keydown" , this.fireKey,  this);
40239         this.el.on("focus", this.onFocus,  this);
40240         this.el.on("blur", this.onBlur,  this);
40241         this.el.relayEvent('keyup', this);
40242
40243         // reference to original value for reset
40244         this.originalValue = this.getValue();
40245         this.resetValue =  this.getValue();
40246     },
40247
40248     // private
40249     onFocus : function(){
40250         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40251             this.el.addClass(this.focusClass);
40252         }
40253         if(!this.hasFocus){
40254             this.hasFocus = true;
40255             this.startValue = this.getValue();
40256             this.fireEvent("focus", this);
40257         }
40258     },
40259
40260     beforeBlur : Roo.emptyFn,
40261
40262     // private
40263     onBlur : function(){
40264         this.beforeBlur();
40265         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40266             this.el.removeClass(this.focusClass);
40267         }
40268         this.hasFocus = false;
40269         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40270             this.validate();
40271         }
40272         var v = this.getValue();
40273         if(String(v) !== String(this.startValue)){
40274             this.fireEvent('change', this, v, this.startValue);
40275         }
40276         this.fireEvent("blur", this);
40277     },
40278
40279     /**
40280      * Returns whether or not the field value is currently valid
40281      * @param {Boolean} preventMark True to disable marking the field invalid
40282      * @return {Boolean} True if the value is valid, else false
40283      */
40284     isValid : function(preventMark){
40285         if(this.disabled){
40286             return true;
40287         }
40288         var restore = this.preventMark;
40289         this.preventMark = preventMark === true;
40290         var v = this.validateValue(this.processValue(this.getRawValue()));
40291         this.preventMark = restore;
40292         return v;
40293     },
40294
40295     /**
40296      * Validates the field value
40297      * @return {Boolean} True if the value is valid, else false
40298      */
40299     validate : function(){
40300         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40301             this.clearInvalid();
40302             return true;
40303         }
40304         return false;
40305     },
40306
40307     processValue : function(value){
40308         return value;
40309     },
40310
40311     // private
40312     // Subclasses should provide the validation implementation by overriding this
40313     validateValue : function(value){
40314         return true;
40315     },
40316
40317     /**
40318      * Mark this field as invalid
40319      * @param {String} msg The validation message
40320      */
40321     markInvalid : function(msg){
40322         if(!this.rendered || this.preventMark){ // not rendered
40323             return;
40324         }
40325         
40326         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40327         
40328         obj.el.addClass(this.invalidClass);
40329         msg = msg || this.invalidText;
40330         switch(this.msgTarget){
40331             case 'qtip':
40332                 obj.el.dom.qtip = msg;
40333                 obj.el.dom.qclass = 'x-form-invalid-tip';
40334                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40335                     Roo.QuickTips.enable();
40336                 }
40337                 break;
40338             case 'title':
40339                 this.el.dom.title = msg;
40340                 break;
40341             case 'under':
40342                 if(!this.errorEl){
40343                     var elp = this.el.findParent('.x-form-element', 5, true);
40344                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40345                     this.errorEl.setWidth(elp.getWidth(true)-20);
40346                 }
40347                 this.errorEl.update(msg);
40348                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40349                 break;
40350             case 'side':
40351                 if(!this.errorIcon){
40352                     var elp = this.el.findParent('.x-form-element', 5, true);
40353                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40354                 }
40355                 this.alignErrorIcon();
40356                 this.errorIcon.dom.qtip = msg;
40357                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40358                 this.errorIcon.show();
40359                 this.on('resize', this.alignErrorIcon, this);
40360                 break;
40361             default:
40362                 var t = Roo.getDom(this.msgTarget);
40363                 t.innerHTML = msg;
40364                 t.style.display = this.msgDisplay;
40365                 break;
40366         }
40367         this.fireEvent('invalid', this, msg);
40368     },
40369
40370     // private
40371     alignErrorIcon : function(){
40372         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40373     },
40374
40375     /**
40376      * Clear any invalid styles/messages for this field
40377      */
40378     clearInvalid : function(){
40379         if(!this.rendered || this.preventMark){ // not rendered
40380             return;
40381         }
40382         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40383         
40384         obj.el.removeClass(this.invalidClass);
40385         switch(this.msgTarget){
40386             case 'qtip':
40387                 obj.el.dom.qtip = '';
40388                 break;
40389             case 'title':
40390                 this.el.dom.title = '';
40391                 break;
40392             case 'under':
40393                 if(this.errorEl){
40394                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40395                 }
40396                 break;
40397             case 'side':
40398                 if(this.errorIcon){
40399                     this.errorIcon.dom.qtip = '';
40400                     this.errorIcon.hide();
40401                     this.un('resize', this.alignErrorIcon, this);
40402                 }
40403                 break;
40404             default:
40405                 var t = Roo.getDom(this.msgTarget);
40406                 t.innerHTML = '';
40407                 t.style.display = 'none';
40408                 break;
40409         }
40410         this.fireEvent('valid', this);
40411     },
40412
40413     /**
40414      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40415      * @return {Mixed} value The field value
40416      */
40417     getRawValue : function(){
40418         var v = this.el.getValue();
40419         
40420         return v;
40421     },
40422
40423     /**
40424      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40425      * @return {Mixed} value The field value
40426      */
40427     getValue : function(){
40428         var v = this.el.getValue();
40429          
40430         return v;
40431     },
40432
40433     /**
40434      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40435      * @param {Mixed} value The value to set
40436      */
40437     setRawValue : function(v){
40438         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40439     },
40440
40441     /**
40442      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40443      * @param {Mixed} value The value to set
40444      */
40445     setValue : function(v){
40446         this.value = v;
40447         if(this.rendered){
40448             this.el.dom.value = (v === null || v === undefined ? '' : v);
40449              this.validate();
40450         }
40451     },
40452
40453     adjustSize : function(w, h){
40454         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40455         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40456         return s;
40457     },
40458
40459     adjustWidth : function(tag, w){
40460         tag = tag.toLowerCase();
40461         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40462             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40463                 if(tag == 'input'){
40464                     return w + 2;
40465                 }
40466                 if(tag == 'textarea'){
40467                     return w-2;
40468                 }
40469             }else if(Roo.isOpera){
40470                 if(tag == 'input'){
40471                     return w + 2;
40472                 }
40473                 if(tag == 'textarea'){
40474                     return w-2;
40475                 }
40476             }
40477         }
40478         return w;
40479     }
40480 });
40481
40482
40483 // anything other than normal should be considered experimental
40484 Roo.form.Field.msgFx = {
40485     normal : {
40486         show: function(msgEl, f){
40487             msgEl.setDisplayed('block');
40488         },
40489
40490         hide : function(msgEl, f){
40491             msgEl.setDisplayed(false).update('');
40492         }
40493     },
40494
40495     slide : {
40496         show: function(msgEl, f){
40497             msgEl.slideIn('t', {stopFx:true});
40498         },
40499
40500         hide : function(msgEl, f){
40501             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40502         }
40503     },
40504
40505     slideRight : {
40506         show: function(msgEl, f){
40507             msgEl.fixDisplay();
40508             msgEl.alignTo(f.el, 'tl-tr');
40509             msgEl.slideIn('l', {stopFx:true});
40510         },
40511
40512         hide : function(msgEl, f){
40513             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40514         }
40515     }
40516 };/*
40517  * Based on:
40518  * Ext JS Library 1.1.1
40519  * Copyright(c) 2006-2007, Ext JS, LLC.
40520  *
40521  * Originally Released Under LGPL - original licence link has changed is not relivant.
40522  *
40523  * Fork - LGPL
40524  * <script type="text/javascript">
40525  */
40526  
40527
40528 /**
40529  * @class Roo.form.TextField
40530  * @extends Roo.form.Field
40531  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40532  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40533  * @constructor
40534  * Creates a new TextField
40535  * @param {Object} config Configuration options
40536  */
40537 Roo.form.TextField = function(config){
40538     Roo.form.TextField.superclass.constructor.call(this, config);
40539     this.addEvents({
40540         /**
40541          * @event autosize
40542          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40543          * according to the default logic, but this event provides a hook for the developer to apply additional
40544          * logic at runtime to resize the field if needed.
40545              * @param {Roo.form.Field} this This text field
40546              * @param {Number} width The new field width
40547              */
40548         autosize : true
40549     });
40550 };
40551
40552 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40553     /**
40554      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40555      */
40556     grow : false,
40557     /**
40558      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40559      */
40560     growMin : 30,
40561     /**
40562      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40563      */
40564     growMax : 800,
40565     /**
40566      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40567      */
40568     vtype : null,
40569     /**
40570      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40571      */
40572     maskRe : null,
40573     /**
40574      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40575      */
40576     disableKeyFilter : false,
40577     /**
40578      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40579      */
40580     allowBlank : true,
40581     /**
40582      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40583      */
40584     minLength : 0,
40585     /**
40586      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40587      */
40588     maxLength : Number.MAX_VALUE,
40589     /**
40590      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40591      */
40592     minLengthText : "The minimum length for this field is {0}",
40593     /**
40594      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40595      */
40596     maxLengthText : "The maximum length for this field is {0}",
40597     /**
40598      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40599      */
40600     selectOnFocus : false,
40601     /**
40602      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40603      */    
40604     allowLeadingSpace : false,
40605     /**
40606      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40607      */
40608     blankText : "This field is required",
40609     /**
40610      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40611      * If available, this function will be called only after the basic validators all return true, and will be passed the
40612      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40613      */
40614     validator : null,
40615     /**
40616      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40617      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40618      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40619      */
40620     regex : null,
40621     /**
40622      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40623      */
40624     regexText : "",
40625     /**
40626      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40627      */
40628     emptyText : null,
40629    
40630
40631     // private
40632     initEvents : function()
40633     {
40634         if (this.emptyText) {
40635             this.el.attr('placeholder', this.emptyText);
40636         }
40637         
40638         Roo.form.TextField.superclass.initEvents.call(this);
40639         if(this.validationEvent == 'keyup'){
40640             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40641             this.el.on('keyup', this.filterValidation, this);
40642         }
40643         else if(this.validationEvent !== false){
40644             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40645         }
40646         
40647         if(this.selectOnFocus){
40648             this.on("focus", this.preFocus, this);
40649         }
40650         if (!this.allowLeadingSpace) {
40651             this.on('blur', this.cleanLeadingSpace, this);
40652         }
40653         
40654         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40655             this.el.on("keypress", this.filterKeys, this);
40656         }
40657         if(this.grow){
40658             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40659             this.el.on("click", this.autoSize,  this);
40660         }
40661         if(this.el.is('input[type=password]') && Roo.isSafari){
40662             this.el.on('keydown', this.SafariOnKeyDown, this);
40663         }
40664     },
40665
40666     processValue : function(value){
40667         if(this.stripCharsRe){
40668             var newValue = value.replace(this.stripCharsRe, '');
40669             if(newValue !== value){
40670                 this.setRawValue(newValue);
40671                 return newValue;
40672             }
40673         }
40674         return value;
40675     },
40676
40677     filterValidation : function(e){
40678         if(!e.isNavKeyPress()){
40679             this.validationTask.delay(this.validationDelay);
40680         }
40681     },
40682
40683     // private
40684     onKeyUp : function(e){
40685         if(!e.isNavKeyPress()){
40686             this.autoSize();
40687         }
40688     },
40689     // private - clean the leading white space
40690     cleanLeadingSpace : function(e)
40691     {
40692         if ( this.inputType == 'file') {
40693             return;
40694         }
40695         
40696         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40697     },
40698     /**
40699      * Resets the current field value to the originally-loaded value and clears any validation messages.
40700      *  
40701      */
40702     reset : function(){
40703         Roo.form.TextField.superclass.reset.call(this);
40704        
40705     }, 
40706     // private
40707     preFocus : function(){
40708         
40709         if(this.selectOnFocus){
40710             this.el.dom.select();
40711         }
40712     },
40713
40714     
40715     // private
40716     filterKeys : function(e){
40717         var k = e.getKey();
40718         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40719             return;
40720         }
40721         var c = e.getCharCode(), cc = String.fromCharCode(c);
40722         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40723             return;
40724         }
40725         if(!this.maskRe.test(cc)){
40726             e.stopEvent();
40727         }
40728     },
40729
40730     setValue : function(v){
40731         
40732         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40733         
40734         this.autoSize();
40735     },
40736
40737     /**
40738      * Validates a value according to the field's validation rules and marks the field as invalid
40739      * if the validation fails
40740      * @param {Mixed} value The value to validate
40741      * @return {Boolean} True if the value is valid, else false
40742      */
40743     validateValue : function(value){
40744         if(value.length < 1)  { // if it's blank
40745              if(this.allowBlank){
40746                 this.clearInvalid();
40747                 return true;
40748              }else{
40749                 this.markInvalid(this.blankText);
40750                 return false;
40751              }
40752         }
40753         if(value.length < this.minLength){
40754             this.markInvalid(String.format(this.minLengthText, this.minLength));
40755             return false;
40756         }
40757         if(value.length > this.maxLength){
40758             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40759             return false;
40760         }
40761         if(this.vtype){
40762             var vt = Roo.form.VTypes;
40763             if(!vt[this.vtype](value, this)){
40764                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40765                 return false;
40766             }
40767         }
40768         if(typeof this.validator == "function"){
40769             var msg = this.validator(value);
40770             if(msg !== true){
40771                 this.markInvalid(msg);
40772                 return false;
40773             }
40774         }
40775         if(this.regex && !this.regex.test(value)){
40776             this.markInvalid(this.regexText);
40777             return false;
40778         }
40779         return true;
40780     },
40781
40782     /**
40783      * Selects text in this field
40784      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40785      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40786      */
40787     selectText : function(start, end){
40788         var v = this.getRawValue();
40789         if(v.length > 0){
40790             start = start === undefined ? 0 : start;
40791             end = end === undefined ? v.length : end;
40792             var d = this.el.dom;
40793             if(d.setSelectionRange){
40794                 d.setSelectionRange(start, end);
40795             }else if(d.createTextRange){
40796                 var range = d.createTextRange();
40797                 range.moveStart("character", start);
40798                 range.moveEnd("character", v.length-end);
40799                 range.select();
40800             }
40801         }
40802     },
40803
40804     /**
40805      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40806      * This only takes effect if grow = true, and fires the autosize event.
40807      */
40808     autoSize : function(){
40809         if(!this.grow || !this.rendered){
40810             return;
40811         }
40812         if(!this.metrics){
40813             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40814         }
40815         var el = this.el;
40816         var v = el.dom.value;
40817         var d = document.createElement('div');
40818         d.appendChild(document.createTextNode(v));
40819         v = d.innerHTML;
40820         d = null;
40821         v += "&#160;";
40822         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40823         this.el.setWidth(w);
40824         this.fireEvent("autosize", this, w);
40825     },
40826     
40827     // private
40828     SafariOnKeyDown : function(event)
40829     {
40830         // this is a workaround for a password hang bug on chrome/ webkit.
40831         
40832         var isSelectAll = false;
40833         
40834         if(this.el.dom.selectionEnd > 0){
40835             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40836         }
40837         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40838             event.preventDefault();
40839             this.setValue('');
40840             return;
40841         }
40842         
40843         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40844             
40845             event.preventDefault();
40846             // this is very hacky as keydown always get's upper case.
40847             
40848             var cc = String.fromCharCode(event.getCharCode());
40849             
40850             
40851             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40852             
40853         }
40854         
40855         
40856     }
40857 });/*
40858  * Based on:
40859  * Ext JS Library 1.1.1
40860  * Copyright(c) 2006-2007, Ext JS, LLC.
40861  *
40862  * Originally Released Under LGPL - original licence link has changed is not relivant.
40863  *
40864  * Fork - LGPL
40865  * <script type="text/javascript">
40866  */
40867  
40868 /**
40869  * @class Roo.form.Hidden
40870  * @extends Roo.form.TextField
40871  * Simple Hidden element used on forms 
40872  * 
40873  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40874  * 
40875  * @constructor
40876  * Creates a new Hidden form element.
40877  * @param {Object} config Configuration options
40878  */
40879
40880
40881
40882 // easy hidden field...
40883 Roo.form.Hidden = function(config){
40884     Roo.form.Hidden.superclass.constructor.call(this, config);
40885 };
40886   
40887 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40888     fieldLabel:      '',
40889     inputType:      'hidden',
40890     width:          50,
40891     allowBlank:     true,
40892     labelSeparator: '',
40893     hidden:         true,
40894     itemCls :       'x-form-item-display-none'
40895
40896
40897 });
40898
40899
40900 /*
40901  * Based on:
40902  * Ext JS Library 1.1.1
40903  * Copyright(c) 2006-2007, Ext JS, LLC.
40904  *
40905  * Originally Released Under LGPL - original licence link has changed is not relivant.
40906  *
40907  * Fork - LGPL
40908  * <script type="text/javascript">
40909  */
40910  
40911 /**
40912  * @class Roo.form.TriggerField
40913  * @extends Roo.form.TextField
40914  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40915  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40916  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40917  * for which you can provide a custom implementation.  For example:
40918  * <pre><code>
40919 var trigger = new Roo.form.TriggerField();
40920 trigger.onTriggerClick = myTriggerFn;
40921 trigger.applyTo('my-field');
40922 </code></pre>
40923  *
40924  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40925  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40926  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40927  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40928  * @constructor
40929  * Create a new TriggerField.
40930  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40931  * to the base TextField)
40932  */
40933 Roo.form.TriggerField = function(config){
40934     this.mimicing = false;
40935     Roo.form.TriggerField.superclass.constructor.call(this, config);
40936 };
40937
40938 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40939     /**
40940      * @cfg {String} triggerClass A CSS class to apply to the trigger
40941      */
40942     /**
40943      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40944      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40945      */
40946     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40947     /**
40948      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40949      */
40950     hideTrigger:false,
40951
40952     /** @cfg {Boolean} grow @hide */
40953     /** @cfg {Number} growMin @hide */
40954     /** @cfg {Number} growMax @hide */
40955
40956     /**
40957      * @hide 
40958      * @method
40959      */
40960     autoSize: Roo.emptyFn,
40961     // private
40962     monitorTab : true,
40963     // private
40964     deferHeight : true,
40965
40966     
40967     actionMode : 'wrap',
40968     // private
40969     onResize : function(w, h){
40970         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40971         if(typeof w == 'number'){
40972             var x = w - this.trigger.getWidth();
40973             this.el.setWidth(this.adjustWidth('input', x));
40974             this.trigger.setStyle('left', x+'px');
40975         }
40976     },
40977
40978     // private
40979     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40980
40981     // private
40982     getResizeEl : function(){
40983         return this.wrap;
40984     },
40985
40986     // private
40987     getPositionEl : function(){
40988         return this.wrap;
40989     },
40990
40991     // private
40992     alignErrorIcon : function(){
40993         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40994     },
40995
40996     // private
40997     onRender : function(ct, position){
40998         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40999         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41000         this.trigger = this.wrap.createChild(this.triggerConfig ||
41001                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41002         if(this.hideTrigger){
41003             this.trigger.setDisplayed(false);
41004         }
41005         this.initTrigger();
41006         if(!this.width){
41007             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41008         }
41009     },
41010
41011     // private
41012     initTrigger : function(){
41013         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41014         this.trigger.addClassOnOver('x-form-trigger-over');
41015         this.trigger.addClassOnClick('x-form-trigger-click');
41016     },
41017
41018     // private
41019     onDestroy : function(){
41020         if(this.trigger){
41021             this.trigger.removeAllListeners();
41022             this.trigger.remove();
41023         }
41024         if(this.wrap){
41025             this.wrap.remove();
41026         }
41027         Roo.form.TriggerField.superclass.onDestroy.call(this);
41028     },
41029
41030     // private
41031     onFocus : function(){
41032         Roo.form.TriggerField.superclass.onFocus.call(this);
41033         if(!this.mimicing){
41034             this.wrap.addClass('x-trigger-wrap-focus');
41035             this.mimicing = true;
41036             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41037             if(this.monitorTab){
41038                 this.el.on("keydown", this.checkTab, this);
41039             }
41040         }
41041     },
41042
41043     // private
41044     checkTab : function(e){
41045         if(e.getKey() == e.TAB){
41046             this.triggerBlur();
41047         }
41048     },
41049
41050     // private
41051     onBlur : function(){
41052         // do nothing
41053     },
41054
41055     // private
41056     mimicBlur : function(e, t){
41057         if(!this.wrap.contains(t) && this.validateBlur()){
41058             this.triggerBlur();
41059         }
41060     },
41061
41062     // private
41063     triggerBlur : function(){
41064         this.mimicing = false;
41065         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41066         if(this.monitorTab){
41067             this.el.un("keydown", this.checkTab, this);
41068         }
41069         this.wrap.removeClass('x-trigger-wrap-focus');
41070         Roo.form.TriggerField.superclass.onBlur.call(this);
41071     },
41072
41073     // private
41074     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41075     validateBlur : function(e, t){
41076         return true;
41077     },
41078
41079     // private
41080     onDisable : function(){
41081         Roo.form.TriggerField.superclass.onDisable.call(this);
41082         if(this.wrap){
41083             this.wrap.addClass('x-item-disabled');
41084         }
41085     },
41086
41087     // private
41088     onEnable : function(){
41089         Roo.form.TriggerField.superclass.onEnable.call(this);
41090         if(this.wrap){
41091             this.wrap.removeClass('x-item-disabled');
41092         }
41093     },
41094
41095     // private
41096     onShow : function(){
41097         var ae = this.getActionEl();
41098         
41099         if(ae){
41100             ae.dom.style.display = '';
41101             ae.dom.style.visibility = 'visible';
41102         }
41103     },
41104
41105     // private
41106     
41107     onHide : function(){
41108         var ae = this.getActionEl();
41109         ae.dom.style.display = 'none';
41110     },
41111
41112     /**
41113      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41114      * by an implementing function.
41115      * @method
41116      * @param {EventObject} e
41117      */
41118     onTriggerClick : Roo.emptyFn
41119 });
41120
41121 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41122 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41123 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41124 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41125     initComponent : function(){
41126         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41127
41128         this.triggerConfig = {
41129             tag:'span', cls:'x-form-twin-triggers', cn:[
41130             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41131             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41132         ]};
41133     },
41134
41135     getTrigger : function(index){
41136         return this.triggers[index];
41137     },
41138
41139     initTrigger : function(){
41140         var ts = this.trigger.select('.x-form-trigger', true);
41141         this.wrap.setStyle('overflow', 'hidden');
41142         var triggerField = this;
41143         ts.each(function(t, all, index){
41144             t.hide = function(){
41145                 var w = triggerField.wrap.getWidth();
41146                 this.dom.style.display = 'none';
41147                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41148             };
41149             t.show = function(){
41150                 var w = triggerField.wrap.getWidth();
41151                 this.dom.style.display = '';
41152                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41153             };
41154             var triggerIndex = 'Trigger'+(index+1);
41155
41156             if(this['hide'+triggerIndex]){
41157                 t.dom.style.display = 'none';
41158             }
41159             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41160             t.addClassOnOver('x-form-trigger-over');
41161             t.addClassOnClick('x-form-trigger-click');
41162         }, this);
41163         this.triggers = ts.elements;
41164     },
41165
41166     onTrigger1Click : Roo.emptyFn,
41167     onTrigger2Click : Roo.emptyFn
41168 });/*
41169  * Based on:
41170  * Ext JS Library 1.1.1
41171  * Copyright(c) 2006-2007, Ext JS, LLC.
41172  *
41173  * Originally Released Under LGPL - original licence link has changed is not relivant.
41174  *
41175  * Fork - LGPL
41176  * <script type="text/javascript">
41177  */
41178  
41179 /**
41180  * @class Roo.form.TextArea
41181  * @extends Roo.form.TextField
41182  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41183  * support for auto-sizing.
41184  * @constructor
41185  * Creates a new TextArea
41186  * @param {Object} config Configuration options
41187  */
41188 Roo.form.TextArea = function(config){
41189     Roo.form.TextArea.superclass.constructor.call(this, config);
41190     // these are provided exchanges for backwards compat
41191     // minHeight/maxHeight were replaced by growMin/growMax to be
41192     // compatible with TextField growing config values
41193     if(this.minHeight !== undefined){
41194         this.growMin = this.minHeight;
41195     }
41196     if(this.maxHeight !== undefined){
41197         this.growMax = this.maxHeight;
41198     }
41199 };
41200
41201 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41202     /**
41203      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41204      */
41205     growMin : 60,
41206     /**
41207      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41208      */
41209     growMax: 1000,
41210     /**
41211      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41212      * in the field (equivalent to setting overflow: hidden, defaults to false)
41213      */
41214     preventScrollbars: false,
41215     /**
41216      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41217      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41218      */
41219
41220     // private
41221     onRender : function(ct, position){
41222         if(!this.el){
41223             this.defaultAutoCreate = {
41224                 tag: "textarea",
41225                 style:"width:300px;height:60px;",
41226                 autocomplete: "new-password"
41227             };
41228         }
41229         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41230         if(this.grow){
41231             this.textSizeEl = Roo.DomHelper.append(document.body, {
41232                 tag: "pre", cls: "x-form-grow-sizer"
41233             });
41234             if(this.preventScrollbars){
41235                 this.el.setStyle("overflow", "hidden");
41236             }
41237             this.el.setHeight(this.growMin);
41238         }
41239     },
41240
41241     onDestroy : function(){
41242         if(this.textSizeEl){
41243             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41244         }
41245         Roo.form.TextArea.superclass.onDestroy.call(this);
41246     },
41247
41248     // private
41249     onKeyUp : function(e){
41250         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41251             this.autoSize();
41252         }
41253     },
41254
41255     /**
41256      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41257      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41258      */
41259     autoSize : function(){
41260         if(!this.grow || !this.textSizeEl){
41261             return;
41262         }
41263         var el = this.el;
41264         var v = el.dom.value;
41265         var ts = this.textSizeEl;
41266
41267         ts.innerHTML = '';
41268         ts.appendChild(document.createTextNode(v));
41269         v = ts.innerHTML;
41270
41271         Roo.fly(ts).setWidth(this.el.getWidth());
41272         if(v.length < 1){
41273             v = "&#160;&#160;";
41274         }else{
41275             if(Roo.isIE){
41276                 v = v.replace(/\n/g, '<p>&#160;</p>');
41277             }
41278             v += "&#160;\n&#160;";
41279         }
41280         ts.innerHTML = v;
41281         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41282         if(h != this.lastHeight){
41283             this.lastHeight = h;
41284             this.el.setHeight(h);
41285             this.fireEvent("autosize", this, h);
41286         }
41287     }
41288 });/*
41289  * Based on:
41290  * Ext JS Library 1.1.1
41291  * Copyright(c) 2006-2007, Ext JS, LLC.
41292  *
41293  * Originally Released Under LGPL - original licence link has changed is not relivant.
41294  *
41295  * Fork - LGPL
41296  * <script type="text/javascript">
41297  */
41298  
41299
41300 /**
41301  * @class Roo.form.NumberField
41302  * @extends Roo.form.TextField
41303  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41304  * @constructor
41305  * Creates a new NumberField
41306  * @param {Object} config Configuration options
41307  */
41308 Roo.form.NumberField = function(config){
41309     Roo.form.NumberField.superclass.constructor.call(this, config);
41310 };
41311
41312 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41313     /**
41314      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41315      */
41316     fieldClass: "x-form-field x-form-num-field",
41317     /**
41318      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41319      */
41320     allowDecimals : true,
41321     /**
41322      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41323      */
41324     decimalSeparator : ".",
41325     /**
41326      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41327      */
41328     decimalPrecision : 2,
41329     /**
41330      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41331      */
41332     allowNegative : true,
41333     /**
41334      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41335      */
41336     minValue : Number.NEGATIVE_INFINITY,
41337     /**
41338      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41339      */
41340     maxValue : Number.MAX_VALUE,
41341     /**
41342      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41343      */
41344     minText : "The minimum value for this field is {0}",
41345     /**
41346      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41347      */
41348     maxText : "The maximum value for this field is {0}",
41349     /**
41350      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41351      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41352      */
41353     nanText : "{0} is not a valid number",
41354
41355     // private
41356     initEvents : function(){
41357         Roo.form.NumberField.superclass.initEvents.call(this);
41358         var allowed = "0123456789";
41359         if(this.allowDecimals){
41360             allowed += this.decimalSeparator;
41361         }
41362         if(this.allowNegative){
41363             allowed += "-";
41364         }
41365         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41366         var keyPress = function(e){
41367             var k = e.getKey();
41368             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41369                 return;
41370             }
41371             var c = e.getCharCode();
41372             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41373                 e.stopEvent();
41374             }
41375         };
41376         this.el.on("keypress", keyPress, this);
41377     },
41378
41379     // private
41380     validateValue : function(value){
41381         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41382             return false;
41383         }
41384         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41385              return true;
41386         }
41387         var num = this.parseValue(value);
41388         if(isNaN(num)){
41389             this.markInvalid(String.format(this.nanText, value));
41390             return false;
41391         }
41392         if(num < this.minValue){
41393             this.markInvalid(String.format(this.minText, this.minValue));
41394             return false;
41395         }
41396         if(num > this.maxValue){
41397             this.markInvalid(String.format(this.maxText, this.maxValue));
41398             return false;
41399         }
41400         return true;
41401     },
41402
41403     getValue : function(){
41404         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41405     },
41406
41407     // private
41408     parseValue : function(value){
41409         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41410         return isNaN(value) ? '' : value;
41411     },
41412
41413     // private
41414     fixPrecision : function(value){
41415         var nan = isNaN(value);
41416         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41417             return nan ? '' : value;
41418         }
41419         return parseFloat(value).toFixed(this.decimalPrecision);
41420     },
41421
41422     setValue : function(v){
41423         v = this.fixPrecision(v);
41424         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41425     },
41426
41427     // private
41428     decimalPrecisionFcn : function(v){
41429         return Math.floor(v);
41430     },
41431
41432     beforeBlur : function(){
41433         var v = this.parseValue(this.getRawValue());
41434         if(v){
41435             this.setValue(v);
41436         }
41437     }
41438 });/*
41439  * Based on:
41440  * Ext JS Library 1.1.1
41441  * Copyright(c) 2006-2007, Ext JS, LLC.
41442  *
41443  * Originally Released Under LGPL - original licence link has changed is not relivant.
41444  *
41445  * Fork - LGPL
41446  * <script type="text/javascript">
41447  */
41448  
41449 /**
41450  * @class Roo.form.DateField
41451  * @extends Roo.form.TriggerField
41452  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41453 * @constructor
41454 * Create a new DateField
41455 * @param {Object} config
41456  */
41457 Roo.form.DateField = function(config)
41458 {
41459     Roo.form.DateField.superclass.constructor.call(this, config);
41460     
41461       this.addEvents({
41462          
41463         /**
41464          * @event select
41465          * Fires when a date is selected
41466              * @param {Roo.form.DateField} combo This combo box
41467              * @param {Date} date The date selected
41468              */
41469         'select' : true
41470          
41471     });
41472     
41473     
41474     if(typeof this.minValue == "string") {
41475         this.minValue = this.parseDate(this.minValue);
41476     }
41477     if(typeof this.maxValue == "string") {
41478         this.maxValue = this.parseDate(this.maxValue);
41479     }
41480     this.ddMatch = null;
41481     if(this.disabledDates){
41482         var dd = this.disabledDates;
41483         var re = "(?:";
41484         for(var i = 0; i < dd.length; i++){
41485             re += dd[i];
41486             if(i != dd.length-1) {
41487                 re += "|";
41488             }
41489         }
41490         this.ddMatch = new RegExp(re + ")");
41491     }
41492 };
41493
41494 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41495     /**
41496      * @cfg {String} format
41497      * The default date format string which can be overriden for localization support.  The format must be
41498      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41499      */
41500     format : "m/d/y",
41501     /**
41502      * @cfg {String} altFormats
41503      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41504      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41505      */
41506     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41507     /**
41508      * @cfg {Array} disabledDays
41509      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41510      */
41511     disabledDays : null,
41512     /**
41513      * @cfg {String} disabledDaysText
41514      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41515      */
41516     disabledDaysText : "Disabled",
41517     /**
41518      * @cfg {Array} disabledDates
41519      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41520      * expression so they are very powerful. Some examples:
41521      * <ul>
41522      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41523      * <li>["03/08", "09/16"] would disable those days for every year</li>
41524      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41525      * <li>["03/../2006"] would disable every day in March 2006</li>
41526      * <li>["^03"] would disable every day in every March</li>
41527      * </ul>
41528      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41529      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41530      */
41531     disabledDates : null,
41532     /**
41533      * @cfg {String} disabledDatesText
41534      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41535      */
41536     disabledDatesText : "Disabled",
41537     /**
41538      * @cfg {Date/String} minValue
41539      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41540      * valid format (defaults to null).
41541      */
41542     minValue : null,
41543     /**
41544      * @cfg {Date/String} maxValue
41545      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41546      * valid format (defaults to null).
41547      */
41548     maxValue : null,
41549     /**
41550      * @cfg {String} minText
41551      * The error text to display when the date in the cell is before minValue (defaults to
41552      * 'The date in this field must be after {minValue}').
41553      */
41554     minText : "The date in this field must be equal to or after {0}",
41555     /**
41556      * @cfg {String} maxText
41557      * The error text to display when the date in the cell is after maxValue (defaults to
41558      * 'The date in this field must be before {maxValue}').
41559      */
41560     maxText : "The date in this field must be equal to or before {0}",
41561     /**
41562      * @cfg {String} invalidText
41563      * The error text to display when the date in the field is invalid (defaults to
41564      * '{value} is not a valid date - it must be in the format {format}').
41565      */
41566     invalidText : "{0} is not a valid date - it must be in the format {1}",
41567     /**
41568      * @cfg {String} triggerClass
41569      * An additional CSS class used to style the trigger button.  The trigger will always get the
41570      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41571      * which displays a calendar icon).
41572      */
41573     triggerClass : 'x-form-date-trigger',
41574     
41575
41576     /**
41577      * @cfg {Boolean} useIso
41578      * if enabled, then the date field will use a hidden field to store the 
41579      * real value as iso formated date. default (false)
41580      */ 
41581     useIso : false,
41582     /**
41583      * @cfg {String/Object} autoCreate
41584      * A DomHelper element spec, or true for a default element spec (defaults to
41585      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41586      */ 
41587     // private
41588     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41589     
41590     // private
41591     hiddenField: false,
41592     
41593     onRender : function(ct, position)
41594     {
41595         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41596         if (this.useIso) {
41597             //this.el.dom.removeAttribute('name'); 
41598             Roo.log("Changing name?");
41599             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41600             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41601                     'before', true);
41602             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41603             // prevent input submission
41604             this.hiddenName = this.name;
41605         }
41606             
41607             
41608     },
41609     
41610     // private
41611     validateValue : function(value)
41612     {
41613         value = this.formatDate(value);
41614         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41615             Roo.log('super failed');
41616             return false;
41617         }
41618         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41619              return true;
41620         }
41621         var svalue = value;
41622         value = this.parseDate(value);
41623         if(!value){
41624             Roo.log('parse date failed' + svalue);
41625             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41626             return false;
41627         }
41628         var time = value.getTime();
41629         if(this.minValue && time < this.minValue.getTime()){
41630             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41631             return false;
41632         }
41633         if(this.maxValue && time > this.maxValue.getTime()){
41634             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41635             return false;
41636         }
41637         if(this.disabledDays){
41638             var day = value.getDay();
41639             for(var i = 0; i < this.disabledDays.length; i++) {
41640                 if(day === this.disabledDays[i]){
41641                     this.markInvalid(this.disabledDaysText);
41642                     return false;
41643                 }
41644             }
41645         }
41646         var fvalue = this.formatDate(value);
41647         if(this.ddMatch && this.ddMatch.test(fvalue)){
41648             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41649             return false;
41650         }
41651         return true;
41652     },
41653
41654     // private
41655     // Provides logic to override the default TriggerField.validateBlur which just returns true
41656     validateBlur : function(){
41657         return !this.menu || !this.menu.isVisible();
41658     },
41659     
41660     getName: function()
41661     {
41662         // returns hidden if it's set..
41663         if (!this.rendered) {return ''};
41664         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41665         
41666     },
41667
41668     /**
41669      * Returns the current date value of the date field.
41670      * @return {Date} The date value
41671      */
41672     getValue : function(){
41673         
41674         return  this.hiddenField ?
41675                 this.hiddenField.value :
41676                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41677     },
41678
41679     /**
41680      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41681      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41682      * (the default format used is "m/d/y").
41683      * <br />Usage:
41684      * <pre><code>
41685 //All of these calls set the same date value (May 4, 2006)
41686
41687 //Pass a date object:
41688 var dt = new Date('5/4/06');
41689 dateField.setValue(dt);
41690
41691 //Pass a date string (default format):
41692 dateField.setValue('5/4/06');
41693
41694 //Pass a date string (custom format):
41695 dateField.format = 'Y-m-d';
41696 dateField.setValue('2006-5-4');
41697 </code></pre>
41698      * @param {String/Date} date The date or valid date string
41699      */
41700     setValue : function(date){
41701         if (this.hiddenField) {
41702             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41703         }
41704         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41705         // make sure the value field is always stored as a date..
41706         this.value = this.parseDate(date);
41707         
41708         
41709     },
41710
41711     // private
41712     parseDate : function(value){
41713         if(!value || value instanceof Date){
41714             return value;
41715         }
41716         var v = Date.parseDate(value, this.format);
41717          if (!v && this.useIso) {
41718             v = Date.parseDate(value, 'Y-m-d');
41719         }
41720         if(!v && this.altFormats){
41721             if(!this.altFormatsArray){
41722                 this.altFormatsArray = this.altFormats.split("|");
41723             }
41724             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41725                 v = Date.parseDate(value, this.altFormatsArray[i]);
41726             }
41727         }
41728         return v;
41729     },
41730
41731     // private
41732     formatDate : function(date, fmt){
41733         return (!date || !(date instanceof Date)) ?
41734                date : date.dateFormat(fmt || this.format);
41735     },
41736
41737     // private
41738     menuListeners : {
41739         select: function(m, d){
41740             
41741             this.setValue(d);
41742             this.fireEvent('select', this, d);
41743         },
41744         show : function(){ // retain focus styling
41745             this.onFocus();
41746         },
41747         hide : function(){
41748             this.focus.defer(10, this);
41749             var ml = this.menuListeners;
41750             this.menu.un("select", ml.select,  this);
41751             this.menu.un("show", ml.show,  this);
41752             this.menu.un("hide", ml.hide,  this);
41753         }
41754     },
41755
41756     // private
41757     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41758     onTriggerClick : function(){
41759         if(this.disabled){
41760             return;
41761         }
41762         if(this.menu == null){
41763             this.menu = new Roo.menu.DateMenu();
41764         }
41765         Roo.apply(this.menu.picker,  {
41766             showClear: this.allowBlank,
41767             minDate : this.minValue,
41768             maxDate : this.maxValue,
41769             disabledDatesRE : this.ddMatch,
41770             disabledDatesText : this.disabledDatesText,
41771             disabledDays : this.disabledDays,
41772             disabledDaysText : this.disabledDaysText,
41773             format : this.useIso ? 'Y-m-d' : this.format,
41774             minText : String.format(this.minText, this.formatDate(this.minValue)),
41775             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41776         });
41777         this.menu.on(Roo.apply({}, this.menuListeners, {
41778             scope:this
41779         }));
41780         this.menu.picker.setValue(this.getValue() || new Date());
41781         this.menu.show(this.el, "tl-bl?");
41782     },
41783
41784     beforeBlur : function(){
41785         var v = this.parseDate(this.getRawValue());
41786         if(v){
41787             this.setValue(v);
41788         }
41789     },
41790
41791     /*@
41792      * overide
41793      * 
41794      */
41795     isDirty : function() {
41796         if(this.disabled) {
41797             return false;
41798         }
41799         
41800         if(typeof(this.startValue) === 'undefined'){
41801             return false;
41802         }
41803         
41804         return String(this.getValue()) !== String(this.startValue);
41805         
41806     },
41807     // @overide
41808     cleanLeadingSpace : function(e)
41809     {
41810        return;
41811     }
41812     
41813 });/*
41814  * Based on:
41815  * Ext JS Library 1.1.1
41816  * Copyright(c) 2006-2007, Ext JS, LLC.
41817  *
41818  * Originally Released Under LGPL - original licence link has changed is not relivant.
41819  *
41820  * Fork - LGPL
41821  * <script type="text/javascript">
41822  */
41823  
41824 /**
41825  * @class Roo.form.MonthField
41826  * @extends Roo.form.TriggerField
41827  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41828 * @constructor
41829 * Create a new MonthField
41830 * @param {Object} config
41831  */
41832 Roo.form.MonthField = function(config){
41833     
41834     Roo.form.MonthField.superclass.constructor.call(this, config);
41835     
41836       this.addEvents({
41837          
41838         /**
41839          * @event select
41840          * Fires when a date is selected
41841              * @param {Roo.form.MonthFieeld} combo This combo box
41842              * @param {Date} date The date selected
41843              */
41844         'select' : true
41845          
41846     });
41847     
41848     
41849     if(typeof this.minValue == "string") {
41850         this.minValue = this.parseDate(this.minValue);
41851     }
41852     if(typeof this.maxValue == "string") {
41853         this.maxValue = this.parseDate(this.maxValue);
41854     }
41855     this.ddMatch = null;
41856     if(this.disabledDates){
41857         var dd = this.disabledDates;
41858         var re = "(?:";
41859         for(var i = 0; i < dd.length; i++){
41860             re += dd[i];
41861             if(i != dd.length-1) {
41862                 re += "|";
41863             }
41864         }
41865         this.ddMatch = new RegExp(re + ")");
41866     }
41867 };
41868
41869 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41870     /**
41871      * @cfg {String} format
41872      * The default date format string which can be overriden for localization support.  The format must be
41873      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41874      */
41875     format : "M Y",
41876     /**
41877      * @cfg {String} altFormats
41878      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41879      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41880      */
41881     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41882     /**
41883      * @cfg {Array} disabledDays
41884      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41885      */
41886     disabledDays : [0,1,2,3,4,5,6],
41887     /**
41888      * @cfg {String} disabledDaysText
41889      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41890      */
41891     disabledDaysText : "Disabled",
41892     /**
41893      * @cfg {Array} disabledDates
41894      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41895      * expression so they are very powerful. Some examples:
41896      * <ul>
41897      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41898      * <li>["03/08", "09/16"] would disable those days for every year</li>
41899      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41900      * <li>["03/../2006"] would disable every day in March 2006</li>
41901      * <li>["^03"] would disable every day in every March</li>
41902      * </ul>
41903      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41904      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41905      */
41906     disabledDates : null,
41907     /**
41908      * @cfg {String} disabledDatesText
41909      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41910      */
41911     disabledDatesText : "Disabled",
41912     /**
41913      * @cfg {Date/String} minValue
41914      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41915      * valid format (defaults to null).
41916      */
41917     minValue : null,
41918     /**
41919      * @cfg {Date/String} maxValue
41920      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41921      * valid format (defaults to null).
41922      */
41923     maxValue : null,
41924     /**
41925      * @cfg {String} minText
41926      * The error text to display when the date in the cell is before minValue (defaults to
41927      * 'The date in this field must be after {minValue}').
41928      */
41929     minText : "The date in this field must be equal to or after {0}",
41930     /**
41931      * @cfg {String} maxTextf
41932      * The error text to display when the date in the cell is after maxValue (defaults to
41933      * 'The date in this field must be before {maxValue}').
41934      */
41935     maxText : "The date in this field must be equal to or before {0}",
41936     /**
41937      * @cfg {String} invalidText
41938      * The error text to display when the date in the field is invalid (defaults to
41939      * '{value} is not a valid date - it must be in the format {format}').
41940      */
41941     invalidText : "{0} is not a valid date - it must be in the format {1}",
41942     /**
41943      * @cfg {String} triggerClass
41944      * An additional CSS class used to style the trigger button.  The trigger will always get the
41945      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41946      * which displays a calendar icon).
41947      */
41948     triggerClass : 'x-form-date-trigger',
41949     
41950
41951     /**
41952      * @cfg {Boolean} useIso
41953      * if enabled, then the date field will use a hidden field to store the 
41954      * real value as iso formated date. default (true)
41955      */ 
41956     useIso : true,
41957     /**
41958      * @cfg {String/Object} autoCreate
41959      * A DomHelper element spec, or true for a default element spec (defaults to
41960      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41961      */ 
41962     // private
41963     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41964     
41965     // private
41966     hiddenField: false,
41967     
41968     hideMonthPicker : false,
41969     
41970     onRender : function(ct, position)
41971     {
41972         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41973         if (this.useIso) {
41974             this.el.dom.removeAttribute('name'); 
41975             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41976                     'before', true);
41977             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41978             // prevent input submission
41979             this.hiddenName = this.name;
41980         }
41981             
41982             
41983     },
41984     
41985     // private
41986     validateValue : function(value)
41987     {
41988         value = this.formatDate(value);
41989         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41990             return false;
41991         }
41992         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41993              return true;
41994         }
41995         var svalue = value;
41996         value = this.parseDate(value);
41997         if(!value){
41998             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41999             return false;
42000         }
42001         var time = value.getTime();
42002         if(this.minValue && time < this.minValue.getTime()){
42003             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42004             return false;
42005         }
42006         if(this.maxValue && time > this.maxValue.getTime()){
42007             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42008             return false;
42009         }
42010         /*if(this.disabledDays){
42011             var day = value.getDay();
42012             for(var i = 0; i < this.disabledDays.length; i++) {
42013                 if(day === this.disabledDays[i]){
42014                     this.markInvalid(this.disabledDaysText);
42015                     return false;
42016                 }
42017             }
42018         }
42019         */
42020         var fvalue = this.formatDate(value);
42021         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42022             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42023             return false;
42024         }
42025         */
42026         return true;
42027     },
42028
42029     // private
42030     // Provides logic to override the default TriggerField.validateBlur which just returns true
42031     validateBlur : function(){
42032         return !this.menu || !this.menu.isVisible();
42033     },
42034
42035     /**
42036      * Returns the current date value of the date field.
42037      * @return {Date} The date value
42038      */
42039     getValue : function(){
42040         
42041         
42042         
42043         return  this.hiddenField ?
42044                 this.hiddenField.value :
42045                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42046     },
42047
42048     /**
42049      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42050      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42051      * (the default format used is "m/d/y").
42052      * <br />Usage:
42053      * <pre><code>
42054 //All of these calls set the same date value (May 4, 2006)
42055
42056 //Pass a date object:
42057 var dt = new Date('5/4/06');
42058 monthField.setValue(dt);
42059
42060 //Pass a date string (default format):
42061 monthField.setValue('5/4/06');
42062
42063 //Pass a date string (custom format):
42064 monthField.format = 'Y-m-d';
42065 monthField.setValue('2006-5-4');
42066 </code></pre>
42067      * @param {String/Date} date The date or valid date string
42068      */
42069     setValue : function(date){
42070         Roo.log('month setValue' + date);
42071         // can only be first of month..
42072         
42073         var val = this.parseDate(date);
42074         
42075         if (this.hiddenField) {
42076             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42077         }
42078         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42079         this.value = this.parseDate(date);
42080     },
42081
42082     // private
42083     parseDate : function(value){
42084         if(!value || value instanceof Date){
42085             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42086             return value;
42087         }
42088         var v = Date.parseDate(value, this.format);
42089         if (!v && this.useIso) {
42090             v = Date.parseDate(value, 'Y-m-d');
42091         }
42092         if (v) {
42093             // 
42094             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42095         }
42096         
42097         
42098         if(!v && this.altFormats){
42099             if(!this.altFormatsArray){
42100                 this.altFormatsArray = this.altFormats.split("|");
42101             }
42102             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42103                 v = Date.parseDate(value, this.altFormatsArray[i]);
42104             }
42105         }
42106         return v;
42107     },
42108
42109     // private
42110     formatDate : function(date, fmt){
42111         return (!date || !(date instanceof Date)) ?
42112                date : date.dateFormat(fmt || this.format);
42113     },
42114
42115     // private
42116     menuListeners : {
42117         select: function(m, d){
42118             this.setValue(d);
42119             this.fireEvent('select', this, d);
42120         },
42121         show : function(){ // retain focus styling
42122             this.onFocus();
42123         },
42124         hide : function(){
42125             this.focus.defer(10, this);
42126             var ml = this.menuListeners;
42127             this.menu.un("select", ml.select,  this);
42128             this.menu.un("show", ml.show,  this);
42129             this.menu.un("hide", ml.hide,  this);
42130         }
42131     },
42132     // private
42133     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42134     onTriggerClick : function(){
42135         if(this.disabled){
42136             return;
42137         }
42138         if(this.menu == null){
42139             this.menu = new Roo.menu.DateMenu();
42140            
42141         }
42142         
42143         Roo.apply(this.menu.picker,  {
42144             
42145             showClear: this.allowBlank,
42146             minDate : this.minValue,
42147             maxDate : this.maxValue,
42148             disabledDatesRE : this.ddMatch,
42149             disabledDatesText : this.disabledDatesText,
42150             
42151             format : this.useIso ? 'Y-m-d' : this.format,
42152             minText : String.format(this.minText, this.formatDate(this.minValue)),
42153             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42154             
42155         });
42156          this.menu.on(Roo.apply({}, this.menuListeners, {
42157             scope:this
42158         }));
42159        
42160         
42161         var m = this.menu;
42162         var p = m.picker;
42163         
42164         // hide month picker get's called when we called by 'before hide';
42165         
42166         var ignorehide = true;
42167         p.hideMonthPicker  = function(disableAnim){
42168             if (ignorehide) {
42169                 return;
42170             }
42171              if(this.monthPicker){
42172                 Roo.log("hideMonthPicker called");
42173                 if(disableAnim === true){
42174                     this.monthPicker.hide();
42175                 }else{
42176                     this.monthPicker.slideOut('t', {duration:.2});
42177                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42178                     p.fireEvent("select", this, this.value);
42179                     m.hide();
42180                 }
42181             }
42182         }
42183         
42184         Roo.log('picker set value');
42185         Roo.log(this.getValue());
42186         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42187         m.show(this.el, 'tl-bl?');
42188         ignorehide  = false;
42189         // this will trigger hideMonthPicker..
42190         
42191         
42192         // hidden the day picker
42193         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42194         
42195         
42196         
42197       
42198         
42199         p.showMonthPicker.defer(100, p);
42200     
42201         
42202        
42203     },
42204
42205     beforeBlur : function(){
42206         var v = this.parseDate(this.getRawValue());
42207         if(v){
42208             this.setValue(v);
42209         }
42210     }
42211
42212     /** @cfg {Boolean} grow @hide */
42213     /** @cfg {Number} growMin @hide */
42214     /** @cfg {Number} growMax @hide */
42215     /**
42216      * @hide
42217      * @method autoSize
42218      */
42219 });/*
42220  * Based on:
42221  * Ext JS Library 1.1.1
42222  * Copyright(c) 2006-2007, Ext JS, LLC.
42223  *
42224  * Originally Released Under LGPL - original licence link has changed is not relivant.
42225  *
42226  * Fork - LGPL
42227  * <script type="text/javascript">
42228  */
42229  
42230
42231 /**
42232  * @class Roo.form.ComboBox
42233  * @extends Roo.form.TriggerField
42234  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42235  * @constructor
42236  * Create a new ComboBox.
42237  * @param {Object} config Configuration options
42238  */
42239 Roo.form.ComboBox = function(config){
42240     Roo.form.ComboBox.superclass.constructor.call(this, config);
42241     this.addEvents({
42242         /**
42243          * @event expand
42244          * Fires when the dropdown list is expanded
42245              * @param {Roo.form.ComboBox} combo This combo box
42246              */
42247         'expand' : true,
42248         /**
42249          * @event collapse
42250          * Fires when the dropdown list is collapsed
42251              * @param {Roo.form.ComboBox} combo This combo box
42252              */
42253         'collapse' : true,
42254         /**
42255          * @event beforeselect
42256          * Fires before a list item is selected. Return false to cancel the selection.
42257              * @param {Roo.form.ComboBox} combo This combo box
42258              * @param {Roo.data.Record} record The data record returned from the underlying store
42259              * @param {Number} index The index of the selected item in the dropdown list
42260              */
42261         'beforeselect' : true,
42262         /**
42263          * @event select
42264          * Fires when a list item is selected
42265              * @param {Roo.form.ComboBox} combo This combo box
42266              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42267              * @param {Number} index The index of the selected item in the dropdown list
42268              */
42269         'select' : true,
42270         /**
42271          * @event beforequery
42272          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42273          * The event object passed has these properties:
42274              * @param {Roo.form.ComboBox} combo This combo box
42275              * @param {String} query The query
42276              * @param {Boolean} forceAll true to force "all" query
42277              * @param {Boolean} cancel true to cancel the query
42278              * @param {Object} e The query event object
42279              */
42280         'beforequery': true,
42281          /**
42282          * @event add
42283          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42284              * @param {Roo.form.ComboBox} combo This combo box
42285              */
42286         'add' : true,
42287         /**
42288          * @event edit
42289          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42290              * @param {Roo.form.ComboBox} combo This combo box
42291              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42292              */
42293         'edit' : true
42294         
42295         
42296     });
42297     if(this.transform){
42298         this.allowDomMove = false;
42299         var s = Roo.getDom(this.transform);
42300         if(!this.hiddenName){
42301             this.hiddenName = s.name;
42302         }
42303         if(!this.store){
42304             this.mode = 'local';
42305             var d = [], opts = s.options;
42306             for(var i = 0, len = opts.length;i < len; i++){
42307                 var o = opts[i];
42308                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42309                 if(o.selected) {
42310                     this.value = value;
42311                 }
42312                 d.push([value, o.text]);
42313             }
42314             this.store = new Roo.data.SimpleStore({
42315                 'id': 0,
42316                 fields: ['value', 'text'],
42317                 data : d
42318             });
42319             this.valueField = 'value';
42320             this.displayField = 'text';
42321         }
42322         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42323         if(!this.lazyRender){
42324             this.target = true;
42325             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42326             s.parentNode.removeChild(s); // remove it
42327             this.render(this.el.parentNode);
42328         }else{
42329             s.parentNode.removeChild(s); // remove it
42330         }
42331
42332     }
42333     if (this.store) {
42334         this.store = Roo.factory(this.store, Roo.data);
42335     }
42336     
42337     this.selectedIndex = -1;
42338     if(this.mode == 'local'){
42339         if(config.queryDelay === undefined){
42340             this.queryDelay = 10;
42341         }
42342         if(config.minChars === undefined){
42343             this.minChars = 0;
42344         }
42345     }
42346 };
42347
42348 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42349     /**
42350      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42351      */
42352     /**
42353      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42354      * rendering into an Roo.Editor, defaults to false)
42355      */
42356     /**
42357      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42358      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42359      */
42360     /**
42361      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42362      */
42363     /**
42364      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42365      * the dropdown list (defaults to undefined, with no header element)
42366      */
42367
42368      /**
42369      * @cfg {String/Roo.Template} tpl The template to use to render the output
42370      */
42371      
42372     // private
42373     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42374     /**
42375      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42376      */
42377     listWidth: undefined,
42378     /**
42379      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42380      * mode = 'remote' or 'text' if mode = 'local')
42381      */
42382     displayField: undefined,
42383     /**
42384      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42385      * mode = 'remote' or 'value' if mode = 'local'). 
42386      * Note: use of a valueField requires the user make a selection
42387      * in order for a value to be mapped.
42388      */
42389     valueField: undefined,
42390     
42391     
42392     /**
42393      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42394      * field's data value (defaults to the underlying DOM element's name)
42395      */
42396     hiddenName: undefined,
42397     /**
42398      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42399      */
42400     listClass: '',
42401     /**
42402      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42403      */
42404     selectedClass: 'x-combo-selected',
42405     /**
42406      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42407      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42408      * which displays a downward arrow icon).
42409      */
42410     triggerClass : 'x-form-arrow-trigger',
42411     /**
42412      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42413      */
42414     shadow:'sides',
42415     /**
42416      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42417      * anchor positions (defaults to 'tl-bl')
42418      */
42419     listAlign: 'tl-bl?',
42420     /**
42421      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42422      */
42423     maxHeight: 300,
42424     /**
42425      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42426      * query specified by the allQuery config option (defaults to 'query')
42427      */
42428     triggerAction: 'query',
42429     /**
42430      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42431      * (defaults to 4, does not apply if editable = false)
42432      */
42433     minChars : 4,
42434     /**
42435      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42436      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42437      */
42438     typeAhead: false,
42439     /**
42440      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42441      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42442      */
42443     queryDelay: 500,
42444     /**
42445      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42446      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42447      */
42448     pageSize: 0,
42449     /**
42450      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42451      * when editable = true (defaults to false)
42452      */
42453     selectOnFocus:false,
42454     /**
42455      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42456      */
42457     queryParam: 'query',
42458     /**
42459      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42460      * when mode = 'remote' (defaults to 'Loading...')
42461      */
42462     loadingText: 'Loading...',
42463     /**
42464      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42465      */
42466     resizable: false,
42467     /**
42468      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42469      */
42470     handleHeight : 8,
42471     /**
42472      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42473      * traditional select (defaults to true)
42474      */
42475     editable: true,
42476     /**
42477      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42478      */
42479     allQuery: '',
42480     /**
42481      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42482      */
42483     mode: 'remote',
42484     /**
42485      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42486      * listWidth has a higher value)
42487      */
42488     minListWidth : 70,
42489     /**
42490      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42491      * allow the user to set arbitrary text into the field (defaults to false)
42492      */
42493     forceSelection:false,
42494     /**
42495      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42496      * if typeAhead = true (defaults to 250)
42497      */
42498     typeAheadDelay : 250,
42499     /**
42500      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42501      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42502      */
42503     valueNotFoundText : undefined,
42504     /**
42505      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42506      */
42507     blockFocus : false,
42508     
42509     /**
42510      * @cfg {Boolean} disableClear Disable showing of clear button.
42511      */
42512     disableClear : false,
42513     /**
42514      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42515      */
42516     alwaysQuery : false,
42517     
42518     //private
42519     addicon : false,
42520     editicon: false,
42521     
42522     // element that contains real text value.. (when hidden is used..)
42523      
42524     // private
42525     onRender : function(ct, position)
42526     {
42527         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42528         
42529         if(this.hiddenName){
42530             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42531                     'before', true);
42532             this.hiddenField.value =
42533                 this.hiddenValue !== undefined ? this.hiddenValue :
42534                 this.value !== undefined ? this.value : '';
42535
42536             // prevent input submission
42537             this.el.dom.removeAttribute('name');
42538              
42539              
42540         }
42541         
42542         if(Roo.isGecko){
42543             this.el.dom.setAttribute('autocomplete', 'off');
42544         }
42545
42546         var cls = 'x-combo-list';
42547
42548         this.list = new Roo.Layer({
42549             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42550         });
42551
42552         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42553         this.list.setWidth(lw);
42554         this.list.swallowEvent('mousewheel');
42555         this.assetHeight = 0;
42556
42557         if(this.title){
42558             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42559             this.assetHeight += this.header.getHeight();
42560         }
42561
42562         this.innerList = this.list.createChild({cls:cls+'-inner'});
42563         this.innerList.on('mouseover', this.onViewOver, this);
42564         this.innerList.on('mousemove', this.onViewMove, this);
42565         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42566         
42567         if(this.allowBlank && !this.pageSize && !this.disableClear){
42568             this.footer = this.list.createChild({cls:cls+'-ft'});
42569             this.pageTb = new Roo.Toolbar(this.footer);
42570            
42571         }
42572         if(this.pageSize){
42573             this.footer = this.list.createChild({cls:cls+'-ft'});
42574             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42575                     {pageSize: this.pageSize});
42576             
42577         }
42578         
42579         if (this.pageTb && this.allowBlank && !this.disableClear) {
42580             var _this = this;
42581             this.pageTb.add(new Roo.Toolbar.Fill(), {
42582                 cls: 'x-btn-icon x-btn-clear',
42583                 text: '&#160;',
42584                 handler: function()
42585                 {
42586                     _this.collapse();
42587                     _this.clearValue();
42588                     _this.onSelect(false, -1);
42589                 }
42590             });
42591         }
42592         if (this.footer) {
42593             this.assetHeight += this.footer.getHeight();
42594         }
42595         
42596
42597         if(!this.tpl){
42598             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42599         }
42600
42601         this.view = new Roo.View(this.innerList, this.tpl, {
42602             singleSelect:true,
42603             store: this.store,
42604             selectedClass: this.selectedClass
42605         });
42606
42607         this.view.on('click', this.onViewClick, this);
42608
42609         this.store.on('beforeload', this.onBeforeLoad, this);
42610         this.store.on('load', this.onLoad, this);
42611         this.store.on('loadexception', this.onLoadException, this);
42612
42613         if(this.resizable){
42614             this.resizer = new Roo.Resizable(this.list,  {
42615                pinned:true, handles:'se'
42616             });
42617             this.resizer.on('resize', function(r, w, h){
42618                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42619                 this.listWidth = w;
42620                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42621                 this.restrictHeight();
42622             }, this);
42623             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42624         }
42625         if(!this.editable){
42626             this.editable = true;
42627             this.setEditable(false);
42628         }  
42629         
42630         
42631         if (typeof(this.events.add.listeners) != 'undefined') {
42632             
42633             this.addicon = this.wrap.createChild(
42634                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42635        
42636             this.addicon.on('click', function(e) {
42637                 this.fireEvent('add', this);
42638             }, this);
42639         }
42640         if (typeof(this.events.edit.listeners) != 'undefined') {
42641             
42642             this.editicon = this.wrap.createChild(
42643                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42644             if (this.addicon) {
42645                 this.editicon.setStyle('margin-left', '40px');
42646             }
42647             this.editicon.on('click', function(e) {
42648                 
42649                 // we fire even  if inothing is selected..
42650                 this.fireEvent('edit', this, this.lastData );
42651                 
42652             }, this);
42653         }
42654         
42655         
42656         
42657     },
42658
42659     // private
42660     initEvents : function(){
42661         Roo.form.ComboBox.superclass.initEvents.call(this);
42662
42663         this.keyNav = new Roo.KeyNav(this.el, {
42664             "up" : function(e){
42665                 this.inKeyMode = true;
42666                 this.selectPrev();
42667             },
42668
42669             "down" : function(e){
42670                 if(!this.isExpanded()){
42671                     this.onTriggerClick();
42672                 }else{
42673                     this.inKeyMode = true;
42674                     this.selectNext();
42675                 }
42676             },
42677
42678             "enter" : function(e){
42679                 this.onViewClick();
42680                 //return true;
42681             },
42682
42683             "esc" : function(e){
42684                 this.collapse();
42685             },
42686
42687             "tab" : function(e){
42688                 this.onViewClick(false);
42689                 this.fireEvent("specialkey", this, e);
42690                 return true;
42691             },
42692
42693             scope : this,
42694
42695             doRelay : function(foo, bar, hname){
42696                 if(hname == 'down' || this.scope.isExpanded()){
42697                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42698                 }
42699                 return true;
42700             },
42701
42702             forceKeyDown: true
42703         });
42704         this.queryDelay = Math.max(this.queryDelay || 10,
42705                 this.mode == 'local' ? 10 : 250);
42706         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42707         if(this.typeAhead){
42708             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42709         }
42710         if(this.editable !== false){
42711             this.el.on("keyup", this.onKeyUp, this);
42712         }
42713         if(this.forceSelection){
42714             this.on('blur', this.doForce, this);
42715         }
42716     },
42717
42718     onDestroy : function(){
42719         if(this.view){
42720             this.view.setStore(null);
42721             this.view.el.removeAllListeners();
42722             this.view.el.remove();
42723             this.view.purgeListeners();
42724         }
42725         if(this.list){
42726             this.list.destroy();
42727         }
42728         if(this.store){
42729             this.store.un('beforeload', this.onBeforeLoad, this);
42730             this.store.un('load', this.onLoad, this);
42731             this.store.un('loadexception', this.onLoadException, this);
42732         }
42733         Roo.form.ComboBox.superclass.onDestroy.call(this);
42734     },
42735
42736     // private
42737     fireKey : function(e){
42738         if(e.isNavKeyPress() && !this.list.isVisible()){
42739             this.fireEvent("specialkey", this, e);
42740         }
42741     },
42742
42743     // private
42744     onResize: function(w, h){
42745         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42746         
42747         if(typeof w != 'number'){
42748             // we do not handle it!?!?
42749             return;
42750         }
42751         var tw = this.trigger.getWidth();
42752         tw += this.addicon ? this.addicon.getWidth() : 0;
42753         tw += this.editicon ? this.editicon.getWidth() : 0;
42754         var x = w - tw;
42755         this.el.setWidth( this.adjustWidth('input', x));
42756             
42757         this.trigger.setStyle('left', x+'px');
42758         
42759         if(this.list && this.listWidth === undefined){
42760             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42761             this.list.setWidth(lw);
42762             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42763         }
42764         
42765     
42766         
42767     },
42768
42769     /**
42770      * Allow or prevent the user from directly editing the field text.  If false is passed,
42771      * the user will only be able to select from the items defined in the dropdown list.  This method
42772      * is the runtime equivalent of setting the 'editable' config option at config time.
42773      * @param {Boolean} value True to allow the user to directly edit the field text
42774      */
42775     setEditable : function(value){
42776         if(value == this.editable){
42777             return;
42778         }
42779         this.editable = value;
42780         if(!value){
42781             this.el.dom.setAttribute('readOnly', true);
42782             this.el.on('mousedown', this.onTriggerClick,  this);
42783             this.el.addClass('x-combo-noedit');
42784         }else{
42785             this.el.dom.setAttribute('readOnly', false);
42786             this.el.un('mousedown', this.onTriggerClick,  this);
42787             this.el.removeClass('x-combo-noedit');
42788         }
42789     },
42790
42791     // private
42792     onBeforeLoad : function(){
42793         if(!this.hasFocus){
42794             return;
42795         }
42796         this.innerList.update(this.loadingText ?
42797                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42798         this.restrictHeight();
42799         this.selectedIndex = -1;
42800     },
42801
42802     // private
42803     onLoad : function(){
42804         if(!this.hasFocus){
42805             return;
42806         }
42807         if(this.store.getCount() > 0){
42808             this.expand();
42809             this.restrictHeight();
42810             if(this.lastQuery == this.allQuery){
42811                 if(this.editable){
42812                     this.el.dom.select();
42813                 }
42814                 if(!this.selectByValue(this.value, true)){
42815                     this.select(0, true);
42816                 }
42817             }else{
42818                 this.selectNext();
42819                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42820                     this.taTask.delay(this.typeAheadDelay);
42821                 }
42822             }
42823         }else{
42824             this.onEmptyResults();
42825         }
42826         //this.el.focus();
42827     },
42828     // private
42829     onLoadException : function()
42830     {
42831         this.collapse();
42832         Roo.log(this.store.reader.jsonData);
42833         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42834             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42835         }
42836         
42837         
42838     },
42839     // private
42840     onTypeAhead : function(){
42841         if(this.store.getCount() > 0){
42842             var r = this.store.getAt(0);
42843             var newValue = r.data[this.displayField];
42844             var len = newValue.length;
42845             var selStart = this.getRawValue().length;
42846             if(selStart != len){
42847                 this.setRawValue(newValue);
42848                 this.selectText(selStart, newValue.length);
42849             }
42850         }
42851     },
42852
42853     // private
42854     onSelect : function(record, index){
42855         if(this.fireEvent('beforeselect', this, record, index) !== false){
42856             this.setFromData(index > -1 ? record.data : false);
42857             this.collapse();
42858             this.fireEvent('select', this, record, index);
42859         }
42860     },
42861
42862     /**
42863      * Returns the currently selected field value or empty string if no value is set.
42864      * @return {String} value The selected value
42865      */
42866     getValue : function(){
42867         if(this.valueField){
42868             return typeof this.value != 'undefined' ? this.value : '';
42869         }
42870         return Roo.form.ComboBox.superclass.getValue.call(this);
42871     },
42872
42873     /**
42874      * Clears any text/value currently set in the field
42875      */
42876     clearValue : function(){
42877         if(this.hiddenField){
42878             this.hiddenField.value = '';
42879         }
42880         this.value = '';
42881         this.setRawValue('');
42882         this.lastSelectionText = '';
42883         
42884     },
42885
42886     /**
42887      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42888      * will be displayed in the field.  If the value does not match the data value of an existing item,
42889      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42890      * Otherwise the field will be blank (although the value will still be set).
42891      * @param {String} value The value to match
42892      */
42893     setValue : function(v){
42894         var text = v;
42895         if(this.valueField){
42896             var r = this.findRecord(this.valueField, v);
42897             if(r){
42898                 text = r.data[this.displayField];
42899             }else if(this.valueNotFoundText !== undefined){
42900                 text = this.valueNotFoundText;
42901             }
42902         }
42903         this.lastSelectionText = text;
42904         if(this.hiddenField){
42905             this.hiddenField.value = v;
42906         }
42907         Roo.form.ComboBox.superclass.setValue.call(this, text);
42908         this.value = v;
42909     },
42910     /**
42911      * @property {Object} the last set data for the element
42912      */
42913     
42914     lastData : false,
42915     /**
42916      * Sets the value of the field based on a object which is related to the record format for the store.
42917      * @param {Object} value the value to set as. or false on reset?
42918      */
42919     setFromData : function(o){
42920         var dv = ''; // display value
42921         var vv = ''; // value value..
42922         this.lastData = o;
42923         if (this.displayField) {
42924             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42925         } else {
42926             // this is an error condition!!!
42927             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42928         }
42929         
42930         if(this.valueField){
42931             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42932         }
42933         if(this.hiddenField){
42934             this.hiddenField.value = vv;
42935             
42936             this.lastSelectionText = dv;
42937             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42938             this.value = vv;
42939             return;
42940         }
42941         // no hidden field.. - we store the value in 'value', but still display
42942         // display field!!!!
42943         this.lastSelectionText = dv;
42944         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42945         this.value = vv;
42946         
42947         
42948     },
42949     // private
42950     reset : function(){
42951         // overridden so that last data is reset..
42952         this.setValue(this.resetValue);
42953         this.originalValue = this.getValue();
42954         this.clearInvalid();
42955         this.lastData = false;
42956         if (this.view) {
42957             this.view.clearSelections();
42958         }
42959     },
42960     // private
42961     findRecord : function(prop, value){
42962         var record;
42963         if(this.store.getCount() > 0){
42964             this.store.each(function(r){
42965                 if(r.data[prop] == value){
42966                     record = r;
42967                     return false;
42968                 }
42969                 return true;
42970             });
42971         }
42972         return record;
42973     },
42974     
42975     getName: function()
42976     {
42977         // returns hidden if it's set..
42978         if (!this.rendered) {return ''};
42979         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42980         
42981     },
42982     // private
42983     onViewMove : function(e, t){
42984         this.inKeyMode = false;
42985     },
42986
42987     // private
42988     onViewOver : function(e, t){
42989         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42990             return;
42991         }
42992         var item = this.view.findItemFromChild(t);
42993         if(item){
42994             var index = this.view.indexOf(item);
42995             this.select(index, false);
42996         }
42997     },
42998
42999     // private
43000     onViewClick : function(doFocus)
43001     {
43002         var index = this.view.getSelectedIndexes()[0];
43003         var r = this.store.getAt(index);
43004         if(r){
43005             this.onSelect(r, index);
43006         }
43007         if(doFocus !== false && !this.blockFocus){
43008             this.el.focus();
43009         }
43010     },
43011
43012     // private
43013     restrictHeight : function(){
43014         this.innerList.dom.style.height = '';
43015         var inner = this.innerList.dom;
43016         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43017         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43018         this.list.beginUpdate();
43019         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43020         this.list.alignTo(this.el, this.listAlign);
43021         this.list.endUpdate();
43022     },
43023
43024     // private
43025     onEmptyResults : function(){
43026         this.collapse();
43027     },
43028
43029     /**
43030      * Returns true if the dropdown list is expanded, else false.
43031      */
43032     isExpanded : function(){
43033         return this.list.isVisible();
43034     },
43035
43036     /**
43037      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43038      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43039      * @param {String} value The data value of the item to select
43040      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43041      * selected item if it is not currently in view (defaults to true)
43042      * @return {Boolean} True if the value matched an item in the list, else false
43043      */
43044     selectByValue : function(v, scrollIntoView){
43045         if(v !== undefined && v !== null){
43046             var r = this.findRecord(this.valueField || this.displayField, v);
43047             if(r){
43048                 this.select(this.store.indexOf(r), scrollIntoView);
43049                 return true;
43050             }
43051         }
43052         return false;
43053     },
43054
43055     /**
43056      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43057      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43058      * @param {Number} index The zero-based index of the list item to select
43059      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43060      * selected item if it is not currently in view (defaults to true)
43061      */
43062     select : function(index, scrollIntoView){
43063         this.selectedIndex = index;
43064         this.view.select(index);
43065         if(scrollIntoView !== false){
43066             var el = this.view.getNode(index);
43067             if(el){
43068                 this.innerList.scrollChildIntoView(el, false);
43069             }
43070         }
43071     },
43072
43073     // private
43074     selectNext : function(){
43075         var ct = this.store.getCount();
43076         if(ct > 0){
43077             if(this.selectedIndex == -1){
43078                 this.select(0);
43079             }else if(this.selectedIndex < ct-1){
43080                 this.select(this.selectedIndex+1);
43081             }
43082         }
43083     },
43084
43085     // private
43086     selectPrev : function(){
43087         var ct = this.store.getCount();
43088         if(ct > 0){
43089             if(this.selectedIndex == -1){
43090                 this.select(0);
43091             }else if(this.selectedIndex != 0){
43092                 this.select(this.selectedIndex-1);
43093             }
43094         }
43095     },
43096
43097     // private
43098     onKeyUp : function(e){
43099         if(this.editable !== false && !e.isSpecialKey()){
43100             this.lastKey = e.getKey();
43101             this.dqTask.delay(this.queryDelay);
43102         }
43103     },
43104
43105     // private
43106     validateBlur : function(){
43107         return !this.list || !this.list.isVisible();   
43108     },
43109
43110     // private
43111     initQuery : function(){
43112         this.doQuery(this.getRawValue());
43113     },
43114
43115     // private
43116     doForce : function(){
43117         if(this.el.dom.value.length > 0){
43118             this.el.dom.value =
43119                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43120              
43121         }
43122     },
43123
43124     /**
43125      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43126      * query allowing the query action to be canceled if needed.
43127      * @param {String} query The SQL query to execute
43128      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43129      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43130      * saved in the current store (defaults to false)
43131      */
43132     doQuery : function(q, forceAll){
43133         if(q === undefined || q === null){
43134             q = '';
43135         }
43136         var qe = {
43137             query: q,
43138             forceAll: forceAll,
43139             combo: this,
43140             cancel:false
43141         };
43142         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43143             return false;
43144         }
43145         q = qe.query;
43146         forceAll = qe.forceAll;
43147         if(forceAll === true || (q.length >= this.minChars)){
43148             if(this.lastQuery != q || this.alwaysQuery){
43149                 this.lastQuery = q;
43150                 if(this.mode == 'local'){
43151                     this.selectedIndex = -1;
43152                     if(forceAll){
43153                         this.store.clearFilter();
43154                     }else{
43155                         this.store.filter(this.displayField, q);
43156                     }
43157                     this.onLoad();
43158                 }else{
43159                     this.store.baseParams[this.queryParam] = q;
43160                     this.store.load({
43161                         params: this.getParams(q)
43162                     });
43163                     this.expand();
43164                 }
43165             }else{
43166                 this.selectedIndex = -1;
43167                 this.onLoad();   
43168             }
43169         }
43170     },
43171
43172     // private
43173     getParams : function(q){
43174         var p = {};
43175         //p[this.queryParam] = q;
43176         if(this.pageSize){
43177             p.start = 0;
43178             p.limit = this.pageSize;
43179         }
43180         return p;
43181     },
43182
43183     /**
43184      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43185      */
43186     collapse : function(){
43187         if(!this.isExpanded()){
43188             return;
43189         }
43190         this.list.hide();
43191         Roo.get(document).un('mousedown', this.collapseIf, this);
43192         Roo.get(document).un('mousewheel', this.collapseIf, this);
43193         if (!this.editable) {
43194             Roo.get(document).un('keydown', this.listKeyPress, this);
43195         }
43196         this.fireEvent('collapse', this);
43197     },
43198
43199     // private
43200     collapseIf : function(e){
43201         if(!e.within(this.wrap) && !e.within(this.list)){
43202             this.collapse();
43203         }
43204     },
43205
43206     /**
43207      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43208      */
43209     expand : function(){
43210         if(this.isExpanded() || !this.hasFocus){
43211             return;
43212         }
43213         this.list.alignTo(this.el, this.listAlign);
43214         this.list.show();
43215         Roo.get(document).on('mousedown', this.collapseIf, this);
43216         Roo.get(document).on('mousewheel', this.collapseIf, this);
43217         if (!this.editable) {
43218             Roo.get(document).on('keydown', this.listKeyPress, this);
43219         }
43220         
43221         this.fireEvent('expand', this);
43222     },
43223
43224     // private
43225     // Implements the default empty TriggerField.onTriggerClick function
43226     onTriggerClick : function(){
43227         if(this.disabled){
43228             return;
43229         }
43230         if(this.isExpanded()){
43231             this.collapse();
43232             if (!this.blockFocus) {
43233                 this.el.focus();
43234             }
43235             
43236         }else {
43237             this.hasFocus = true;
43238             if(this.triggerAction == 'all') {
43239                 this.doQuery(this.allQuery, true);
43240             } else {
43241                 this.doQuery(this.getRawValue());
43242             }
43243             if (!this.blockFocus) {
43244                 this.el.focus();
43245             }
43246         }
43247     },
43248     listKeyPress : function(e)
43249     {
43250         //Roo.log('listkeypress');
43251         // scroll to first matching element based on key pres..
43252         if (e.isSpecialKey()) {
43253             return false;
43254         }
43255         var k = String.fromCharCode(e.getKey()).toUpperCase();
43256         //Roo.log(k);
43257         var match  = false;
43258         var csel = this.view.getSelectedNodes();
43259         var cselitem = false;
43260         if (csel.length) {
43261             var ix = this.view.indexOf(csel[0]);
43262             cselitem  = this.store.getAt(ix);
43263             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43264                 cselitem = false;
43265             }
43266             
43267         }
43268         
43269         this.store.each(function(v) { 
43270             if (cselitem) {
43271                 // start at existing selection.
43272                 if (cselitem.id == v.id) {
43273                     cselitem = false;
43274                 }
43275                 return;
43276             }
43277                 
43278             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43279                 match = this.store.indexOf(v);
43280                 return false;
43281             }
43282         }, this);
43283         
43284         if (match === false) {
43285             return true; // no more action?
43286         }
43287         // scroll to?
43288         this.view.select(match);
43289         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43290         sn.scrollIntoView(sn.dom.parentNode, false);
43291     } 
43292
43293     /** 
43294     * @cfg {Boolean} grow 
43295     * @hide 
43296     */
43297     /** 
43298     * @cfg {Number} growMin 
43299     * @hide 
43300     */
43301     /** 
43302     * @cfg {Number} growMax 
43303     * @hide 
43304     */
43305     /**
43306      * @hide
43307      * @method autoSize
43308      */
43309 });/*
43310  * Copyright(c) 2010-2012, Roo J Solutions Limited
43311  *
43312  * Licence LGPL
43313  *
43314  */
43315
43316 /**
43317  * @class Roo.form.ComboBoxArray
43318  * @extends Roo.form.TextField
43319  * A facebook style adder... for lists of email / people / countries  etc...
43320  * pick multiple items from a combo box, and shows each one.
43321  *
43322  *  Fred [x]  Brian [x]  [Pick another |v]
43323  *
43324  *
43325  *  For this to work: it needs various extra information
43326  *    - normal combo problay has
43327  *      name, hiddenName
43328  *    + displayField, valueField
43329  *
43330  *    For our purpose...
43331  *
43332  *
43333  *   If we change from 'extends' to wrapping...
43334  *   
43335  *  
43336  *
43337  
43338  
43339  * @constructor
43340  * Create a new ComboBoxArray.
43341  * @param {Object} config Configuration options
43342  */
43343  
43344
43345 Roo.form.ComboBoxArray = function(config)
43346 {
43347     this.addEvents({
43348         /**
43349          * @event beforeremove
43350          * Fires before remove the value from the list
43351              * @param {Roo.form.ComboBoxArray} _self This combo box array
43352              * @param {Roo.form.ComboBoxArray.Item} item removed item
43353              */
43354         'beforeremove' : true,
43355         /**
43356          * @event remove
43357          * Fires when remove the value from the list
43358              * @param {Roo.form.ComboBoxArray} _self This combo box array
43359              * @param {Roo.form.ComboBoxArray.Item} item removed item
43360              */
43361         'remove' : true
43362         
43363         
43364     });
43365     
43366     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43367     
43368     this.items = new Roo.util.MixedCollection(false);
43369     
43370     // construct the child combo...
43371     
43372     
43373     
43374     
43375    
43376     
43377 }
43378
43379  
43380 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43381
43382     /**
43383      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43384      */
43385     
43386     lastData : false,
43387     
43388     // behavies liek a hiddne field
43389     inputType:      'hidden',
43390     /**
43391      * @cfg {Number} width The width of the box that displays the selected element
43392      */ 
43393     width:          300,
43394
43395     
43396     
43397     /**
43398      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43399      */
43400     name : false,
43401     /**
43402      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43403      */
43404     hiddenName : false,
43405       /**
43406      * @cfg {String} seperator    The value seperator normally ',' 
43407      */
43408     seperator : ',',
43409     
43410     // private the array of items that are displayed..
43411     items  : false,
43412     // private - the hidden field el.
43413     hiddenEl : false,
43414     // private - the filed el..
43415     el : false,
43416     
43417     //validateValue : function() { return true; }, // all values are ok!
43418     //onAddClick: function() { },
43419     
43420     onRender : function(ct, position) 
43421     {
43422         
43423         // create the standard hidden element
43424         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43425         
43426         
43427         // give fake names to child combo;
43428         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43429         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43430         
43431         this.combo = Roo.factory(this.combo, Roo.form);
43432         this.combo.onRender(ct, position);
43433         if (typeof(this.combo.width) != 'undefined') {
43434             this.combo.onResize(this.combo.width,0);
43435         }
43436         
43437         this.combo.initEvents();
43438         
43439         // assigned so form know we need to do this..
43440         this.store          = this.combo.store;
43441         this.valueField     = this.combo.valueField;
43442         this.displayField   = this.combo.displayField ;
43443         
43444         
43445         this.combo.wrap.addClass('x-cbarray-grp');
43446         
43447         var cbwrap = this.combo.wrap.createChild(
43448             {tag: 'div', cls: 'x-cbarray-cb'},
43449             this.combo.el.dom
43450         );
43451         
43452              
43453         this.hiddenEl = this.combo.wrap.createChild({
43454             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43455         });
43456         this.el = this.combo.wrap.createChild({
43457             tag: 'input',  type:'hidden' , name: this.name, value : ''
43458         });
43459          //   this.el.dom.removeAttribute("name");
43460         
43461         
43462         this.outerWrap = this.combo.wrap;
43463         this.wrap = cbwrap;
43464         
43465         this.outerWrap.setWidth(this.width);
43466         this.outerWrap.dom.removeChild(this.el.dom);
43467         
43468         this.wrap.dom.appendChild(this.el.dom);
43469         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43470         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43471         
43472         this.combo.trigger.setStyle('position','relative');
43473         this.combo.trigger.setStyle('left', '0px');
43474         this.combo.trigger.setStyle('top', '2px');
43475         
43476         this.combo.el.setStyle('vertical-align', 'text-bottom');
43477         
43478         //this.trigger.setStyle('vertical-align', 'top');
43479         
43480         // this should use the code from combo really... on('add' ....)
43481         if (this.adder) {
43482             
43483         
43484             this.adder = this.outerWrap.createChild(
43485                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43486             var _t = this;
43487             this.adder.on('click', function(e) {
43488                 _t.fireEvent('adderclick', this, e);
43489             }, _t);
43490         }
43491         //var _t = this;
43492         //this.adder.on('click', this.onAddClick, _t);
43493         
43494         
43495         this.combo.on('select', function(cb, rec, ix) {
43496             this.addItem(rec.data);
43497             
43498             cb.setValue('');
43499             cb.el.dom.value = '';
43500             //cb.lastData = rec.data;
43501             // add to list
43502             
43503         }, this);
43504         
43505         
43506     },
43507     
43508     
43509     getName: function()
43510     {
43511         // returns hidden if it's set..
43512         if (!this.rendered) {return ''};
43513         return  this.hiddenName ? this.hiddenName : this.name;
43514         
43515     },
43516     
43517     
43518     onResize: function(w, h){
43519         
43520         return;
43521         // not sure if this is needed..
43522         //this.combo.onResize(w,h);
43523         
43524         if(typeof w != 'number'){
43525             // we do not handle it!?!?
43526             return;
43527         }
43528         var tw = this.combo.trigger.getWidth();
43529         tw += this.addicon ? this.addicon.getWidth() : 0;
43530         tw += this.editicon ? this.editicon.getWidth() : 0;
43531         var x = w - tw;
43532         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43533             
43534         this.combo.trigger.setStyle('left', '0px');
43535         
43536         if(this.list && this.listWidth === undefined){
43537             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43538             this.list.setWidth(lw);
43539             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43540         }
43541         
43542     
43543         
43544     },
43545     
43546     addItem: function(rec)
43547     {
43548         var valueField = this.combo.valueField;
43549         var displayField = this.combo.displayField;
43550         
43551         if (this.items.indexOfKey(rec[valueField]) > -1) {
43552             //console.log("GOT " + rec.data.id);
43553             return;
43554         }
43555         
43556         var x = new Roo.form.ComboBoxArray.Item({
43557             //id : rec[this.idField],
43558             data : rec,
43559             displayField : displayField ,
43560             tipField : displayField ,
43561             cb : this
43562         });
43563         // use the 
43564         this.items.add(rec[valueField],x);
43565         // add it before the element..
43566         this.updateHiddenEl();
43567         x.render(this.outerWrap, this.wrap.dom);
43568         // add the image handler..
43569     },
43570     
43571     updateHiddenEl : function()
43572     {
43573         this.validate();
43574         if (!this.hiddenEl) {
43575             return;
43576         }
43577         var ar = [];
43578         var idField = this.combo.valueField;
43579         
43580         this.items.each(function(f) {
43581             ar.push(f.data[idField]);
43582         });
43583         this.hiddenEl.dom.value = ar.join(this.seperator);
43584         this.validate();
43585     },
43586     
43587     reset : function()
43588     {
43589         this.items.clear();
43590         
43591         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43592            el.remove();
43593         });
43594         
43595         this.el.dom.value = '';
43596         if (this.hiddenEl) {
43597             this.hiddenEl.dom.value = '';
43598         }
43599         
43600     },
43601     getValue: function()
43602     {
43603         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43604     },
43605     setValue: function(v) // not a valid action - must use addItems..
43606     {
43607         
43608         this.reset();
43609          
43610         if (this.store.isLocal && (typeof(v) == 'string')) {
43611             // then we can use the store to find the values..
43612             // comma seperated at present.. this needs to allow JSON based encoding..
43613             this.hiddenEl.value  = v;
43614             var v_ar = [];
43615             Roo.each(v.split(this.seperator), function(k) {
43616                 Roo.log("CHECK " + this.valueField + ',' + k);
43617                 var li = this.store.query(this.valueField, k);
43618                 if (!li.length) {
43619                     return;
43620                 }
43621                 var add = {};
43622                 add[this.valueField] = k;
43623                 add[this.displayField] = li.item(0).data[this.displayField];
43624                 
43625                 this.addItem(add);
43626             }, this) 
43627              
43628         }
43629         if (typeof(v) == 'object' ) {
43630             // then let's assume it's an array of objects..
43631             Roo.each(v, function(l) {
43632                 var add = l;
43633                 if (typeof(l) == 'string') {
43634                     add = {};
43635                     add[this.valueField] = l;
43636                     add[this.displayField] = l
43637                 }
43638                 this.addItem(add);
43639             }, this);
43640              
43641         }
43642         
43643         
43644     },
43645     setFromData: function(v)
43646     {
43647         // this recieves an object, if setValues is called.
43648         this.reset();
43649         this.el.dom.value = v[this.displayField];
43650         this.hiddenEl.dom.value = v[this.valueField];
43651         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43652             return;
43653         }
43654         var kv = v[this.valueField];
43655         var dv = v[this.displayField];
43656         kv = typeof(kv) != 'string' ? '' : kv;
43657         dv = typeof(dv) != 'string' ? '' : dv;
43658         
43659         
43660         var keys = kv.split(this.seperator);
43661         var display = dv.split(this.seperator);
43662         for (var i = 0 ; i < keys.length; i++) {
43663             add = {};
43664             add[this.valueField] = keys[i];
43665             add[this.displayField] = display[i];
43666             this.addItem(add);
43667         }
43668       
43669         
43670     },
43671     
43672     /**
43673      * Validates the combox array value
43674      * @return {Boolean} True if the value is valid, else false
43675      */
43676     validate : function(){
43677         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43678             this.clearInvalid();
43679             return true;
43680         }
43681         return false;
43682     },
43683     
43684     validateValue : function(value){
43685         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43686         
43687     },
43688     
43689     /*@
43690      * overide
43691      * 
43692      */
43693     isDirty : function() {
43694         if(this.disabled) {
43695             return false;
43696         }
43697         
43698         try {
43699             var d = Roo.decode(String(this.originalValue));
43700         } catch (e) {
43701             return String(this.getValue()) !== String(this.originalValue);
43702         }
43703         
43704         var originalValue = [];
43705         
43706         for (var i = 0; i < d.length; i++){
43707             originalValue.push(d[i][this.valueField]);
43708         }
43709         
43710         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43711         
43712     }
43713     
43714 });
43715
43716
43717
43718 /**
43719  * @class Roo.form.ComboBoxArray.Item
43720  * @extends Roo.BoxComponent
43721  * A selected item in the list
43722  *  Fred [x]  Brian [x]  [Pick another |v]
43723  * 
43724  * @constructor
43725  * Create a new item.
43726  * @param {Object} config Configuration options
43727  */
43728  
43729 Roo.form.ComboBoxArray.Item = function(config) {
43730     config.id = Roo.id();
43731     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43732 }
43733
43734 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43735     data : {},
43736     cb: false,
43737     displayField : false,
43738     tipField : false,
43739     
43740     
43741     defaultAutoCreate : {
43742         tag: 'div',
43743         cls: 'x-cbarray-item',
43744         cn : [ 
43745             { tag: 'div' },
43746             {
43747                 tag: 'img',
43748                 width:16,
43749                 height : 16,
43750                 src : Roo.BLANK_IMAGE_URL ,
43751                 align: 'center'
43752             }
43753         ]
43754         
43755     },
43756     
43757  
43758     onRender : function(ct, position)
43759     {
43760         Roo.form.Field.superclass.onRender.call(this, ct, position);
43761         
43762         if(!this.el){
43763             var cfg = this.getAutoCreate();
43764             this.el = ct.createChild(cfg, position);
43765         }
43766         
43767         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43768         
43769         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43770             this.cb.renderer(this.data) :
43771             String.format('{0}',this.data[this.displayField]);
43772         
43773             
43774         this.el.child('div').dom.setAttribute('qtip',
43775                         String.format('{0}',this.data[this.tipField])
43776         );
43777         
43778         this.el.child('img').on('click', this.remove, this);
43779         
43780     },
43781    
43782     remove : function()
43783     {
43784         if(this.cb.disabled){
43785             return;
43786         }
43787         
43788         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43789             this.cb.items.remove(this);
43790             this.el.child('img').un('click', this.remove, this);
43791             this.el.remove();
43792             this.cb.updateHiddenEl();
43793
43794             this.cb.fireEvent('remove', this.cb, this);
43795         }
43796         
43797     }
43798 });/*
43799  * RooJS Library 1.1.1
43800  * Copyright(c) 2008-2011  Alan Knowles
43801  *
43802  * License - LGPL
43803  */
43804  
43805
43806 /**
43807  * @class Roo.form.ComboNested
43808  * @extends Roo.form.ComboBox
43809  * A combobox for that allows selection of nested items in a list,
43810  * eg.
43811  *
43812  *  Book
43813  *    -> red
43814  *    -> green
43815  *  Table
43816  *    -> square
43817  *      ->red
43818  *      ->green
43819  *    -> rectangle
43820  *      ->green
43821  *      
43822  * 
43823  * @constructor
43824  * Create a new ComboNested
43825  * @param {Object} config Configuration options
43826  */
43827 Roo.form.ComboNested = function(config){
43828     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43829     // should verify some data...
43830     // like
43831     // hiddenName = required..
43832     // displayField = required
43833     // valudField == required
43834     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43835     var _t = this;
43836     Roo.each(req, function(e) {
43837         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43838             throw "Roo.form.ComboNested : missing value for: " + e;
43839         }
43840     });
43841      
43842     
43843 };
43844
43845 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43846    
43847     /*
43848      * @config {Number} max Number of columns to show
43849      */
43850     
43851     maxColumns : 3,
43852    
43853     list : null, // the outermost div..
43854     innerLists : null, // the
43855     views : null,
43856     stores : null,
43857     // private
43858     loadingChildren : false,
43859     
43860     onRender : function(ct, position)
43861     {
43862         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43863         
43864         if(this.hiddenName){
43865             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43866                     'before', true);
43867             this.hiddenField.value =
43868                 this.hiddenValue !== undefined ? this.hiddenValue :
43869                 this.value !== undefined ? this.value : '';
43870
43871             // prevent input submission
43872             this.el.dom.removeAttribute('name');
43873              
43874              
43875         }
43876         
43877         if(Roo.isGecko){
43878             this.el.dom.setAttribute('autocomplete', 'off');
43879         }
43880
43881         var cls = 'x-combo-list';
43882
43883         this.list = new Roo.Layer({
43884             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43885         });
43886
43887         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43888         this.list.setWidth(lw);
43889         this.list.swallowEvent('mousewheel');
43890         this.assetHeight = 0;
43891
43892         if(this.title){
43893             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43894             this.assetHeight += this.header.getHeight();
43895         }
43896         this.innerLists = [];
43897         this.views = [];
43898         this.stores = [];
43899         for (var i =0 ; i < this.maxColumns; i++) {
43900             this.onRenderList( cls, i);
43901         }
43902         
43903         // always needs footer, as we are going to have an 'OK' button.
43904         this.footer = this.list.createChild({cls:cls+'-ft'});
43905         this.pageTb = new Roo.Toolbar(this.footer);  
43906         var _this = this;
43907         this.pageTb.add(  {
43908             
43909             text: 'Done',
43910             handler: function()
43911             {
43912                 _this.collapse();
43913             }
43914         });
43915         
43916         if ( this.allowBlank && !this.disableClear) {
43917             
43918             this.pageTb.add(new Roo.Toolbar.Fill(), {
43919                 cls: 'x-btn-icon x-btn-clear',
43920                 text: '&#160;',
43921                 handler: function()
43922                 {
43923                     _this.collapse();
43924                     _this.clearValue();
43925                     _this.onSelect(false, -1);
43926                 }
43927             });
43928         }
43929         if (this.footer) {
43930             this.assetHeight += this.footer.getHeight();
43931         }
43932         
43933     },
43934     onRenderList : function (  cls, i)
43935     {
43936         
43937         var lw = Math.floor(
43938                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43939         );
43940         
43941         this.list.setWidth(lw); // default to '1'
43942
43943         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43944         //il.on('mouseover', this.onViewOver, this, { list:  i });
43945         //il.on('mousemove', this.onViewMove, this, { list:  i });
43946         il.setWidth(lw);
43947         il.setStyle({ 'overflow-x' : 'hidden'});
43948
43949         if(!this.tpl){
43950             this.tpl = new Roo.Template({
43951                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43952                 isEmpty: function (value, allValues) {
43953                     //Roo.log(value);
43954                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43955                     return dl ? 'has-children' : 'no-children'
43956                 }
43957             });
43958         }
43959         
43960         var store  = this.store;
43961         if (i > 0) {
43962             store  = new Roo.data.SimpleStore({
43963                 //fields : this.store.reader.meta.fields,
43964                 reader : this.store.reader,
43965                 data : [ ]
43966             });
43967         }
43968         this.stores[i]  = store;
43969                   
43970         var view = this.views[i] = new Roo.View(
43971             il,
43972             this.tpl,
43973             {
43974                 singleSelect:true,
43975                 store: store,
43976                 selectedClass: this.selectedClass
43977             }
43978         );
43979         view.getEl().setWidth(lw);
43980         view.getEl().setStyle({
43981             position: i < 1 ? 'relative' : 'absolute',
43982             top: 0,
43983             left: (i * lw ) + 'px',
43984             display : i > 0 ? 'none' : 'block'
43985         });
43986         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43987         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43988         //view.on('click', this.onViewClick, this, { list : i });
43989
43990         store.on('beforeload', this.onBeforeLoad, this);
43991         store.on('load',  this.onLoad, this, { list  : i});
43992         store.on('loadexception', this.onLoadException, this);
43993
43994         // hide the other vies..
43995         
43996         
43997         
43998     },
43999       
44000     restrictHeight : function()
44001     {
44002         var mh = 0;
44003         Roo.each(this.innerLists, function(il,i) {
44004             var el = this.views[i].getEl();
44005             el.dom.style.height = '';
44006             var inner = el.dom;
44007             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44008             // only adjust heights on other ones..
44009             mh = Math.max(h, mh);
44010             if (i < 1) {
44011                 
44012                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44013                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44014                
44015             }
44016             
44017             
44018         }, this);
44019         
44020         this.list.beginUpdate();
44021         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44022         this.list.alignTo(this.el, this.listAlign);
44023         this.list.endUpdate();
44024         
44025     },
44026      
44027     
44028     // -- store handlers..
44029     // private
44030     onBeforeLoad : function()
44031     {
44032         if(!this.hasFocus){
44033             return;
44034         }
44035         this.innerLists[0].update(this.loadingText ?
44036                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44037         this.restrictHeight();
44038         this.selectedIndex = -1;
44039     },
44040     // private
44041     onLoad : function(a,b,c,d)
44042     {
44043         if (!this.loadingChildren) {
44044             // then we are loading the top level. - hide the children
44045             for (var i = 1;i < this.views.length; i++) {
44046                 this.views[i].getEl().setStyle({ display : 'none' });
44047             }
44048             var lw = Math.floor(
44049                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44050             );
44051         
44052              this.list.setWidth(lw); // default to '1'
44053
44054             
44055         }
44056         if(!this.hasFocus){
44057             return;
44058         }
44059         
44060         if(this.store.getCount() > 0) {
44061             this.expand();
44062             this.restrictHeight();   
44063         } else {
44064             this.onEmptyResults();
44065         }
44066         
44067         if (!this.loadingChildren) {
44068             this.selectActive();
44069         }
44070         /*
44071         this.stores[1].loadData([]);
44072         this.stores[2].loadData([]);
44073         this.views
44074         */    
44075     
44076         //this.el.focus();
44077     },
44078     
44079     
44080     // private
44081     onLoadException : function()
44082     {
44083         this.collapse();
44084         Roo.log(this.store.reader.jsonData);
44085         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44086             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44087         }
44088         
44089         
44090     },
44091     // no cleaning of leading spaces on blur here.
44092     cleanLeadingSpace : function(e) { },
44093     
44094
44095     onSelectChange : function (view, sels, opts )
44096     {
44097         var ix = view.getSelectedIndexes();
44098          
44099         if (opts.list > this.maxColumns - 2) {
44100             if (view.store.getCount()<  1) {
44101                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44102
44103             } else  {
44104                 if (ix.length) {
44105                     // used to clear ?? but if we are loading unselected 
44106                     this.setFromData(view.store.getAt(ix[0]).data);
44107                 }
44108                 
44109             }
44110             
44111             return;
44112         }
44113         
44114         if (!ix.length) {
44115             // this get's fired when trigger opens..
44116            // this.setFromData({});
44117             var str = this.stores[opts.list+1];
44118             str.data.clear(); // removeall wihtout the fire events..
44119             return;
44120         }
44121         
44122         var rec = view.store.getAt(ix[0]);
44123          
44124         this.setFromData(rec.data);
44125         this.fireEvent('select', this, rec, ix[0]);
44126         
44127         var lw = Math.floor(
44128              (
44129                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44130              ) / this.maxColumns
44131         );
44132         this.loadingChildren = true;
44133         this.stores[opts.list+1].loadDataFromChildren( rec );
44134         this.loadingChildren = false;
44135         var dl = this.stores[opts.list+1]. getTotalCount();
44136         
44137         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44138         
44139         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44140         for (var i = opts.list+2; i < this.views.length;i++) {
44141             this.views[i].getEl().setStyle({ display : 'none' });
44142         }
44143         
44144         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44145         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44146         
44147         if (this.isLoading) {
44148            // this.selectActive(opts.list);
44149         }
44150          
44151     },
44152     
44153     
44154     
44155     
44156     onDoubleClick : function()
44157     {
44158         this.collapse(); //??
44159     },
44160     
44161      
44162     
44163     
44164     
44165     // private
44166     recordToStack : function(store, prop, value, stack)
44167     {
44168         var cstore = new Roo.data.SimpleStore({
44169             //fields : this.store.reader.meta.fields, // we need array reader.. for
44170             reader : this.store.reader,
44171             data : [ ]
44172         });
44173         var _this = this;
44174         var record  = false;
44175         var srec = false;
44176         if(store.getCount() < 1){
44177             return false;
44178         }
44179         store.each(function(r){
44180             if(r.data[prop] == value){
44181                 record = r;
44182             srec = r;
44183                 return false;
44184             }
44185             if (r.data.cn && r.data.cn.length) {
44186                 cstore.loadDataFromChildren( r);
44187                 var cret = _this.recordToStack(cstore, prop, value, stack);
44188                 if (cret !== false) {
44189                     record = cret;
44190                     srec = r;
44191                     return false;
44192                 }
44193             }
44194              
44195             return true;
44196         });
44197         if (record == false) {
44198             return false
44199         }
44200         stack.unshift(srec);
44201         return record;
44202     },
44203     
44204     /*
44205      * find the stack of stores that match our value.
44206      *
44207      * 
44208      */
44209     
44210     selectActive : function ()
44211     {
44212         // if store is not loaded, then we will need to wait for that to happen first.
44213         var stack = [];
44214         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44215         for (var i = 0; i < stack.length; i++ ) {
44216             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44217         }
44218         
44219     }
44220         
44221          
44222     
44223     
44224     
44225     
44226 });/*
44227  * Based on:
44228  * Ext JS Library 1.1.1
44229  * Copyright(c) 2006-2007, Ext JS, LLC.
44230  *
44231  * Originally Released Under LGPL - original licence link has changed is not relivant.
44232  *
44233  * Fork - LGPL
44234  * <script type="text/javascript">
44235  */
44236 /**
44237  * @class Roo.form.Checkbox
44238  * @extends Roo.form.Field
44239  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44240  * @constructor
44241  * Creates a new Checkbox
44242  * @param {Object} config Configuration options
44243  */
44244 Roo.form.Checkbox = function(config){
44245     Roo.form.Checkbox.superclass.constructor.call(this, config);
44246     this.addEvents({
44247         /**
44248          * @event check
44249          * Fires when the checkbox is checked or unchecked.
44250              * @param {Roo.form.Checkbox} this This checkbox
44251              * @param {Boolean} checked The new checked value
44252              */
44253         check : true
44254     });
44255 };
44256
44257 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44258     /**
44259      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44260      */
44261     focusClass : undefined,
44262     /**
44263      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44264      */
44265     fieldClass: "x-form-field",
44266     /**
44267      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44268      */
44269     checked: false,
44270     /**
44271      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44272      * {tag: "input", type: "checkbox", autocomplete: "off"})
44273      */
44274     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44275     /**
44276      * @cfg {String} boxLabel The text that appears beside the checkbox
44277      */
44278     boxLabel : "",
44279     /**
44280      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44281      */  
44282     inputValue : '1',
44283     /**
44284      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44285      */
44286      valueOff: '0', // value when not checked..
44287
44288     actionMode : 'viewEl', 
44289     //
44290     // private
44291     itemCls : 'x-menu-check-item x-form-item',
44292     groupClass : 'x-menu-group-item',
44293     inputType : 'hidden',
44294     
44295     
44296     inSetChecked: false, // check that we are not calling self...
44297     
44298     inputElement: false, // real input element?
44299     basedOn: false, // ????
44300     
44301     isFormField: true, // not sure where this is needed!!!!
44302
44303     onResize : function(){
44304         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44305         if(!this.boxLabel){
44306             this.el.alignTo(this.wrap, 'c-c');
44307         }
44308     },
44309
44310     initEvents : function(){
44311         Roo.form.Checkbox.superclass.initEvents.call(this);
44312         this.el.on("click", this.onClick,  this);
44313         this.el.on("change", this.onClick,  this);
44314     },
44315
44316
44317     getResizeEl : function(){
44318         return this.wrap;
44319     },
44320
44321     getPositionEl : function(){
44322         return this.wrap;
44323     },
44324
44325     // private
44326     onRender : function(ct, position){
44327         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44328         /*
44329         if(this.inputValue !== undefined){
44330             this.el.dom.value = this.inputValue;
44331         }
44332         */
44333         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44334         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44335         var viewEl = this.wrap.createChild({ 
44336             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44337         this.viewEl = viewEl;   
44338         this.wrap.on('click', this.onClick,  this); 
44339         
44340         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44341         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44342         
44343         
44344         
44345         if(this.boxLabel){
44346             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44347         //    viewEl.on('click', this.onClick,  this); 
44348         }
44349         //if(this.checked){
44350             this.setChecked(this.checked);
44351         //}else{
44352             //this.checked = this.el.dom;
44353         //}
44354
44355     },
44356
44357     // private
44358     initValue : Roo.emptyFn,
44359
44360     /**
44361      * Returns the checked state of the checkbox.
44362      * @return {Boolean} True if checked, else false
44363      */
44364     getValue : function(){
44365         if(this.el){
44366             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44367         }
44368         return this.valueOff;
44369         
44370     },
44371
44372         // private
44373     onClick : function(){ 
44374         if (this.disabled) {
44375             return;
44376         }
44377         this.setChecked(!this.checked);
44378
44379         //if(this.el.dom.checked != this.checked){
44380         //    this.setValue(this.el.dom.checked);
44381        // }
44382     },
44383
44384     /**
44385      * Sets the checked state of the checkbox.
44386      * On is always based on a string comparison between inputValue and the param.
44387      * @param {Boolean/String} value - the value to set 
44388      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44389      */
44390     setValue : function(v,suppressEvent){
44391         
44392         
44393         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44394         //if(this.el && this.el.dom){
44395         //    this.el.dom.checked = this.checked;
44396         //    this.el.dom.defaultChecked = this.checked;
44397         //}
44398         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44399         //this.fireEvent("check", this, this.checked);
44400     },
44401     // private..
44402     setChecked : function(state,suppressEvent)
44403     {
44404         if (this.inSetChecked) {
44405             this.checked = state;
44406             return;
44407         }
44408         
44409     
44410         if(this.wrap){
44411             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44412         }
44413         this.checked = state;
44414         if(suppressEvent !== true){
44415             this.fireEvent('check', this, state);
44416         }
44417         this.inSetChecked = true;
44418         this.el.dom.value = state ? this.inputValue : this.valueOff;
44419         this.inSetChecked = false;
44420         
44421     },
44422     // handle setting of hidden value by some other method!!?!?
44423     setFromHidden: function()
44424     {
44425         if(!this.el){
44426             return;
44427         }
44428         //console.log("SET FROM HIDDEN");
44429         //alert('setFrom hidden');
44430         this.setValue(this.el.dom.value);
44431     },
44432     
44433     onDestroy : function()
44434     {
44435         if(this.viewEl){
44436             Roo.get(this.viewEl).remove();
44437         }
44438          
44439         Roo.form.Checkbox.superclass.onDestroy.call(this);
44440     },
44441     
44442     setBoxLabel : function(str)
44443     {
44444         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44445     }
44446
44447 });/*
44448  * Based on:
44449  * Ext JS Library 1.1.1
44450  * Copyright(c) 2006-2007, Ext JS, LLC.
44451  *
44452  * Originally Released Under LGPL - original licence link has changed is not relivant.
44453  *
44454  * Fork - LGPL
44455  * <script type="text/javascript">
44456  */
44457  
44458 /**
44459  * @class Roo.form.Radio
44460  * @extends Roo.form.Checkbox
44461  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44462  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44463  * @constructor
44464  * Creates a new Radio
44465  * @param {Object} config Configuration options
44466  */
44467 Roo.form.Radio = function(){
44468     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44469 };
44470 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44471     inputType: 'radio',
44472
44473     /**
44474      * If this radio is part of a group, it will return the selected value
44475      * @return {String}
44476      */
44477     getGroupValue : function(){
44478         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44479     },
44480     
44481     
44482     onRender : function(ct, position){
44483         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44484         
44485         if(this.inputValue !== undefined){
44486             this.el.dom.value = this.inputValue;
44487         }
44488          
44489         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44490         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44491         //var viewEl = this.wrap.createChild({ 
44492         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44493         //this.viewEl = viewEl;   
44494         //this.wrap.on('click', this.onClick,  this); 
44495         
44496         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44497         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44498         
44499         
44500         
44501         if(this.boxLabel){
44502             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44503         //    viewEl.on('click', this.onClick,  this); 
44504         }
44505          if(this.checked){
44506             this.el.dom.checked =   'checked' ;
44507         }
44508          
44509     } 
44510     
44511     
44512 });Roo.htmleditor = {}; 
44513 /**
44514  * @class Roo.htmleditor.Filter
44515  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
44516  * @cfg {DomElement} node The node to iterate and filter
44517  * @cfg {boolean|String|Array} tag Tags to replace 
44518  * @constructor
44519  * Create a new Filter.
44520  * @param {Object} config Configuration options
44521  */
44522
44523
44524
44525 Roo.htmleditor.Filter = function(cfg) {
44526     Roo.apply(this.cfg);
44527     // this does not actually call walk as it's really just a abstract class
44528 }
44529
44530
44531 Roo.htmleditor.Filter.prototype = {
44532     
44533     node: false,
44534     
44535     tag: false,
44536
44537     // overrride to do replace comments.
44538     replaceComment : false,
44539     
44540     // overrride to do replace or do stuff with tags..
44541     replaceTag : false,
44542     
44543     walk : function(dom)
44544     {
44545         Roo.each( Array.from(dom.childNodes), function( e ) {
44546             switch(true) {
44547                 
44548                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
44549                     this.replaceComment(e);
44550                     return;
44551                 
44552                 case e.nodeType != 1: //not a node.
44553                     return;
44554                 
44555                 case this.tag === true: // everything
44556                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
44557                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
44558                     if (this.replaceTag && false === this.replaceTag(e)) {
44559                         return;
44560                     }
44561                     if (e.hasChildNodes()) {
44562                         this.walk(e);
44563                     }
44564                     return;
44565                 
44566                 default:    // tags .. that do not match.
44567                     if (e.hasChildNodes()) {
44568                         this.walk(e);
44569                     }
44570             }
44571             
44572         }, this);
44573         
44574     }
44575 }; 
44576
44577 /**
44578  * @class Roo.htmleditor.FilterAttributes
44579  * clean attributes and  styles including http:// etc.. in attribute
44580  * @constructor
44581 * Run a new Attribute Filter
44582 * @param {Object} config Configuration options
44583  */
44584 Roo.htmleditor.FilterAttributes = function(cfg)
44585 {
44586     Roo.apply(this, cfg);
44587     this.attrib_black = this.attrib_black || [];
44588     this.attrib_clean = this.attrib_clean || [];
44589     this.style_white = this.style_white || [];
44590     this.style_black = this.style_black || [];
44591     this.walk(cfg.node);
44592 }
44593
44594 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
44595 {
44596     tag: true, // all tags
44597     
44598     attrib_black : false, // array
44599     attrib_clean : false,
44600     style_white : false,
44601     style_black : false,
44602      
44603      
44604     replaceTag : function(node)
44605     {
44606         if (!node.attributes || !node.attributes.length) {
44607             return true;
44608         }
44609         
44610         for (var i = node.attributes.length-1; i > -1 ; i--) {
44611             var a = node.attributes[i];
44612             //console.log(a);
44613             
44614             if (a.name.toLowerCase().substr(0,2)=='on')  {
44615                 node.removeAttribute(a.name);
44616                 continue;
44617             }
44618             
44619             
44620             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
44621                 node.removeAttribute(a.name);
44622                 continue;
44623             }
44624             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
44625                 this.cleanAttr(node,a.name,a.value); // fixme..
44626                 continue;
44627             }
44628             if (a.name == 'style') {
44629                 this.cleanStyle(node,a.name,a.value);
44630                 continue;
44631             }
44632             /// clean up MS crap..
44633             // tecnically this should be a list of valid class'es..
44634             
44635             
44636             if (a.name == 'class') {
44637                 if (a.value.match(/^Mso/)) {
44638                     node.removeAttribute('class');
44639                 }
44640                 
44641                 if (a.value.match(/^body$/)) {
44642                     node.removeAttribute('class');
44643                 }
44644                 continue;
44645             }
44646             
44647             
44648             // style cleanup!?
44649             // class cleanup?
44650             
44651         }
44652         return true; // clean children
44653     },
44654         
44655     cleanAttr: function(node, n,v)
44656     {
44657         
44658         if (v.match(/^\./) || v.match(/^\//)) {
44659             return;
44660         }
44661         if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44662             return;
44663         }
44664         if (v.match(/^#/)) {
44665             return;
44666         }
44667         if (v.match(/^\{/)) { // allow template editing.
44668             return;
44669         }
44670 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44671         node.removeAttribute(n);
44672         
44673     },
44674     cleanStyle : function(node,  n,v)
44675     {
44676         if (v.match(/expression/)) { //XSS?? should we even bother..
44677             node.removeAttribute(n);
44678             return;
44679         }
44680         
44681         var parts = v.split(/;/);
44682         var clean = [];
44683         
44684         Roo.each(parts, function(p) {
44685             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44686             if (!p.length) {
44687                 return true;
44688             }
44689             var l = p.split(':').shift().replace(/\s+/g,'');
44690             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44691             
44692             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
44693                 return true;
44694             }
44695             //Roo.log()
44696             // only allow 'c whitelisted system attributes'
44697             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
44698                 return true;
44699             }
44700             
44701             
44702             clean.push(p);
44703             return true;
44704         },this);
44705         if (clean.length) { 
44706             node.setAttribute(n, clean.join(';'));
44707         } else {
44708             node.removeAttribute(n);
44709         }
44710         
44711     }
44712         
44713         
44714         
44715     
44716 });/**
44717  * @class Roo.htmleditor.FilterBlack
44718  * remove blacklisted elements.
44719  * @constructor
44720  * Run a new Blacklisted Filter
44721  * @param {Object} config Configuration options
44722  */
44723
44724 Roo.htmleditor.FilterBlack = function(cfg)
44725 {
44726     Roo.apply(this, cfg);
44727     this.walk(cfg.node);
44728 }
44729
44730 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
44731 {
44732     tag : true, // all elements.
44733    
44734     replace : function(n)
44735     {
44736         n.parentNode.removeChild(n);
44737     }
44738 });
44739 /**
44740  * @class Roo.htmleditor.FilterComment
44741  * remove comments.
44742  * @constructor
44743 * Run a new Comments Filter
44744 * @param {Object} config Configuration options
44745  */
44746 Roo.htmleditor.FilterComment = function(cfg)
44747 {
44748     this.walk(cfg.node);
44749 }
44750
44751 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
44752 {
44753   
44754     replaceComment : function(n)
44755     {
44756         n.parentNode.removeChild(n);
44757     }
44758 });/**
44759  * @class Roo.htmleditor.FilterKeepChildren
44760  * remove tags but keep children
44761  * @constructor
44762  * Run a new Keep Children Filter
44763  * @param {Object} config Configuration options
44764  */
44765
44766 Roo.htmleditor.FilterKeepChildren = function(cfg)
44767 {
44768     Roo.apply(this, cfg);
44769     if (this.tag === false) {
44770         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
44771     }
44772     this.walk(cfg.node);
44773 }
44774
44775 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
44776 {
44777     
44778   
44779     replaceTag : function(node)
44780     {
44781         // walk children...
44782         var ar = Array.from(node.childNodes);
44783         for (var i = 0; i < ar.length; i++) {
44784             node.removeChild(ar[i]);
44785             // what if we need to walk these???
44786             node.parentNode.insertBefore(ar[i], node);
44787             if (this.tag !== false) {
44788                 this.walk(ar[i]);
44789             }
44790         }
44791         node.parentNode.removeChild(node);
44792         return false; // don't walk children
44793         
44794         
44795     }
44796 });/**
44797  * @class Roo.htmleditor.FilterParagraph
44798  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
44799  * like on 'push' to remove the <p> tags and replace them with line breaks.
44800  * @constructor
44801  * Run a new Paragraph Filter
44802  * @param {Object} config Configuration options
44803  */
44804
44805 Roo.htmleditor.FilterParagraph = function(cfg)
44806 {
44807     // no need to apply config.
44808     this.walk(cfg.node);
44809 }
44810
44811 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
44812 {
44813     
44814      
44815     tag : 'P',
44816     
44817      
44818     replaceTag : function(node)
44819     {
44820         
44821         if (node.childNodes.length == 1 &&
44822             node.childNodes[0].nodeType == 3 &&
44823             node.childNodes[0].textContent.trim().length < 1
44824             ) {
44825             // remove and replace with '<BR>';
44826             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
44827             return false; // no need to walk..
44828         }
44829         var ar = Array.from(node.childNodes);
44830         for (var i = 0; i < ar.length; i++) {
44831             node.removeChild(ar[i]);
44832             // what if we need to walk these???
44833             node.parentNode.insertBefore(ar[i], node);
44834         }
44835         // now what about this?
44836         // <p> &nbsp; </p>
44837         
44838         // double BR.
44839         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
44840         node.parentNode.removeChild(node);
44841         
44842         return false;
44843
44844     }
44845     
44846 });/**
44847  * @class Roo.htmleditor.FilterSpan
44848  * filter span's with no attributes out..
44849  * @constructor
44850  * Run a new Span Filter
44851  * @param {Object} config Configuration options
44852  */
44853
44854 Roo.htmleditor.FilterSpan = function(cfg)
44855 {
44856     // no need to apply config.
44857     this.walk(cfg.node);
44858 }
44859
44860 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
44861 {
44862      
44863     tag : 'SPAN',
44864      
44865  
44866     replaceTag : function(node)
44867     {
44868         if (node.attributes && node.attributes.length > 0) {
44869             return true; // walk if there are any.
44870         }
44871         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
44872         return false;
44873      
44874     }
44875     
44876 });/**
44877  * @class Roo.htmleditor.FilterTableWidth
44878   try and remove table width data - as that frequently messes up other stuff.
44879  * 
44880  *      was cleanTableWidths.
44881  *
44882  * Quite often pasting from word etc.. results in tables with column and widths.
44883  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44884  *
44885  * @constructor
44886  * Run a new Table Filter
44887  * @param {Object} config Configuration options
44888  */
44889
44890 Roo.htmleditor.FilterTableWidth = function(cfg)
44891 {
44892     // no need to apply config.
44893     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
44894     this.walk(cfg.node);
44895 }
44896
44897 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
44898 {
44899      
44900      
44901     
44902     replaceTag: function(node) {
44903         
44904         
44905       
44906         if (node.hasAttribute('width')) {
44907             node.removeAttribute('width');
44908         }
44909         
44910          
44911         if (node.hasAttribute("style")) {
44912             // pretty basic...
44913             
44914             var styles = node.getAttribute("style").split(";");
44915             var nstyle = [];
44916             Roo.each(styles, function(s) {
44917                 if (!s.match(/:/)) {
44918                     return;
44919                 }
44920                 var kv = s.split(":");
44921                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44922                     return;
44923                 }
44924                 // what ever is left... we allow.
44925                 nstyle.push(s);
44926             });
44927             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44928             if (!nstyle.length) {
44929                 node.removeAttribute('style');
44930             }
44931         }
44932         
44933         return true; // continue doing children..
44934     }
44935 });/**
44936  * @class Roo.htmleditor.FilterWord
44937  * try and clean up all the mess that Word generates.
44938  * 
44939  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
44940  
44941  * @constructor
44942  * Run a new Span Filter
44943  * @param {Object} config Configuration options
44944  */
44945
44946 Roo.htmleditor.FilterWord = function(cfg)
44947 {
44948     // no need to apply config.
44949     this.walk(cfg.node);
44950 }
44951
44952 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
44953 {
44954     tag: true,
44955      
44956     
44957     /**
44958      * Clean up MS wordisms...
44959      */
44960     replaceTag : function(node)
44961     {
44962          
44963         // no idea what this does - span with text, replaceds with just text.
44964         if(
44965                 node.nodeName == 'SPAN' &&
44966                 !node.hasAttributes() &&
44967                 node.childNodes.length == 1 &&
44968                 node.firstChild.nodeName == "#text"  
44969         ) {
44970             var textNode = node.firstChild;
44971             node.removeChild(textNode);
44972             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44973                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44974             }
44975             node.parentNode.insertBefore(textNode, node);
44976             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44977                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44978             }
44979             
44980             node.parentNode.removeChild(node);
44981             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
44982         }
44983         
44984    
44985         
44986         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44987             node.parentNode.removeChild(node);
44988             return false; // dont do chidlren
44989         }
44990         //Roo.log(node.tagName);
44991         // remove - but keep children..
44992         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44993             //Roo.log('-- removed');
44994             while (node.childNodes.length) {
44995                 var cn = node.childNodes[0];
44996                 node.removeChild(cn);
44997                 node.parentNode.insertBefore(cn, node);
44998                 // move node to parent - and clean it..
44999                 this.replaceTag(cn);
45000             }
45001             node.parentNode.removeChild(node);
45002             /// no need to iterate chidlren = it's got none..
45003             //this.iterateChildren(node, this.cleanWord);
45004             return false; // no need to iterate children.
45005         }
45006         // clean styles
45007         if (node.className.length) {
45008             
45009             var cn = node.className.split(/\W+/);
45010             var cna = [];
45011             Roo.each(cn, function(cls) {
45012                 if (cls.match(/Mso[a-zA-Z]+/)) {
45013                     return;
45014                 }
45015                 cna.push(cls);
45016             });
45017             node.className = cna.length ? cna.join(' ') : '';
45018             if (!cna.length) {
45019                 node.removeAttribute("class");
45020             }
45021         }
45022         
45023         if (node.hasAttribute("lang")) {
45024             node.removeAttribute("lang");
45025         }
45026         
45027         if (node.hasAttribute("style")) {
45028             
45029             var styles = node.getAttribute("style").split(";");
45030             var nstyle = [];
45031             Roo.each(styles, function(s) {
45032                 if (!s.match(/:/)) {
45033                     return;
45034                 }
45035                 var kv = s.split(":");
45036                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45037                     return;
45038                 }
45039                 // what ever is left... we allow.
45040                 nstyle.push(s);
45041             });
45042             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45043             if (!nstyle.length) {
45044                 node.removeAttribute('style');
45045             }
45046         }
45047         return true; // do children
45048         
45049         
45050         
45051     }
45052 });
45053 /**
45054  * @class Roo.htmleditor.Tidy
45055  * Tidy HTML 
45056  * @cfg {Roo.HtmlEditorCore} core the editor.
45057  * @constructor
45058  * Create a new Filter.
45059  * @param {Object} config Configuration options
45060  */
45061
45062
45063 Roo.htmleditor.Tidy = function(cfg) {
45064     Roo.apply(this, cfg);
45065     
45066     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
45067      
45068 }
45069
45070 Roo.htmleditor.Tidy.toString = function(node)
45071 {
45072     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
45073 }
45074
45075 Roo.htmleditor.Tidy.prototype = {
45076     
45077     
45078     wrap : function(s) {
45079         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
45080     },
45081
45082     
45083     tidy : function(node, indent) {
45084      
45085         if  (node.nodeType == 3) {
45086             // text.
45087             
45088             
45089             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
45090                 
45091             
45092         }
45093         
45094         if  (node.nodeType != 1) {
45095             return '';
45096         }
45097         
45098         
45099         
45100         if (node.tagName == 'BODY') {
45101             
45102             return this.cn(node, '');
45103         }
45104              
45105              // Prints the node tagName, such as <A>, <IMG>, etc
45106         var ret = "<" + node.tagName +  this.attr(node) ;
45107         
45108         // elements with no children..
45109         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
45110                 return ret + '/>';
45111         }
45112         ret += '>';
45113         
45114         
45115         var cindent = indent === false ? '' : (indent + '  ');
45116         // tags where we will not pad the children.. (inline text tags etc..)
45117         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
45118             cindent = false;
45119             
45120             
45121         }
45122         
45123         var cn = this.cn(node, cindent );
45124         
45125         return ret + cn  + '</' + node.tagName + '>';
45126         
45127     },
45128     cn: function(node, indent)
45129     {
45130         var ret = [];
45131         
45132         var ar = Array.from(node.childNodes);
45133         for (var i = 0 ; i < ar.length ; i++) {
45134             
45135             
45136             
45137             if (indent !== false   // indent==false preservies everything
45138                 && i > 0
45139                 && ar[i].nodeType == 3 
45140                 && ar[i].nodeValue.length > 0
45141                 && ar[i].nodeValue.match(/^\s+/)
45142             ) {
45143                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
45144                     ret.pop(); // remove line break from last?
45145                 }
45146                 
45147                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
45148             }
45149             if (indent !== false
45150                 && ar[i].nodeType == 1 // element - and indent is not set... 
45151             ) {
45152                 ret.push("\n" + indent); 
45153             }
45154             
45155             ret.push(this.tidy(ar[i], indent));
45156             // text + trailing indent 
45157             if (indent !== false
45158                 && ar[i].nodeType == 3
45159                 && ar[i].nodeValue.length > 0
45160                 && ar[i].nodeValue.match(/\s+$/)
45161             ){
45162                 ret.push("\n" + indent); 
45163             }
45164             
45165             
45166             
45167             
45168         }
45169         // what if all text?
45170         
45171         
45172         return ret.join('');
45173     },
45174     
45175          
45176         
45177     attr : function(node)
45178     {
45179         var attr = [];
45180         for(i = 0; i < node.attributes.length;i++) {
45181             
45182             // skip empty values?
45183             if (!node.attributes.item(i).value.length) {
45184                 continue;
45185             }
45186             attr.push(  node.attributes.item(i).name + '="' +
45187                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
45188             );
45189         }
45190         return attr.length ? (' ' + attr.join(' ') ) : '';
45191         
45192     }
45193     
45194     
45195     
45196 }
45197 /**
45198  * @class Roo.htmleditor.KeyEnter
45199  * Handle Enter press..
45200  * @cfg {Roo.HtmlEditorCore} core the editor.
45201  * @constructor
45202  * Create a new Filter.
45203  * @param {Object} config Configuration options
45204  */
45205
45206
45207
45208 Roo.htmleditor.KeyEnter = function(cfg) {
45209     Roo.apply(this, cfg);
45210     // this does not actually call walk as it's really just a abstract class
45211  
45212     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
45213 }
45214
45215
45216 Roo.htmleditor.KeyEnter.prototype = {
45217     
45218     core : false,
45219     
45220     keypress : function(e) {
45221         if (e.charCode != 13) {
45222             return true;
45223         }
45224         e.preventDefault();
45225         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
45226         var doc = this.core.doc;
45227         
45228         var docFragment = doc.createDocumentFragment();
45229     
45230         //add a new line
45231         var newEle = doc.createTextNode('\n');
45232         docFragment.appendChild(newEle);
45233     
45234         //add the br, or p, or something else
45235         newEle = doc.createElement('br');
45236         docFragment.appendChild(newEle);
45237     
45238         //make the br replace selection
45239         var range = this.core.win.getSelection().getRangeAt(0);
45240         range.deleteContents();
45241         range.insertNode(docFragment);
45242     
45243         //create a new range
45244         range = doc.createRange();
45245         range.setStartAfter(newEle);
45246         range.collapse(true);
45247     
45248         //make the cursor there
45249         var sel = this.core.win.getSelection();
45250         sel.removeAllRanges();
45251         sel.addRange(range);
45252     
45253         return false;
45254          
45255     }
45256 };
45257      
45258 /**
45259  * @class Roo.htmleditor.Block
45260  * Base class for html editor blocks - do not use it directly .. extend it..
45261  * @cfg {DomElement} node The node to apply stuff to.
45262  * @cfg {String} friendly_name the name that appears in the context bar about this block
45263  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
45264  
45265  * @constructor
45266  * Create a new Filter.
45267  * @param {Object} config Configuration options
45268  */
45269
45270 Roo.htmleditor.Block  = function(cfg)
45271 {
45272     // do nothing .. should not be called really.
45273 }
45274
45275 Roo.htmleditor.Block.factory = function(node)
45276 {
45277     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
45278     if (typeof(cls) == 'undefined') {
45279         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
45280         return false;
45281     }
45282     return new cls({ node: node });  /// should trigger update element
45283 }
45284
45285
45286 Roo.htmleditor.Block.prototype = {
45287     
45288      // used by context menu
45289     friendly_name : 'Image with caption',
45290     
45291     context : false,
45292     /**
45293      * Update a node with values from this object
45294      * @param {DomElement} node
45295      */
45296     updateElement : function(node)
45297     {
45298         Roo.DomHelper.update(node, this.toObject());
45299     },
45300      /**
45301      * convert to plain HTML for calling insertAtCursor..
45302      */
45303     toHTML : function()
45304     {
45305         return Roo.DomHelper.markup(this.toObject());
45306     },
45307     /**
45308      * used by readEleemnt to extract data from a node
45309      * may need improving as it's pretty basic
45310      
45311      * @param {DomElement} node
45312      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
45313      * @param {String} attribute (use html - for contents, or style for using next param as style)
45314      * @param {String} style the style property - eg. text-align
45315      */
45316     getVal : function(node, tag, attr, style)
45317     {
45318         var n = node;
45319         if (n.tagName != tag.toUpperCase()) {
45320             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
45321             // but kiss for now.
45322             n = node.getElementsByTagName(tag).item(0);
45323         }
45324         if (attr == 'html') {
45325             return n.innerHTML;
45326         }
45327         if (attr == 'style') {
45328             return Roo.get(n).getStyle(style);
45329         }
45330         
45331         return Roo.get(n).attr(attr);
45332             
45333     },
45334     /**
45335      * create a DomHelper friendly object - for use with 
45336      * Roo.DomHelper.markup / overwrite / etc..
45337      * (override this)
45338      */
45339     toObject : function()
45340     {
45341         return {};
45342     },
45343       /**
45344      * Read a node that has a 'data-block' property - and extract the values from it.
45345      * @param {DomElement} node - the node
45346      */
45347     readElement : function(node)
45348     {
45349         
45350     } 
45351     
45352     
45353 }
45354
45355  
45356
45357 /**
45358  * @class Roo.htmleditor.BlockFigure
45359  * Block that has an image and a figcaption
45360  * @cfg {String} image_src the url for the image
45361  * @cfg {String} align (left|right) alignment for the block default left
45362  * @cfg {String} text_align (left|right) alignment for the text caption default left.
45363  * @cfg {String} caption the text to appear below  (and in the alt tag)
45364  * @cfg {String|number} image_width the width of the image number or %?
45365  * @cfg {String|number} image_height the height of the image number or %?
45366  * 
45367  * @constructor
45368  * Create a new Filter.
45369  * @param {Object} config Configuration options
45370  */
45371
45372 Roo.htmleditor.BlockFigure = function(cfg)
45373 {
45374     if (cfg.node) {
45375         this.readElement(cfg.node);
45376         this.updateElement(cfg.node);
45377     }
45378     Roo.apply(this, cfg);
45379 }
45380 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
45381  
45382     
45383     // setable values.
45384     image_src: '',
45385     
45386     align: 'left',
45387     caption : '',
45388     text_align: 'left',
45389     
45390     width : '45%',
45391     margin: '2%',
45392     
45393     // used by context menu
45394     friendly_name : 'Image with caption',
45395     
45396     context : { // ?? static really
45397         width : {
45398             title: "Width",
45399             width: 40
45400             // ?? number
45401         },
45402         margin : {
45403             title: "Margin",
45404             width: 40
45405             // ?? number
45406         },
45407         align: {
45408             title: "Align",
45409             opts : [[ "left"],[ "right"]],
45410             width : 80
45411             
45412         },
45413         text_align: {
45414             title: "Caption Align",
45415             opts : [ [ "left"],[ "right"],[ "center"]],
45416             width : 80
45417         },
45418         
45419        
45420         image_src : {
45421             title: "Src",
45422             width: 220
45423         }
45424     },
45425     /**
45426      * create a DomHelper friendly object - for use with
45427      * Roo.DomHelper.markup / overwrite / etc..
45428      */
45429     toObject : function()
45430     {
45431         var d = document.createElement('div');
45432         d.innerHTML = this.caption;
45433         
45434         return {
45435             tag: 'figure',
45436             'data-block' : 'Figure',
45437             contenteditable : 'false',
45438             style : {
45439                 display: 'table',
45440                 float :  this.align ,
45441                 width :  this.width,
45442                 margin: this.margin
45443             },
45444             cn : [
45445                 {
45446                     tag : 'img',
45447                     src : this.image_src,
45448                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
45449                     style: {
45450                         width: '100%'
45451                     }
45452                 },
45453                 {
45454                     tag: 'figcaption',
45455                     contenteditable : true,
45456                     style : {
45457                         'text-align': this.text_align
45458                     },
45459                     html : this.caption
45460                     
45461                 }
45462             ]
45463         };
45464     },
45465     
45466     readElement : function(node)
45467     {
45468         this.image_src = this.getVal(node, 'img', 'src');
45469         this.align = this.getVal(node, 'figure', 'style', 'float');
45470         this.caption = this.getVal(node, 'figcaption', 'html');
45471         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
45472         this.width = this.getVal(node, 'figure', 'style', 'width');
45473         this.margin = this.getVal(node, 'figure', 'style', 'margin');
45474         
45475     } 
45476     
45477   
45478    
45479      
45480     
45481     
45482     
45483     
45484 })
45485
45486 //<script type="text/javascript">
45487
45488 /*
45489  * Based  Ext JS Library 1.1.1
45490  * Copyright(c) 2006-2007, Ext JS, LLC.
45491  * LGPL
45492  *
45493  */
45494  
45495 /**
45496  * @class Roo.HtmlEditorCore
45497  * @extends Roo.Component
45498  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
45499  *
45500  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45501  */
45502
45503 Roo.HtmlEditorCore = function(config){
45504     
45505     
45506     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
45507     
45508     
45509     this.addEvents({
45510         /**
45511          * @event initialize
45512          * Fires when the editor is fully initialized (including the iframe)
45513          * @param {Roo.HtmlEditorCore} this
45514          */
45515         initialize: true,
45516         /**
45517          * @event activate
45518          * Fires when the editor is first receives the focus. Any insertion must wait
45519          * until after this event.
45520          * @param {Roo.HtmlEditorCore} this
45521          */
45522         activate: true,
45523          /**
45524          * @event beforesync
45525          * Fires before the textarea is updated with content from the editor iframe. Return false
45526          * to cancel the sync.
45527          * @param {Roo.HtmlEditorCore} this
45528          * @param {String} html
45529          */
45530         beforesync: true,
45531          /**
45532          * @event beforepush
45533          * Fires before the iframe editor is updated with content from the textarea. Return false
45534          * to cancel the push.
45535          * @param {Roo.HtmlEditorCore} this
45536          * @param {String} html
45537          */
45538         beforepush: true,
45539          /**
45540          * @event sync
45541          * Fires when the textarea is updated with content from the editor iframe.
45542          * @param {Roo.HtmlEditorCore} this
45543          * @param {String} html
45544          */
45545         sync: true,
45546          /**
45547          * @event push
45548          * Fires when the iframe editor is updated with content from the textarea.
45549          * @param {Roo.HtmlEditorCore} this
45550          * @param {String} html
45551          */
45552         push: true,
45553         
45554         /**
45555          * @event editorevent
45556          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45557          * @param {Roo.HtmlEditorCore} this
45558          */
45559         editorevent: true
45560         
45561     });
45562     
45563     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
45564     
45565     // defaults : white / black...
45566     this.applyBlacklists();
45567     
45568     
45569     
45570 };
45571
45572
45573 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
45574
45575
45576      /**
45577      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
45578      */
45579     
45580     owner : false,
45581     
45582      /**
45583      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45584      *                        Roo.resizable.
45585      */
45586     resizable : false,
45587      /**
45588      * @cfg {Number} height (in pixels)
45589      */   
45590     height: 300,
45591    /**
45592      * @cfg {Number} width (in pixels)
45593      */   
45594     width: 500,
45595     
45596     /**
45597      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45598      * 
45599      */
45600     stylesheets: false,
45601     
45602     /**
45603      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
45604      */
45605     allowComments: false,
45606     // id of frame..
45607     frameId: false,
45608     
45609     // private properties
45610     validationEvent : false,
45611     deferHeight: true,
45612     initialized : false,
45613     activated : false,
45614     sourceEditMode : false,
45615     onFocus : Roo.emptyFn,
45616     iframePad:3,
45617     hideMode:'offsets',
45618     
45619     clearUp: true,
45620     
45621     // blacklist + whitelisted elements..
45622     black: false,
45623     white: false,
45624      
45625     bodyCls : '',
45626
45627     /**
45628      * Protected method that will not generally be called directly. It
45629      * is called when the editor initializes the iframe with HTML contents. Override this method if you
45630      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
45631      */
45632     getDocMarkup : function(){
45633         // body styles..
45634         var st = '';
45635         
45636         // inherit styels from page...?? 
45637         if (this.stylesheets === false) {
45638             
45639             Roo.get(document.head).select('style').each(function(node) {
45640                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45641             });
45642             
45643             Roo.get(document.head).select('link').each(function(node) { 
45644                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45645             });
45646             
45647         } else if (!this.stylesheets.length) {
45648                 // simple..
45649                 st = '<style type="text/css">' +
45650                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45651                    '</style>';
45652         } else {
45653             for (var i in this.stylesheets) {
45654                 if (typeof(this.stylesheets[i]) != 'string') {
45655                     continue;
45656                 }
45657                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
45658             }
45659             
45660         }
45661         
45662         st +=  '<style type="text/css">' +
45663             'IMG { cursor: pointer } ' +
45664         '</style>';
45665
45666         var cls = 'roo-htmleditor-body';
45667         
45668         if(this.bodyCls.length){
45669             cls += ' ' + this.bodyCls;
45670         }
45671         
45672         return '<html><head>' + st  +
45673             //<style type="text/css">' +
45674             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45675             //'</style>' +
45676             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
45677     },
45678
45679     // private
45680     onRender : function(ct, position)
45681     {
45682         var _t = this;
45683         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
45684         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
45685         
45686         
45687         this.el.dom.style.border = '0 none';
45688         this.el.dom.setAttribute('tabIndex', -1);
45689         this.el.addClass('x-hidden hide');
45690         
45691         
45692         
45693         if(Roo.isIE){ // fix IE 1px bogus margin
45694             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
45695         }
45696        
45697         
45698         this.frameId = Roo.id();
45699         
45700          
45701         
45702         var iframe = this.owner.wrap.createChild({
45703             tag: 'iframe',
45704             cls: 'form-control', // bootstrap..
45705             id: this.frameId,
45706             name: this.frameId,
45707             frameBorder : 'no',
45708             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
45709         }, this.el
45710         );
45711         
45712         
45713         this.iframe = iframe.dom;
45714
45715         this.assignDocWin();
45716         
45717         this.doc.designMode = 'on';
45718        
45719         this.doc.open();
45720         this.doc.write(this.getDocMarkup());
45721         this.doc.close();
45722
45723         
45724         var task = { // must defer to wait for browser to be ready
45725             run : function(){
45726                 //console.log("run task?" + this.doc.readyState);
45727                 this.assignDocWin();
45728                 if(this.doc.body || this.doc.readyState == 'complete'){
45729                     try {
45730                         this.doc.designMode="on";
45731                     } catch (e) {
45732                         return;
45733                     }
45734                     Roo.TaskMgr.stop(task);
45735                     this.initEditor.defer(10, this);
45736                 }
45737             },
45738             interval : 10,
45739             duration: 10000,
45740             scope: this
45741         };
45742         Roo.TaskMgr.start(task);
45743
45744     },
45745
45746     // private
45747     onResize : function(w, h)
45748     {
45749          Roo.log('resize: ' +w + ',' + h );
45750         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
45751         if(!this.iframe){
45752             return;
45753         }
45754         if(typeof w == 'number'){
45755             
45756             this.iframe.style.width = w + 'px';
45757         }
45758         if(typeof h == 'number'){
45759             
45760             this.iframe.style.height = h + 'px';
45761             if(this.doc){
45762                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
45763             }
45764         }
45765         
45766     },
45767
45768     /**
45769      * Toggles the editor between standard and source edit mode.
45770      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45771      */
45772     toggleSourceEdit : function(sourceEditMode){
45773         
45774         this.sourceEditMode = sourceEditMode === true;
45775         
45776         if(this.sourceEditMode){
45777  
45778             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
45779             
45780         }else{
45781             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
45782             //this.iframe.className = '';
45783             this.deferFocus();
45784         }
45785         //this.setSize(this.owner.wrap.getSize());
45786         //this.fireEvent('editmodechange', this, this.sourceEditMode);
45787     },
45788
45789     
45790   
45791
45792     /**
45793      * Protected method that will not generally be called directly. If you need/want
45794      * custom HTML cleanup, this is the method you should override.
45795      * @param {String} html The HTML to be cleaned
45796      * return {String} The cleaned HTML
45797      */
45798     cleanHtml : function(html){
45799         html = String(html);
45800         if(html.length > 5){
45801             if(Roo.isSafari){ // strip safari nonsense
45802                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
45803             }
45804         }
45805         if(html == '&nbsp;'){
45806             html = '';
45807         }
45808         return html;
45809     },
45810
45811     /**
45812      * HTML Editor -> Textarea
45813      * Protected method that will not generally be called directly. Syncs the contents
45814      * of the editor iframe with the textarea.
45815      */
45816     syncValue : function()
45817     {
45818         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
45819         if(this.initialized){
45820             var bd = (this.doc.body || this.doc.documentElement);
45821             //this.cleanUpPaste(); -- this is done else where and causes havoc..
45822             
45823             // not sure if this is really the place for this
45824             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
45825             // this has to update attributes that get duped.. like alt and caption..
45826             
45827             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
45828                  Roo.htmleditor.Block.factory(e);
45829             },this);
45830             
45831             
45832             var div = document.createElement('div');
45833             div.innerHTML = bd.innerHTML;
45834             // remove content editable. (blocks)
45835             
45836            
45837             
45838             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
45839             //?? tidy?
45840             var html = div.innerHTML;
45841             if(Roo.isSafari){
45842                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
45843                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
45844                 if(m && m[1]){
45845                     html = '<div style="'+m[0]+'">' + html + '</div>';
45846                 }
45847             }
45848             html = this.cleanHtml(html);
45849             // fix up the special chars.. normaly like back quotes in word...
45850             // however we do not want to do this with chinese..
45851             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
45852                 
45853                 var cc = match.charCodeAt();
45854
45855                 // Get the character value, handling surrogate pairs
45856                 if (match.length == 2) {
45857                     // It's a surrogate pair, calculate the Unicode code point
45858                     var high = match.charCodeAt(0) - 0xD800;
45859                     var low  = match.charCodeAt(1) - 0xDC00;
45860                     cc = (high * 0x400) + low + 0x10000;
45861                 }  else if (
45862                     (cc >= 0x4E00 && cc < 0xA000 ) ||
45863                     (cc >= 0x3400 && cc < 0x4E00 ) ||
45864                     (cc >= 0xf900 && cc < 0xfb00 )
45865                 ) {
45866                         return match;
45867                 }  
45868          
45869                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
45870                 return "&#" + cc + ";";
45871                 
45872                 
45873             });
45874             
45875             
45876              
45877             if(this.owner.fireEvent('beforesync', this, html) !== false){
45878                 this.el.dom.value = html;
45879                 this.owner.fireEvent('sync', this, html);
45880             }
45881         }
45882     },
45883
45884     /**
45885      * TEXTAREA -> EDITABLE
45886      * Protected method that will not generally be called directly. Pushes the value of the textarea
45887      * into the iframe editor.
45888      */
45889     pushValue : function()
45890     {
45891         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
45892         if(this.initialized){
45893             var v = this.el.dom.value.trim();
45894             
45895             
45896             if(this.owner.fireEvent('beforepush', this, v) !== false){
45897                 var d = (this.doc.body || this.doc.documentElement);
45898                 d.innerHTML = v;
45899                 //this.cleanUpPaste();
45900                 this.el.dom.value = d.innerHTML;
45901                 this.owner.fireEvent('push', this, v);
45902             }
45903             
45904             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
45905                 
45906                 Roo.htmleditor.Block.factory(e);
45907                 
45908             },this);
45909             var lc = this.doc.body.lastChild;
45910             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
45911                 // add an extra line at the end.
45912                 this.doc.body.appendChild(this.doc.createElement('br'));
45913             }
45914             
45915             
45916         }
45917     },
45918
45919     // private
45920     deferFocus : function(){
45921         this.focus.defer(10, this);
45922     },
45923
45924     // doc'ed in Field
45925     focus : function(){
45926         if(this.win && !this.sourceEditMode){
45927             this.win.focus();
45928         }else{
45929             this.el.focus();
45930         }
45931     },
45932     
45933     assignDocWin: function()
45934     {
45935         var iframe = this.iframe;
45936         
45937          if(Roo.isIE){
45938             this.doc = iframe.contentWindow.document;
45939             this.win = iframe.contentWindow;
45940         } else {
45941 //            if (!Roo.get(this.frameId)) {
45942 //                return;
45943 //            }
45944 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
45945 //            this.win = Roo.get(this.frameId).dom.contentWindow;
45946             
45947             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
45948                 return;
45949             }
45950             
45951             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
45952             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
45953         }
45954     },
45955     
45956     // private
45957     initEditor : function(){
45958         //console.log("INIT EDITOR");
45959         this.assignDocWin();
45960         
45961         
45962         
45963         this.doc.designMode="on";
45964         this.doc.open();
45965         this.doc.write(this.getDocMarkup());
45966         this.doc.close();
45967         
45968         var dbody = (this.doc.body || this.doc.documentElement);
45969         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
45970         // this copies styles from the containing element into thsi one..
45971         // not sure why we need all of this..
45972         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
45973         
45974         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
45975         //ss['background-attachment'] = 'fixed'; // w3c
45976         dbody.bgProperties = 'fixed'; // ie
45977         //Roo.DomHelper.applyStyles(dbody, ss);
45978         Roo.EventManager.on(this.doc, {
45979             //'mousedown': this.onEditorEvent,
45980             'mouseup': this.onEditorEvent,
45981             'dblclick': this.onEditorEvent,
45982             'click': this.onEditorEvent,
45983             'keyup': this.onEditorEvent,
45984             'paste': this.onPasteEvent,
45985             buffer:100,
45986             scope: this
45987         });
45988         if(Roo.isGecko){
45989             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
45990         }
45991         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
45992             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
45993         }
45994         this.initialized = true;
45995
45996         
45997         // initialize special key events - enter
45998         new Roo.htmleditor.KeyEnter({core : this});
45999         
46000          
46001         
46002         this.owner.fireEvent('initialize', this);
46003         this.pushValue();
46004     },
46005     
46006     onPasteEvent : function(e,v)  {
46007          // default behaveiour should be our local cleanup paste? (optional?)
46008          // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
46009          this.owner.fireEvent('paste', e, v);
46010     },
46011     // private
46012     onDestroy : function(){
46013         
46014         
46015         
46016         if(this.rendered){
46017             
46018             //for (var i =0; i < this.toolbars.length;i++) {
46019             //    // fixme - ask toolbars for heights?
46020             //    this.toolbars[i].onDestroy();
46021            // }
46022             
46023             //this.wrap.dom.innerHTML = '';
46024             //this.wrap.remove();
46025         }
46026     },
46027
46028     // private
46029     onFirstFocus : function(){
46030         
46031         this.assignDocWin();
46032         
46033         
46034         this.activated = true;
46035          
46036     
46037         if(Roo.isGecko){ // prevent silly gecko errors
46038             this.win.focus();
46039             var s = this.win.getSelection();
46040             if(!s.focusNode || s.focusNode.nodeType != 3){
46041                 var r = s.getRangeAt(0);
46042                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
46043                 r.collapse(true);
46044                 this.deferFocus();
46045             }
46046             try{
46047                 this.execCmd('useCSS', true);
46048                 this.execCmd('styleWithCSS', false);
46049             }catch(e){}
46050         }
46051         this.owner.fireEvent('activate', this);
46052     },
46053
46054     // private
46055     adjustFont: function(btn){
46056         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
46057         //if(Roo.isSafari){ // safari
46058         //    adjust *= 2;
46059        // }
46060         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
46061         if(Roo.isSafari){ // safari
46062             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
46063             v =  (v < 10) ? 10 : v;
46064             v =  (v > 48) ? 48 : v;
46065             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
46066             
46067         }
46068         
46069         
46070         v = Math.max(1, v+adjust);
46071         
46072         this.execCmd('FontSize', v  );
46073     },
46074
46075     onEditorEvent : function(e)
46076     {
46077         this.owner.fireEvent('editorevent', this, e);
46078       //  this.updateToolbar();
46079         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
46080     },
46081
46082     insertTag : function(tg)
46083     {
46084         // could be a bit smarter... -> wrap the current selected tRoo..
46085         if (tg.toLowerCase() == 'span' ||
46086             tg.toLowerCase() == 'code' ||
46087             tg.toLowerCase() == 'sup' ||
46088             tg.toLowerCase() == 'sub' 
46089             ) {
46090             
46091             range = this.createRange(this.getSelection());
46092             var wrappingNode = this.doc.createElement(tg.toLowerCase());
46093             wrappingNode.appendChild(range.extractContents());
46094             range.insertNode(wrappingNode);
46095
46096             return;
46097             
46098             
46099             
46100         }
46101         this.execCmd("formatblock",   tg);
46102         
46103     },
46104     
46105     insertText : function(txt)
46106     {
46107         
46108         
46109         var range = this.createRange();
46110         range.deleteContents();
46111                //alert(Sender.getAttribute('label'));
46112                
46113         range.insertNode(this.doc.createTextNode(txt));
46114     } ,
46115     
46116      
46117
46118     /**
46119      * Executes a Midas editor command on the editor document and performs necessary focus and
46120      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
46121      * @param {String} cmd The Midas command
46122      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46123      */
46124     relayCmd : function(cmd, value){
46125         this.win.focus();
46126         this.execCmd(cmd, value);
46127         this.owner.fireEvent('editorevent', this);
46128         //this.updateToolbar();
46129         this.owner.deferFocus();
46130     },
46131
46132     /**
46133      * Executes a Midas editor command directly on the editor document.
46134      * For visual commands, you should use {@link #relayCmd} instead.
46135      * <b>This should only be called after the editor is initialized.</b>
46136      * @param {String} cmd The Midas command
46137      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46138      */
46139     execCmd : function(cmd, value){
46140         this.doc.execCommand(cmd, false, value === undefined ? null : value);
46141         this.syncValue();
46142     },
46143  
46144  
46145    
46146     /**
46147      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
46148      * to insert tRoo.
46149      * @param {String} text | dom node.. 
46150      */
46151     insertAtCursor : function(text)
46152     {
46153         
46154         if(!this.activated){
46155             return;
46156         }
46157         /*
46158         if(Roo.isIE){
46159             this.win.focus();
46160             var r = this.doc.selection.createRange();
46161             if(r){
46162                 r.collapse(true);
46163                 r.pasteHTML(text);
46164                 this.syncValue();
46165                 this.deferFocus();
46166             
46167             }
46168             return;
46169         }
46170         */
46171         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
46172             this.win.focus();
46173             
46174             
46175             // from jquery ui (MIT licenced)
46176             var range, node;
46177             var win = this.win;
46178             
46179             if (win.getSelection && win.getSelection().getRangeAt) {
46180                 range = win.getSelection().getRangeAt(0);
46181                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
46182                 range.insertNode(node);
46183             } else if (win.document.selection && win.document.selection.createRange) {
46184                 // no firefox support
46185                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46186                 win.document.selection.createRange().pasteHTML(txt);
46187             } else {
46188                 // no firefox support
46189                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46190                 this.execCmd('InsertHTML', txt);
46191             } 
46192             
46193             this.syncValue();
46194             
46195             this.deferFocus();
46196         }
46197     },
46198  // private
46199     mozKeyPress : function(e){
46200         if(e.ctrlKey){
46201             var c = e.getCharCode(), cmd;
46202           
46203             if(c > 0){
46204                 c = String.fromCharCode(c).toLowerCase();
46205                 switch(c){
46206                     case 'b':
46207                         cmd = 'bold';
46208                         break;
46209                     case 'i':
46210                         cmd = 'italic';
46211                         break;
46212                     
46213                     case 'u':
46214                         cmd = 'underline';
46215                         break;
46216                     
46217                     //case 'v':
46218                       //  this.cleanUpPaste.defer(100, this);
46219                       //  return;
46220                         
46221                 }
46222                 if(cmd){
46223                     this.win.focus();
46224                     this.execCmd(cmd);
46225                     this.deferFocus();
46226                     e.preventDefault();
46227                 }
46228                 
46229             }
46230         }
46231     },
46232
46233     // private
46234     fixKeys : function(){ // load time branching for fastest keydown performance
46235         if(Roo.isIE){
46236             return function(e){
46237                 var k = e.getKey(), r;
46238                 if(k == e.TAB){
46239                     e.stopEvent();
46240                     r = this.doc.selection.createRange();
46241                     if(r){
46242                         r.collapse(true);
46243                         r.pasteHTML('&#160;&#160;&#160;&#160;');
46244                         this.deferFocus();
46245                     }
46246                     return;
46247                 }
46248                 
46249                 if(k == e.ENTER){
46250                     r = this.doc.selection.createRange();
46251                     if(r){
46252                         var target = r.parentElement();
46253                         if(!target || target.tagName.toLowerCase() != 'li'){
46254                             e.stopEvent();
46255                             r.pasteHTML('<br/>');
46256                             r.collapse(false);
46257                             r.select();
46258                         }
46259                     }
46260                 }
46261                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
46262                 //    this.cleanUpPaste.defer(100, this);
46263                 //    return;
46264                 //}
46265                 
46266                 
46267             };
46268         }else if(Roo.isOpera){
46269             return function(e){
46270                 var k = e.getKey();
46271                 if(k == e.TAB){
46272                     e.stopEvent();
46273                     this.win.focus();
46274                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
46275                     this.deferFocus();
46276                 }
46277                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
46278                 //    this.cleanUpPaste.defer(100, this);
46279                  //   return;
46280                 //}
46281                 
46282             };
46283         }else if(Roo.isSafari){
46284             return function(e){
46285                 var k = e.getKey();
46286                 
46287                 if(k == e.TAB){
46288                     e.stopEvent();
46289                     this.execCmd('InsertText','\t');
46290                     this.deferFocus();
46291                     return;
46292                 }
46293                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
46294                  //   this.cleanUpPaste.defer(100, this);
46295                  //   return;
46296                // }
46297                 
46298              };
46299         }
46300     }(),
46301     
46302     getAllAncestors: function()
46303     {
46304         var p = this.getSelectedNode();
46305         var a = [];
46306         if (!p) {
46307             a.push(p); // push blank onto stack..
46308             p = this.getParentElement();
46309         }
46310         
46311         
46312         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
46313             a.push(p);
46314             p = p.parentNode;
46315         }
46316         a.push(this.doc.body);
46317         return a;
46318     },
46319     lastSel : false,
46320     lastSelNode : false,
46321     
46322     
46323     getSelection : function() 
46324     {
46325         this.assignDocWin();
46326         return Roo.isIE ? this.doc.selection : this.win.getSelection();
46327     },
46328     
46329     getSelectedNode: function() 
46330     {
46331         // this may only work on Gecko!!!
46332         
46333         // should we cache this!!!!
46334         
46335         
46336         
46337          
46338         var range = this.createRange(this.getSelection()).cloneRange();
46339         
46340         if (Roo.isIE) {
46341             var parent = range.parentElement();
46342             while (true) {
46343                 var testRange = range.duplicate();
46344                 testRange.moveToElementText(parent);
46345                 if (testRange.inRange(range)) {
46346                     break;
46347                 }
46348                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
46349                     break;
46350                 }
46351                 parent = parent.parentElement;
46352             }
46353             return parent;
46354         }
46355         
46356         // is ancestor a text element.
46357         var ac =  range.commonAncestorContainer;
46358         if (ac.nodeType == 3) {
46359             ac = ac.parentNode;
46360         }
46361         
46362         var ar = ac.childNodes;
46363          
46364         var nodes = [];
46365         var other_nodes = [];
46366         var has_other_nodes = false;
46367         for (var i=0;i<ar.length;i++) {
46368             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
46369                 continue;
46370             }
46371             // fullly contained node.
46372             
46373             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
46374                 nodes.push(ar[i]);
46375                 continue;
46376             }
46377             
46378             // probably selected..
46379             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
46380                 other_nodes.push(ar[i]);
46381                 continue;
46382             }
46383             // outer..
46384             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
46385                 continue;
46386             }
46387             
46388             
46389             has_other_nodes = true;
46390         }
46391         if (!nodes.length && other_nodes.length) {
46392             nodes= other_nodes;
46393         }
46394         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
46395             return false;
46396         }
46397         
46398         return nodes[0];
46399     },
46400     createRange: function(sel)
46401     {
46402         // this has strange effects when using with 
46403         // top toolbar - not sure if it's a great idea.
46404         //this.editor.contentWindow.focus();
46405         if (typeof sel != "undefined") {
46406             try {
46407                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
46408             } catch(e) {
46409                 return this.doc.createRange();
46410             }
46411         } else {
46412             return this.doc.createRange();
46413         }
46414     },
46415     getParentElement: function()
46416     {
46417         
46418         this.assignDocWin();
46419         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
46420         
46421         var range = this.createRange(sel);
46422          
46423         try {
46424             var p = range.commonAncestorContainer;
46425             while (p.nodeType == 3) { // text node
46426                 p = p.parentNode;
46427             }
46428             return p;
46429         } catch (e) {
46430             return null;
46431         }
46432     
46433     },
46434     /***
46435      *
46436      * Range intersection.. the hard stuff...
46437      *  '-1' = before
46438      *  '0' = hits..
46439      *  '1' = after.
46440      *         [ -- selected range --- ]
46441      *   [fail]                        [fail]
46442      *
46443      *    basically..
46444      *      if end is before start or  hits it. fail.
46445      *      if start is after end or hits it fail.
46446      *
46447      *   if either hits (but other is outside. - then it's not 
46448      *   
46449      *    
46450      **/
46451     
46452     
46453     // @see http://www.thismuchiknow.co.uk/?p=64.
46454     rangeIntersectsNode : function(range, node)
46455     {
46456         var nodeRange = node.ownerDocument.createRange();
46457         try {
46458             nodeRange.selectNode(node);
46459         } catch (e) {
46460             nodeRange.selectNodeContents(node);
46461         }
46462     
46463         var rangeStartRange = range.cloneRange();
46464         rangeStartRange.collapse(true);
46465     
46466         var rangeEndRange = range.cloneRange();
46467         rangeEndRange.collapse(false);
46468     
46469         var nodeStartRange = nodeRange.cloneRange();
46470         nodeStartRange.collapse(true);
46471     
46472         var nodeEndRange = nodeRange.cloneRange();
46473         nodeEndRange.collapse(false);
46474     
46475         return rangeStartRange.compareBoundaryPoints(
46476                  Range.START_TO_START, nodeEndRange) == -1 &&
46477                rangeEndRange.compareBoundaryPoints(
46478                  Range.START_TO_START, nodeStartRange) == 1;
46479         
46480          
46481     },
46482     rangeCompareNode : function(range, node)
46483     {
46484         var nodeRange = node.ownerDocument.createRange();
46485         try {
46486             nodeRange.selectNode(node);
46487         } catch (e) {
46488             nodeRange.selectNodeContents(node);
46489         }
46490         
46491         
46492         range.collapse(true);
46493     
46494         nodeRange.collapse(true);
46495      
46496         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
46497         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
46498          
46499         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
46500         
46501         var nodeIsBefore   =  ss == 1;
46502         var nodeIsAfter    = ee == -1;
46503         
46504         if (nodeIsBefore && nodeIsAfter) {
46505             return 0; // outer
46506         }
46507         if (!nodeIsBefore && nodeIsAfter) {
46508             return 1; //right trailed.
46509         }
46510         
46511         if (nodeIsBefore && !nodeIsAfter) {
46512             return 2;  // left trailed.
46513         }
46514         // fully contined.
46515         return 3;
46516     },
46517 /*
46518     // private? - in a new class?
46519     cleanUpPaste :  function()
46520     {
46521         // cleans up the whole document..
46522         Roo.log('cleanuppaste');
46523         
46524         this.cleanUpChild(this.doc.body);
46525         var clean = this.cleanWordChars(this.doc.body.innerHTML);
46526         if (clean != this.doc.body.innerHTML) {
46527             this.doc.body.innerHTML = clean;
46528         }
46529         
46530     },
46531     */
46532     cleanWordChars : function(input) {// change the chars to hex code
46533         var he = Roo.HtmlEditorCore;
46534         
46535         var output = input;
46536         Roo.each(he.swapCodes, function(sw) { 
46537             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
46538             
46539             output = output.replace(swapper, sw[1]);
46540         });
46541         
46542         return output;
46543     },
46544     
46545      
46546     
46547         
46548     
46549     cleanUpChild : function (node)
46550     {
46551         
46552         new Roo.htmleditor.FilterComment({node : node});
46553         new Roo.htmleditor.FilterAttributes({
46554                 node : node,
46555                 attrib_black : this.ablack,
46556                 attrib_clean : this.aclean,
46557                 style_white : this.cwhite,
46558                 style_black : this.cblack
46559         });
46560         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
46561         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
46562          
46563         
46564     },
46565     
46566     /**
46567      * Clean up MS wordisms...
46568      * @deprecated - use filter directly
46569      */
46570     cleanWord : function(node)
46571     {
46572         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
46573         
46574     },
46575    
46576     
46577     /**
46578
46579      * @deprecated - use filters
46580      */
46581     cleanTableWidths : function(node)
46582     {
46583         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
46584         
46585  
46586     },
46587     
46588      
46589         
46590     applyBlacklists : function()
46591     {
46592         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
46593         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
46594         
46595         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
46596         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
46597         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
46598         
46599         this.white = [];
46600         this.black = [];
46601         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
46602             if (b.indexOf(tag) > -1) {
46603                 return;
46604             }
46605             this.white.push(tag);
46606             
46607         }, this);
46608         
46609         Roo.each(w, function(tag) {
46610             if (b.indexOf(tag) > -1) {
46611                 return;
46612             }
46613             if (this.white.indexOf(tag) > -1) {
46614                 return;
46615             }
46616             this.white.push(tag);
46617             
46618         }, this);
46619         
46620         
46621         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
46622             if (w.indexOf(tag) > -1) {
46623                 return;
46624             }
46625             this.black.push(tag);
46626             
46627         }, this);
46628         
46629         Roo.each(b, function(tag) {
46630             if (w.indexOf(tag) > -1) {
46631                 return;
46632             }
46633             if (this.black.indexOf(tag) > -1) {
46634                 return;
46635             }
46636             this.black.push(tag);
46637             
46638         }, this);
46639         
46640         
46641         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
46642         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
46643         
46644         this.cwhite = [];
46645         this.cblack = [];
46646         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
46647             if (b.indexOf(tag) > -1) {
46648                 return;
46649             }
46650             this.cwhite.push(tag);
46651             
46652         }, this);
46653         
46654         Roo.each(w, function(tag) {
46655             if (b.indexOf(tag) > -1) {
46656                 return;
46657             }
46658             if (this.cwhite.indexOf(tag) > -1) {
46659                 return;
46660             }
46661             this.cwhite.push(tag);
46662             
46663         }, this);
46664         
46665         
46666         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
46667             if (w.indexOf(tag) > -1) {
46668                 return;
46669             }
46670             this.cblack.push(tag);
46671             
46672         }, this);
46673         
46674         Roo.each(b, function(tag) {
46675             if (w.indexOf(tag) > -1) {
46676                 return;
46677             }
46678             if (this.cblack.indexOf(tag) > -1) {
46679                 return;
46680             }
46681             this.cblack.push(tag);
46682             
46683         }, this);
46684     },
46685     
46686     setStylesheets : function(stylesheets)
46687     {
46688         if(typeof(stylesheets) == 'string'){
46689             Roo.get(this.iframe.contentDocument.head).createChild({
46690                 tag : 'link',
46691                 rel : 'stylesheet',
46692                 type : 'text/css',
46693                 href : stylesheets
46694             });
46695             
46696             return;
46697         }
46698         var _this = this;
46699      
46700         Roo.each(stylesheets, function(s) {
46701             if(!s.length){
46702                 return;
46703             }
46704             
46705             Roo.get(_this.iframe.contentDocument.head).createChild({
46706                 tag : 'link',
46707                 rel : 'stylesheet',
46708                 type : 'text/css',
46709                 href : s
46710             });
46711         });
46712
46713         
46714     },
46715     
46716     removeStylesheets : function()
46717     {
46718         var _this = this;
46719         
46720         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
46721             s.remove();
46722         });
46723     },
46724     
46725     setStyle : function(style)
46726     {
46727         Roo.get(this.iframe.contentDocument.head).createChild({
46728             tag : 'style',
46729             type : 'text/css',
46730             html : style
46731         });
46732
46733         return;
46734     }
46735     
46736     // hide stuff that is not compatible
46737     /**
46738      * @event blur
46739      * @hide
46740      */
46741     /**
46742      * @event change
46743      * @hide
46744      */
46745     /**
46746      * @event focus
46747      * @hide
46748      */
46749     /**
46750      * @event specialkey
46751      * @hide
46752      */
46753     /**
46754      * @cfg {String} fieldClass @hide
46755      */
46756     /**
46757      * @cfg {String} focusClass @hide
46758      */
46759     /**
46760      * @cfg {String} autoCreate @hide
46761      */
46762     /**
46763      * @cfg {String} inputType @hide
46764      */
46765     /**
46766      * @cfg {String} invalidClass @hide
46767      */
46768     /**
46769      * @cfg {String} invalidText @hide
46770      */
46771     /**
46772      * @cfg {String} msgFx @hide
46773      */
46774     /**
46775      * @cfg {String} validateOnBlur @hide
46776      */
46777 });
46778
46779 Roo.HtmlEditorCore.white = [
46780         'area', 'br', 'img', 'input', 'hr', 'wbr',
46781         
46782        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
46783        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
46784        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
46785        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
46786        'table',   'ul',         'xmp', 
46787        
46788        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
46789       'thead',   'tr', 
46790      
46791       'dir', 'menu', 'ol', 'ul', 'dl',
46792        
46793       'embed',  'object'
46794 ];
46795
46796
46797 Roo.HtmlEditorCore.black = [
46798     //    'embed',  'object', // enable - backend responsiblity to clean thiese
46799         'applet', // 
46800         'base',   'basefont', 'bgsound', 'blink',  'body', 
46801         'frame',  'frameset', 'head',    'html',   'ilayer', 
46802         'iframe', 'layer',  'link',     'meta',    'object',   
46803         'script', 'style' ,'title',  'xml' // clean later..
46804 ];
46805 Roo.HtmlEditorCore.clean = [
46806     'script', 'style', 'title', 'xml'
46807 ];
46808 Roo.HtmlEditorCore.tag_remove = [
46809     'font'
46810 ];
46811 // attributes..
46812
46813 Roo.HtmlEditorCore.ablack = [
46814     'on'
46815 ];
46816     
46817 Roo.HtmlEditorCore.aclean = [ 
46818     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
46819 ];
46820
46821 // protocols..
46822 Roo.HtmlEditorCore.pwhite= [
46823         'http',  'https',  'mailto'
46824 ];
46825
46826 // white listed style attributes.
46827 Roo.HtmlEditorCore.cwhite= [
46828       //  'text-align', /// default is to allow most things..
46829       
46830          
46831 //        'font-size'//??
46832 ];
46833
46834 // black listed style attributes.
46835 Roo.HtmlEditorCore.cblack= [
46836       //  'font-size' -- this can be set by the project 
46837 ];
46838
46839
46840 Roo.HtmlEditorCore.swapCodes   =[ 
46841     [    8211, "&#8211;" ], 
46842     [    8212, "&#8212;" ], 
46843     [    8216,  "'" ],  
46844     [    8217, "'" ],  
46845     [    8220, '"' ],  
46846     [    8221, '"' ],  
46847     [    8226, "*" ],  
46848     [    8230, "..." ]
46849 ]; 
46850
46851     //<script type="text/javascript">
46852
46853 /*
46854  * Ext JS Library 1.1.1
46855  * Copyright(c) 2006-2007, Ext JS, LLC.
46856  * Licence LGPL
46857  * 
46858  */
46859  
46860  
46861 Roo.form.HtmlEditor = function(config){
46862     
46863     
46864     
46865     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
46866     
46867     if (!this.toolbars) {
46868         this.toolbars = [];
46869     }
46870     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
46871     
46872     
46873 };
46874
46875 /**
46876  * @class Roo.form.HtmlEditor
46877  * @extends Roo.form.Field
46878  * Provides a lightweight HTML Editor component.
46879  *
46880  * This has been tested on Fireforx / Chrome.. IE may not be so great..
46881  * 
46882  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
46883  * supported by this editor.</b><br/><br/>
46884  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
46885  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46886  */
46887 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
46888     /**
46889      * @cfg {Boolean} clearUp
46890      */
46891     clearUp : true,
46892       /**
46893      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
46894      */
46895     toolbars : false,
46896    
46897      /**
46898      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46899      *                        Roo.resizable.
46900      */
46901     resizable : false,
46902      /**
46903      * @cfg {Number} height (in pixels)
46904      */   
46905     height: 300,
46906    /**
46907      * @cfg {Number} width (in pixels)
46908      */   
46909     width: 500,
46910     
46911     /**
46912      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
46913      * 
46914      */
46915     stylesheets: false,
46916     
46917     
46918      /**
46919      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
46920      * 
46921      */
46922     cblack: false,
46923     /**
46924      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
46925      * 
46926      */
46927     cwhite: false,
46928     
46929      /**
46930      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
46931      * 
46932      */
46933     black: false,
46934     /**
46935      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
46936      * 
46937      */
46938     white: false,
46939     /**
46940      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46941      */
46942     allowComments: false,
46943     /**
46944      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
46945      */
46946     
46947     
46948      bodyCls : '',
46949     
46950     // id of frame..
46951     frameId: false,
46952     
46953     // private properties
46954     validationEvent : false,
46955     deferHeight: true,
46956     initialized : false,
46957     activated : false,
46958     
46959     onFocus : Roo.emptyFn,
46960     iframePad:3,
46961     hideMode:'offsets',
46962     
46963     actionMode : 'container', // defaults to hiding it...
46964     
46965     defaultAutoCreate : { // modified by initCompnoent..
46966         tag: "textarea",
46967         style:"width:500px;height:300px;",
46968         autocomplete: "new-password"
46969     },
46970
46971     // private
46972     initComponent : function(){
46973         this.addEvents({
46974             /**
46975              * @event initialize
46976              * Fires when the editor is fully initialized (including the iframe)
46977              * @param {HtmlEditor} this
46978              */
46979             initialize: true,
46980             /**
46981              * @event activate
46982              * Fires when the editor is first receives the focus. Any insertion must wait
46983              * until after this event.
46984              * @param {HtmlEditor} this
46985              */
46986             activate: true,
46987              /**
46988              * @event beforesync
46989              * Fires before the textarea is updated with content from the editor iframe. Return false
46990              * to cancel the sync.
46991              * @param {HtmlEditor} this
46992              * @param {String} html
46993              */
46994             beforesync: true,
46995              /**
46996              * @event beforepush
46997              * Fires before the iframe editor is updated with content from the textarea. Return false
46998              * to cancel the push.
46999              * @param {HtmlEditor} this
47000              * @param {String} html
47001              */
47002             beforepush: true,
47003              /**
47004              * @event sync
47005              * Fires when the textarea is updated with content from the editor iframe.
47006              * @param {HtmlEditor} this
47007              * @param {String} html
47008              */
47009             sync: true,
47010              /**
47011              * @event push
47012              * Fires when the iframe editor is updated with content from the textarea.
47013              * @param {HtmlEditor} this
47014              * @param {String} html
47015              */
47016             push: true,
47017              /**
47018              * @event editmodechange
47019              * Fires when the editor switches edit modes
47020              * @param {HtmlEditor} this
47021              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
47022              */
47023             editmodechange: true,
47024             /**
47025              * @event editorevent
47026              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
47027              * @param {HtmlEditor} this
47028              */
47029             editorevent: true,
47030             /**
47031              * @event firstfocus
47032              * Fires when on first focus - needed by toolbars..
47033              * @param {HtmlEditor} this
47034              */
47035             firstfocus: true,
47036             /**
47037              * @event autosave
47038              * Auto save the htmlEditor value as a file into Events
47039              * @param {HtmlEditor} this
47040              */
47041             autosave: true,
47042             /**
47043              * @event savedpreview
47044              * preview the saved version of htmlEditor
47045              * @param {HtmlEditor} this
47046              */
47047             savedpreview: true,
47048             
47049             /**
47050             * @event stylesheetsclick
47051             * Fires when press the Sytlesheets button
47052             * @param {Roo.HtmlEditorCore} this
47053             */
47054             stylesheetsclick: true,
47055             /**
47056             * @event paste
47057             * Fires when press user pastes into the editor
47058             * @param {Roo.HtmlEditorCore} this
47059             */
47060             paste: true 
47061         });
47062         this.defaultAutoCreate =  {
47063             tag: "textarea",
47064             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
47065             autocomplete: "new-password"
47066         };
47067     },
47068
47069     /**
47070      * Protected method that will not generally be called directly. It
47071      * is called when the editor creates its toolbar. Override this method if you need to
47072      * add custom toolbar buttons.
47073      * @param {HtmlEditor} editor
47074      */
47075     createToolbar : function(editor){
47076         Roo.log("create toolbars");
47077         if (!editor.toolbars || !editor.toolbars.length) {
47078             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
47079         }
47080         
47081         for (var i =0 ; i < editor.toolbars.length;i++) {
47082             editor.toolbars[i] = Roo.factory(
47083                     typeof(editor.toolbars[i]) == 'string' ?
47084                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
47085                 Roo.form.HtmlEditor);
47086             editor.toolbars[i].init(editor);
47087         }
47088          
47089         
47090     },
47091
47092      
47093     // private
47094     onRender : function(ct, position)
47095     {
47096         var _t = this;
47097         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
47098         
47099         this.wrap = this.el.wrap({
47100             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
47101         });
47102         
47103         this.editorcore.onRender(ct, position);
47104          
47105         if (this.resizable) {
47106             this.resizeEl = new Roo.Resizable(this.wrap, {
47107                 pinned : true,
47108                 wrap: true,
47109                 dynamic : true,
47110                 minHeight : this.height,
47111                 height: this.height,
47112                 handles : this.resizable,
47113                 width: this.width,
47114                 listeners : {
47115                     resize : function(r, w, h) {
47116                         _t.onResize(w,h); // -something
47117                     }
47118                 }
47119             });
47120             
47121         }
47122         this.createToolbar(this);
47123        
47124         
47125         if(!this.width){
47126             this.setSize(this.wrap.getSize());
47127         }
47128         if (this.resizeEl) {
47129             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
47130             // should trigger onReize..
47131         }
47132         
47133         this.keyNav = new Roo.KeyNav(this.el, {
47134             
47135             "tab" : function(e){
47136                 e.preventDefault();
47137                 
47138                 var value = this.getValue();
47139                 
47140                 var start = this.el.dom.selectionStart;
47141                 var end = this.el.dom.selectionEnd;
47142                 
47143                 if(!e.shiftKey){
47144                     
47145                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
47146                     this.el.dom.setSelectionRange(end + 1, end + 1);
47147                     return;
47148                 }
47149                 
47150                 var f = value.substring(0, start).split("\t");
47151                 
47152                 if(f.pop().length != 0){
47153                     return;
47154                 }
47155                 
47156                 this.setValue(f.join("\t") + value.substring(end));
47157                 this.el.dom.setSelectionRange(start - 1, start - 1);
47158                 
47159             },
47160             
47161             "home" : function(e){
47162                 e.preventDefault();
47163                 
47164                 var curr = this.el.dom.selectionStart;
47165                 var lines = this.getValue().split("\n");
47166                 
47167                 if(!lines.length){
47168                     return;
47169                 }
47170                 
47171                 if(e.ctrlKey){
47172                     this.el.dom.setSelectionRange(0, 0);
47173                     return;
47174                 }
47175                 
47176                 var pos = 0;
47177                 
47178                 for (var i = 0; i < lines.length;i++) {
47179                     pos += lines[i].length;
47180                     
47181                     if(i != 0){
47182                         pos += 1;
47183                     }
47184                     
47185                     if(pos < curr){
47186                         continue;
47187                     }
47188                     
47189                     pos -= lines[i].length;
47190                     
47191                     break;
47192                 }
47193                 
47194                 if(!e.shiftKey){
47195                     this.el.dom.setSelectionRange(pos, pos);
47196                     return;
47197                 }
47198                 
47199                 this.el.dom.selectionStart = pos;
47200                 this.el.dom.selectionEnd = curr;
47201             },
47202             
47203             "end" : function(e){
47204                 e.preventDefault();
47205                 
47206                 var curr = this.el.dom.selectionStart;
47207                 var lines = this.getValue().split("\n");
47208                 
47209                 if(!lines.length){
47210                     return;
47211                 }
47212                 
47213                 if(e.ctrlKey){
47214                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
47215                     return;
47216                 }
47217                 
47218                 var pos = 0;
47219                 
47220                 for (var i = 0; i < lines.length;i++) {
47221                     
47222                     pos += lines[i].length;
47223                     
47224                     if(i != 0){
47225                         pos += 1;
47226                     }
47227                     
47228                     if(pos < curr){
47229                         continue;
47230                     }
47231                     
47232                     break;
47233                 }
47234                 
47235                 if(!e.shiftKey){
47236                     this.el.dom.setSelectionRange(pos, pos);
47237                     return;
47238                 }
47239                 
47240                 this.el.dom.selectionStart = curr;
47241                 this.el.dom.selectionEnd = pos;
47242             },
47243
47244             scope : this,
47245
47246             doRelay : function(foo, bar, hname){
47247                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47248             },
47249
47250             forceKeyDown: true
47251         });
47252         
47253 //        if(this.autosave && this.w){
47254 //            this.autoSaveFn = setInterval(this.autosave, 1000);
47255 //        }
47256     },
47257
47258     // private
47259     onResize : function(w, h)
47260     {
47261         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
47262         var ew = false;
47263         var eh = false;
47264         
47265         if(this.el ){
47266             if(typeof w == 'number'){
47267                 var aw = w - this.wrap.getFrameWidth('lr');
47268                 this.el.setWidth(this.adjustWidth('textarea', aw));
47269                 ew = aw;
47270             }
47271             if(typeof h == 'number'){
47272                 var tbh = 0;
47273                 for (var i =0; i < this.toolbars.length;i++) {
47274                     // fixme - ask toolbars for heights?
47275                     tbh += this.toolbars[i].tb.el.getHeight();
47276                     if (this.toolbars[i].footer) {
47277                         tbh += this.toolbars[i].footer.el.getHeight();
47278                     }
47279                 }
47280                 
47281                 
47282                 
47283                 
47284                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
47285                 ah -= 5; // knock a few pixes off for look..
47286 //                Roo.log(ah);
47287                 this.el.setHeight(this.adjustWidth('textarea', ah));
47288                 var eh = ah;
47289             }
47290         }
47291         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
47292         this.editorcore.onResize(ew,eh);
47293         
47294     },
47295
47296     /**
47297      * Toggles the editor between standard and source edit mode.
47298      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
47299      */
47300     toggleSourceEdit : function(sourceEditMode)
47301     {
47302         this.editorcore.toggleSourceEdit(sourceEditMode);
47303         
47304         if(this.editorcore.sourceEditMode){
47305             Roo.log('editor - showing textarea');
47306             
47307 //            Roo.log('in');
47308 //            Roo.log(this.syncValue());
47309             this.editorcore.syncValue();
47310             this.el.removeClass('x-hidden');
47311             this.el.dom.removeAttribute('tabIndex');
47312             this.el.focus();
47313             this.el.dom.scrollTop = 0;
47314             
47315             
47316             for (var i = 0; i < this.toolbars.length; i++) {
47317                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
47318                     this.toolbars[i].tb.hide();
47319                     this.toolbars[i].footer.hide();
47320                 }
47321             }
47322             
47323         }else{
47324             Roo.log('editor - hiding textarea');
47325 //            Roo.log('out')
47326 //            Roo.log(this.pushValue()); 
47327             this.editorcore.pushValue();
47328             
47329             this.el.addClass('x-hidden');
47330             this.el.dom.setAttribute('tabIndex', -1);
47331             
47332             for (var i = 0; i < this.toolbars.length; i++) {
47333                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
47334                     this.toolbars[i].tb.show();
47335                     this.toolbars[i].footer.show();
47336                 }
47337             }
47338             
47339             //this.deferFocus();
47340         }
47341         
47342         this.setSize(this.wrap.getSize());
47343         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
47344         
47345         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
47346     },
47347  
47348     // private (for BoxComponent)
47349     adjustSize : Roo.BoxComponent.prototype.adjustSize,
47350
47351     // private (for BoxComponent)
47352     getResizeEl : function(){
47353         return this.wrap;
47354     },
47355
47356     // private (for BoxComponent)
47357     getPositionEl : function(){
47358         return this.wrap;
47359     },
47360
47361     // private
47362     initEvents : function(){
47363         this.originalValue = this.getValue();
47364     },
47365
47366     /**
47367      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
47368      * @method
47369      */
47370     markInvalid : Roo.emptyFn,
47371     /**
47372      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
47373      * @method
47374      */
47375     clearInvalid : Roo.emptyFn,
47376
47377     setValue : function(v){
47378         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
47379         this.editorcore.pushValue();
47380     },
47381
47382      
47383     // private
47384     deferFocus : function(){
47385         this.focus.defer(10, this);
47386     },
47387
47388     // doc'ed in Field
47389     focus : function(){
47390         this.editorcore.focus();
47391         
47392     },
47393       
47394
47395     // private
47396     onDestroy : function(){
47397         
47398         
47399         
47400         if(this.rendered){
47401             
47402             for (var i =0; i < this.toolbars.length;i++) {
47403                 // fixme - ask toolbars for heights?
47404                 this.toolbars[i].onDestroy();
47405             }
47406             
47407             this.wrap.dom.innerHTML = '';
47408             this.wrap.remove();
47409         }
47410     },
47411
47412     // private
47413     onFirstFocus : function(){
47414         //Roo.log("onFirstFocus");
47415         this.editorcore.onFirstFocus();
47416          for (var i =0; i < this.toolbars.length;i++) {
47417             this.toolbars[i].onFirstFocus();
47418         }
47419         
47420     },
47421     
47422     // private
47423     syncValue : function()
47424     {
47425         this.editorcore.syncValue();
47426     },
47427     
47428     pushValue : function()
47429     {
47430         this.editorcore.pushValue();
47431     },
47432     
47433     setStylesheets : function(stylesheets)
47434     {
47435         this.editorcore.setStylesheets(stylesheets);
47436     },
47437     
47438     removeStylesheets : function()
47439     {
47440         this.editorcore.removeStylesheets();
47441     }
47442      
47443     
47444     // hide stuff that is not compatible
47445     /**
47446      * @event blur
47447      * @hide
47448      */
47449     /**
47450      * @event change
47451      * @hide
47452      */
47453     /**
47454      * @event focus
47455      * @hide
47456      */
47457     /**
47458      * @event specialkey
47459      * @hide
47460      */
47461     /**
47462      * @cfg {String} fieldClass @hide
47463      */
47464     /**
47465      * @cfg {String} focusClass @hide
47466      */
47467     /**
47468      * @cfg {String} autoCreate @hide
47469      */
47470     /**
47471      * @cfg {String} inputType @hide
47472      */
47473     /**
47474      * @cfg {String} invalidClass @hide
47475      */
47476     /**
47477      * @cfg {String} invalidText @hide
47478      */
47479     /**
47480      * @cfg {String} msgFx @hide
47481      */
47482     /**
47483      * @cfg {String} validateOnBlur @hide
47484      */
47485 });
47486  
47487     // <script type="text/javascript">
47488 /*
47489  * Based on
47490  * Ext JS Library 1.1.1
47491  * Copyright(c) 2006-2007, Ext JS, LLC.
47492  *  
47493  
47494  */
47495
47496 /**
47497  * @class Roo.form.HtmlEditorToolbar1
47498  * Basic Toolbar
47499  * 
47500  * Usage:
47501  *
47502  new Roo.form.HtmlEditor({
47503     ....
47504     toolbars : [
47505         new Roo.form.HtmlEditorToolbar1({
47506             disable : { fonts: 1 , format: 1, ..., ... , ...],
47507             btns : [ .... ]
47508         })
47509     }
47510      
47511  * 
47512  * @cfg {Object} disable List of elements to disable..
47513  * @cfg {Array} btns List of additional buttons.
47514  * 
47515  * 
47516  * NEEDS Extra CSS? 
47517  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
47518  */
47519  
47520 Roo.form.HtmlEditor.ToolbarStandard = function(config)
47521 {
47522     
47523     Roo.apply(this, config);
47524     
47525     // default disabled, based on 'good practice'..
47526     this.disable = this.disable || {};
47527     Roo.applyIf(this.disable, {
47528         fontSize : true,
47529         colors : true,
47530         specialElements : true
47531     });
47532     
47533     
47534     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
47535     // dont call parent... till later.
47536 }
47537
47538 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
47539     
47540     tb: false,
47541     
47542     rendered: false,
47543     
47544     editor : false,
47545     editorcore : false,
47546     /**
47547      * @cfg {Object} disable  List of toolbar elements to disable
47548          
47549      */
47550     disable : false,
47551     
47552     
47553      /**
47554      * @cfg {String} createLinkText The default text for the create link prompt
47555      */
47556     createLinkText : 'Please enter the URL for the link:',
47557     /**
47558      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
47559      */
47560     defaultLinkValue : 'http:/'+'/',
47561    
47562     
47563       /**
47564      * @cfg {Array} fontFamilies An array of available font families
47565      */
47566     fontFamilies : [
47567         'Arial',
47568         'Courier New',
47569         'Tahoma',
47570         'Times New Roman',
47571         'Verdana'
47572     ],
47573     
47574     specialChars : [
47575            "&#169;",
47576           "&#174;",     
47577           "&#8482;",    
47578           "&#163;" ,    
47579          // "&#8212;",    
47580           "&#8230;",    
47581           "&#247;" ,    
47582         //  "&#225;" ,     ?? a acute?
47583            "&#8364;"    , //Euro
47584        //   "&#8220;"    ,
47585         //  "&#8221;"    ,
47586         //  "&#8226;"    ,
47587           "&#176;"  //   , // degrees
47588
47589          // "&#233;"     , // e ecute
47590          // "&#250;"     , // u ecute?
47591     ],
47592     
47593     specialElements : [
47594         {
47595             text: "Insert Table",
47596             xtype: 'MenuItem',
47597             xns : Roo.Menu,
47598             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
47599                 
47600         },
47601         {    
47602             text: "Insert Image",
47603             xtype: 'MenuItem',
47604             xns : Roo.Menu,
47605             ihtml : '<img src="about:blank"/>'
47606             
47607         }
47608         
47609          
47610     ],
47611     
47612     
47613     inputElements : [ 
47614             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
47615             "input:submit", "input:button", "select", "textarea", "label" ],
47616     formats : [
47617         ["p"] ,  
47618         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
47619         ["pre"],[ "code"], 
47620         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
47621         ['div'],['span'],
47622         ['sup'],['sub']
47623     ],
47624     
47625     cleanStyles : [
47626         "font-size"
47627     ],
47628      /**
47629      * @cfg {String} defaultFont default font to use.
47630      */
47631     defaultFont: 'tahoma',
47632    
47633     fontSelect : false,
47634     
47635     
47636     formatCombo : false,
47637     
47638     init : function(editor)
47639     {
47640         this.editor = editor;
47641         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47642         var editorcore = this.editorcore;
47643         
47644         var _t = this;
47645         
47646         var fid = editorcore.frameId;
47647         var etb = this;
47648         function btn(id, toggle, handler){
47649             var xid = fid + '-'+ id ;
47650             return {
47651                 id : xid,
47652                 cmd : id,
47653                 cls : 'x-btn-icon x-edit-'+id,
47654                 enableToggle:toggle !== false,
47655                 scope: _t, // was editor...
47656                 handler:handler||_t.relayBtnCmd,
47657                 clickEvent:'mousedown',
47658                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47659                 tabIndex:-1
47660             };
47661         }
47662         
47663         
47664         
47665         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47666         this.tb = tb;
47667          // stop form submits
47668         tb.el.on('click', function(e){
47669             e.preventDefault(); // what does this do?
47670         });
47671
47672         if(!this.disable.font) { // && !Roo.isSafari){
47673             /* why no safari for fonts 
47674             editor.fontSelect = tb.el.createChild({
47675                 tag:'select',
47676                 tabIndex: -1,
47677                 cls:'x-font-select',
47678                 html: this.createFontOptions()
47679             });
47680             
47681             editor.fontSelect.on('change', function(){
47682                 var font = editor.fontSelect.dom.value;
47683                 editor.relayCmd('fontname', font);
47684                 editor.deferFocus();
47685             }, editor);
47686             
47687             tb.add(
47688                 editor.fontSelect.dom,
47689                 '-'
47690             );
47691             */
47692             
47693         };
47694         if(!this.disable.formats){
47695             this.formatCombo = new Roo.form.ComboBox({
47696                 store: new Roo.data.SimpleStore({
47697                     id : 'tag',
47698                     fields: ['tag'],
47699                     data : this.formats // from states.js
47700                 }),
47701                 blockFocus : true,
47702                 name : '',
47703                 //autoCreate : {tag: "div",  size: "20"},
47704                 displayField:'tag',
47705                 typeAhead: false,
47706                 mode: 'local',
47707                 editable : false,
47708                 triggerAction: 'all',
47709                 emptyText:'Add tag',
47710                 selectOnFocus:true,
47711                 width:135,
47712                 listeners : {
47713                     'select': function(c, r, i) {
47714                         editorcore.insertTag(r.get('tag'));
47715                         editor.focus();
47716                     }
47717                 }
47718
47719             });
47720             tb.addField(this.formatCombo);
47721             
47722         }
47723         
47724         if(!this.disable.format){
47725             tb.add(
47726                 btn('bold'),
47727                 btn('italic'),
47728                 btn('underline'),
47729                 btn('strikethrough')
47730             );
47731         };
47732         if(!this.disable.fontSize){
47733             tb.add(
47734                 '-',
47735                 
47736                 
47737                 btn('increasefontsize', false, editorcore.adjustFont),
47738                 btn('decreasefontsize', false, editorcore.adjustFont)
47739             );
47740         };
47741         
47742         
47743         if(!this.disable.colors){
47744             tb.add(
47745                 '-', {
47746                     id:editorcore.frameId +'-forecolor',
47747                     cls:'x-btn-icon x-edit-forecolor',
47748                     clickEvent:'mousedown',
47749                     tooltip: this.buttonTips['forecolor'] || undefined,
47750                     tabIndex:-1,
47751                     menu : new Roo.menu.ColorMenu({
47752                         allowReselect: true,
47753                         focus: Roo.emptyFn,
47754                         value:'000000',
47755                         plain:true,
47756                         selectHandler: function(cp, color){
47757                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
47758                             editor.deferFocus();
47759                         },
47760                         scope: editorcore,
47761                         clickEvent:'mousedown'
47762                     })
47763                 }, {
47764                     id:editorcore.frameId +'backcolor',
47765                     cls:'x-btn-icon x-edit-backcolor',
47766                     clickEvent:'mousedown',
47767                     tooltip: this.buttonTips['backcolor'] || undefined,
47768                     tabIndex:-1,
47769                     menu : new Roo.menu.ColorMenu({
47770                         focus: Roo.emptyFn,
47771                         value:'FFFFFF',
47772                         plain:true,
47773                         allowReselect: true,
47774                         selectHandler: function(cp, color){
47775                             if(Roo.isGecko){
47776                                 editorcore.execCmd('useCSS', false);
47777                                 editorcore.execCmd('hilitecolor', color);
47778                                 editorcore.execCmd('useCSS', true);
47779                                 editor.deferFocus();
47780                             }else{
47781                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
47782                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
47783                                 editor.deferFocus();
47784                             }
47785                         },
47786                         scope:editorcore,
47787                         clickEvent:'mousedown'
47788                     })
47789                 }
47790             );
47791         };
47792         // now add all the items...
47793         
47794
47795         if(!this.disable.alignments){
47796             tb.add(
47797                 '-',
47798                 btn('justifyleft'),
47799                 btn('justifycenter'),
47800                 btn('justifyright')
47801             );
47802         };
47803
47804         //if(!Roo.isSafari){
47805             if(!this.disable.links){
47806                 tb.add(
47807                     '-',
47808                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
47809                 );
47810             };
47811
47812             if(!this.disable.lists){
47813                 tb.add(
47814                     '-',
47815                     btn('insertorderedlist'),
47816                     btn('insertunorderedlist')
47817                 );
47818             }
47819             if(!this.disable.sourceEdit){
47820                 tb.add(
47821                     '-',
47822                     btn('sourceedit', true, function(btn){
47823                         this.toggleSourceEdit(btn.pressed);
47824                     })
47825                 );
47826             }
47827         //}
47828         
47829         var smenu = { };
47830         // special menu.. - needs to be tidied up..
47831         if (!this.disable.special) {
47832             smenu = {
47833                 text: "&#169;",
47834                 cls: 'x-edit-none',
47835                 
47836                 menu : {
47837                     items : []
47838                 }
47839             };
47840             for (var i =0; i < this.specialChars.length; i++) {
47841                 smenu.menu.items.push({
47842                     
47843                     html: this.specialChars[i],
47844                     handler: function(a,b) {
47845                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
47846                         //editor.insertAtCursor(a.html);
47847                         
47848                     },
47849                     tabIndex:-1
47850                 });
47851             }
47852             
47853             
47854             tb.add(smenu);
47855             
47856             
47857         }
47858         
47859         var cmenu = { };
47860         if (!this.disable.cleanStyles) {
47861             cmenu = {
47862                 cls: 'x-btn-icon x-btn-clear',
47863                 
47864                 menu : {
47865                     items : []
47866                 }
47867             };
47868             for (var i =0; i < this.cleanStyles.length; i++) {
47869                 cmenu.menu.items.push({
47870                     actiontype : this.cleanStyles[i],
47871                     html: 'Remove ' + this.cleanStyles[i],
47872                     handler: function(a,b) {
47873 //                        Roo.log(a);
47874 //                        Roo.log(b);
47875                         var c = Roo.get(editorcore.doc.body);
47876                         c.select('[style]').each(function(s) {
47877                             s.dom.style.removeProperty(a.actiontype);
47878                         });
47879                         editorcore.syncValue();
47880                     },
47881                     tabIndex:-1
47882                 });
47883             }
47884             cmenu.menu.items.push({
47885                 actiontype : 'tablewidths',
47886                 html: 'Remove Table Widths',
47887                 handler: function(a,b) {
47888                     editorcore.cleanTableWidths();
47889                     editorcore.syncValue();
47890                 },
47891                 tabIndex:-1
47892             });
47893             cmenu.menu.items.push({
47894                 actiontype : 'word',
47895                 html: 'Remove MS Word Formating',
47896                 handler: function(a,b) {
47897                     editorcore.cleanWord();
47898                     editorcore.syncValue();
47899                 },
47900                 tabIndex:-1
47901             });
47902             
47903             cmenu.menu.items.push({
47904                 actiontype : 'all',
47905                 html: 'Remove All Styles',
47906                 handler: function(a,b) {
47907                     
47908                     var c = Roo.get(editorcore.doc.body);
47909                     c.select('[style]').each(function(s) {
47910                         s.dom.removeAttribute('style');
47911                     });
47912                     editorcore.syncValue();
47913                 },
47914                 tabIndex:-1
47915             });
47916             
47917             cmenu.menu.items.push({
47918                 actiontype : 'all',
47919                 html: 'Remove All CSS Classes',
47920                 handler: function(a,b) {
47921                     
47922                     var c = Roo.get(editorcore.doc.body);
47923                     c.select('[class]').each(function(s) {
47924                         s.dom.removeAttribute('class');
47925                     });
47926                     editorcore.cleanWord();
47927                     editorcore.syncValue();
47928                 },
47929                 tabIndex:-1
47930             });
47931             
47932              cmenu.menu.items.push({
47933                 actiontype : 'tidy',
47934                 html: 'Tidy HTML Source',
47935                 handler: function(a,b) {
47936                     new Roo.htmleditor.Tidy(editorcore.doc.body);
47937                     editorcore.syncValue();
47938                 },
47939                 tabIndex:-1
47940             });
47941             
47942             
47943             tb.add(cmenu);
47944         }
47945          
47946         if (!this.disable.specialElements) {
47947             var semenu = {
47948                 text: "Other;",
47949                 cls: 'x-edit-none',
47950                 menu : {
47951                     items : []
47952                 }
47953             };
47954             for (var i =0; i < this.specialElements.length; i++) {
47955                 semenu.menu.items.push(
47956                     Roo.apply({ 
47957                         handler: function(a,b) {
47958                             editor.insertAtCursor(this.ihtml);
47959                         }
47960                     }, this.specialElements[i])
47961                 );
47962                     
47963             }
47964             
47965             tb.add(semenu);
47966             
47967             
47968         }
47969          
47970         
47971         if (this.btns) {
47972             for(var i =0; i< this.btns.length;i++) {
47973                 var b = Roo.factory(this.btns[i],Roo.form);
47974                 b.cls =  'x-edit-none';
47975                 
47976                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
47977                     b.cls += ' x-init-enable';
47978                 }
47979                 
47980                 b.scope = editorcore;
47981                 tb.add(b);
47982             }
47983         
47984         }
47985         
47986         
47987         
47988         // disable everything...
47989         
47990         this.tb.items.each(function(item){
47991             
47992            if(
47993                 item.id != editorcore.frameId+ '-sourceedit' && 
47994                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
47995             ){
47996                 
47997                 item.disable();
47998             }
47999         });
48000         this.rendered = true;
48001         
48002         // the all the btns;
48003         editor.on('editorevent', this.updateToolbar, this);
48004         // other toolbars need to implement this..
48005         //editor.on('editmodechange', this.updateToolbar, this);
48006     },
48007     
48008     
48009     relayBtnCmd : function(btn) {
48010         this.editorcore.relayCmd(btn.cmd);
48011     },
48012     // private used internally
48013     createLink : function(){
48014         Roo.log("create link?");
48015         var url = prompt(this.createLinkText, this.defaultLinkValue);
48016         if(url && url != 'http:/'+'/'){
48017             this.editorcore.relayCmd('createlink', url);
48018         }
48019     },
48020
48021     
48022     /**
48023      * Protected method that will not generally be called directly. It triggers
48024      * a toolbar update by reading the markup state of the current selection in the editor.
48025      */
48026     updateToolbar: function(){
48027
48028         if(!this.editorcore.activated){
48029             this.editor.onFirstFocus();
48030             return;
48031         }
48032
48033         var btns = this.tb.items.map, 
48034             doc = this.editorcore.doc,
48035             frameId = this.editorcore.frameId;
48036
48037         if(!this.disable.font && !Roo.isSafari){
48038             /*
48039             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
48040             if(name != this.fontSelect.dom.value){
48041                 this.fontSelect.dom.value = name;
48042             }
48043             */
48044         }
48045         if(!this.disable.format){
48046             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
48047             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
48048             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
48049             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
48050         }
48051         if(!this.disable.alignments){
48052             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
48053             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
48054             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
48055         }
48056         if(!Roo.isSafari && !this.disable.lists){
48057             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
48058             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
48059         }
48060         
48061         var ans = this.editorcore.getAllAncestors();
48062         if (this.formatCombo) {
48063             
48064             
48065             var store = this.formatCombo.store;
48066             this.formatCombo.setValue("");
48067             for (var i =0; i < ans.length;i++) {
48068                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
48069                     // select it..
48070                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
48071                     break;
48072                 }
48073             }
48074         }
48075         
48076         
48077         
48078         // hides menus... - so this cant be on a menu...
48079         Roo.menu.MenuMgr.hideAll();
48080
48081         //this.editorsyncValue();
48082     },
48083    
48084     
48085     createFontOptions : function(){
48086         var buf = [], fs = this.fontFamilies, ff, lc;
48087         
48088         
48089         
48090         for(var i = 0, len = fs.length; i< len; i++){
48091             ff = fs[i];
48092             lc = ff.toLowerCase();
48093             buf.push(
48094                 '<option value="',lc,'" style="font-family:',ff,';"',
48095                     (this.defaultFont == lc ? ' selected="true">' : '>'),
48096                     ff,
48097                 '</option>'
48098             );
48099         }
48100         return buf.join('');
48101     },
48102     
48103     toggleSourceEdit : function(sourceEditMode){
48104         
48105         Roo.log("toolbar toogle");
48106         if(sourceEditMode === undefined){
48107             sourceEditMode = !this.sourceEditMode;
48108         }
48109         this.sourceEditMode = sourceEditMode === true;
48110         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
48111         // just toggle the button?
48112         if(btn.pressed !== this.sourceEditMode){
48113             btn.toggle(this.sourceEditMode);
48114             return;
48115         }
48116         
48117         if(sourceEditMode){
48118             Roo.log("disabling buttons");
48119             this.tb.items.each(function(item){
48120                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
48121                     item.disable();
48122                 }
48123             });
48124           
48125         }else{
48126             Roo.log("enabling buttons");
48127             if(this.editorcore.initialized){
48128                 this.tb.items.each(function(item){
48129                     item.enable();
48130                 });
48131             }
48132             
48133         }
48134         Roo.log("calling toggole on editor");
48135         // tell the editor that it's been pressed..
48136         this.editor.toggleSourceEdit(sourceEditMode);
48137        
48138     },
48139      /**
48140      * Object collection of toolbar tooltips for the buttons in the editor. The key
48141      * is the command id associated with that button and the value is a valid QuickTips object.
48142      * For example:
48143 <pre><code>
48144 {
48145     bold : {
48146         title: 'Bold (Ctrl+B)',
48147         text: 'Make the selected text bold.',
48148         cls: 'x-html-editor-tip'
48149     },
48150     italic : {
48151         title: 'Italic (Ctrl+I)',
48152         text: 'Make the selected text italic.',
48153         cls: 'x-html-editor-tip'
48154     },
48155     ...
48156 </code></pre>
48157     * @type Object
48158      */
48159     buttonTips : {
48160         bold : {
48161             title: 'Bold (Ctrl+B)',
48162             text: 'Make the selected text bold.',
48163             cls: 'x-html-editor-tip'
48164         },
48165         italic : {
48166             title: 'Italic (Ctrl+I)',
48167             text: 'Make the selected text italic.',
48168             cls: 'x-html-editor-tip'
48169         },
48170         underline : {
48171             title: 'Underline (Ctrl+U)',
48172             text: 'Underline the selected text.',
48173             cls: 'x-html-editor-tip'
48174         },
48175         strikethrough : {
48176             title: 'Strikethrough',
48177             text: 'Strikethrough the selected text.',
48178             cls: 'x-html-editor-tip'
48179         },
48180         increasefontsize : {
48181             title: 'Grow Text',
48182             text: 'Increase the font size.',
48183             cls: 'x-html-editor-tip'
48184         },
48185         decreasefontsize : {
48186             title: 'Shrink Text',
48187             text: 'Decrease the font size.',
48188             cls: 'x-html-editor-tip'
48189         },
48190         backcolor : {
48191             title: 'Text Highlight Color',
48192             text: 'Change the background color of the selected text.',
48193             cls: 'x-html-editor-tip'
48194         },
48195         forecolor : {
48196             title: 'Font Color',
48197             text: 'Change the color of the selected text.',
48198             cls: 'x-html-editor-tip'
48199         },
48200         justifyleft : {
48201             title: 'Align Text Left',
48202             text: 'Align text to the left.',
48203             cls: 'x-html-editor-tip'
48204         },
48205         justifycenter : {
48206             title: 'Center Text',
48207             text: 'Center text in the editor.',
48208             cls: 'x-html-editor-tip'
48209         },
48210         justifyright : {
48211             title: 'Align Text Right',
48212             text: 'Align text to the right.',
48213             cls: 'x-html-editor-tip'
48214         },
48215         insertunorderedlist : {
48216             title: 'Bullet List',
48217             text: 'Start a bulleted list.',
48218             cls: 'x-html-editor-tip'
48219         },
48220         insertorderedlist : {
48221             title: 'Numbered List',
48222             text: 'Start a numbered list.',
48223             cls: 'x-html-editor-tip'
48224         },
48225         createlink : {
48226             title: 'Hyperlink',
48227             text: 'Make the selected text a hyperlink.',
48228             cls: 'x-html-editor-tip'
48229         },
48230         sourceedit : {
48231             title: 'Source Edit',
48232             text: 'Switch to source editing mode.',
48233             cls: 'x-html-editor-tip'
48234         }
48235     },
48236     // private
48237     onDestroy : function(){
48238         if(this.rendered){
48239             
48240             this.tb.items.each(function(item){
48241                 if(item.menu){
48242                     item.menu.removeAll();
48243                     if(item.menu.el){
48244                         item.menu.el.destroy();
48245                     }
48246                 }
48247                 item.destroy();
48248             });
48249              
48250         }
48251     },
48252     onFirstFocus: function() {
48253         this.tb.items.each(function(item){
48254            item.enable();
48255         });
48256     }
48257 });
48258
48259
48260
48261
48262 // <script type="text/javascript">
48263 /*
48264  * Based on
48265  * Ext JS Library 1.1.1
48266  * Copyright(c) 2006-2007, Ext JS, LLC.
48267  *  
48268  
48269  */
48270
48271  
48272 /**
48273  * @class Roo.form.HtmlEditor.ToolbarContext
48274  * Context Toolbar
48275  * 
48276  * Usage:
48277  *
48278  new Roo.form.HtmlEditor({
48279     ....
48280     toolbars : [
48281         { xtype: 'ToolbarStandard', styles : {} }
48282         { xtype: 'ToolbarContext', disable : {} }
48283     ]
48284 })
48285
48286      
48287  * 
48288  * @config : {Object} disable List of elements to disable.. (not done yet.)
48289  * @config : {Object} styles  Map of styles available.
48290  * 
48291  */
48292
48293 Roo.form.HtmlEditor.ToolbarContext = function(config)
48294 {
48295     
48296     Roo.apply(this, config);
48297     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48298     // dont call parent... till later.
48299     this.styles = this.styles || {};
48300 }
48301
48302  
48303
48304 Roo.form.HtmlEditor.ToolbarContext.types = {
48305     'IMG' : {
48306         width : {
48307             title: "Width",
48308             width: 40
48309         },
48310         height:  {
48311             title: "Height",
48312             width: 40
48313         },
48314         align: {
48315             title: "Align",
48316             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
48317             width : 80
48318             
48319         },
48320         border: {
48321             title: "Border",
48322             width: 40
48323         },
48324         alt: {
48325             title: "Alt",
48326             width: 120
48327         },
48328         src : {
48329             title: "Src",
48330             width: 220
48331         }
48332         
48333     },
48334     
48335     'FIGURE' : {
48336          align: {
48337             title: "Align",
48338             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
48339             width : 80  
48340         }
48341     },
48342     'A' : {
48343         name : {
48344             title: "Name",
48345             width: 50
48346         },
48347         target:  {
48348             title: "Target",
48349             width: 120
48350         },
48351         href:  {
48352             title: "Href",
48353             width: 220
48354         } // border?
48355         
48356     },
48357     'TABLE' : {
48358         rows : {
48359             title: "Rows",
48360             width: 20
48361         },
48362         cols : {
48363             title: "Cols",
48364             width: 20
48365         },
48366         width : {
48367             title: "Width",
48368             width: 40
48369         },
48370         height : {
48371             title: "Height",
48372             width: 40
48373         },
48374         border : {
48375             title: "Border",
48376             width: 20
48377         }
48378     },
48379     'TD' : {
48380         width : {
48381             title: "Width",
48382             width: 40
48383         },
48384         height : {
48385             title: "Height",
48386             width: 40
48387         },   
48388         align: {
48389             title: "Align",
48390             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
48391             width: 80
48392         },
48393         valign: {
48394             title: "Valign",
48395             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
48396             width: 80
48397         },
48398         colspan: {
48399             title: "Colspan",
48400             width: 20
48401             
48402         },
48403          'font-family'  : {
48404             title : "Font",
48405             style : 'fontFamily',
48406             displayField: 'display',
48407             optname : 'font-family',
48408             width: 140
48409         }
48410     },
48411     'INPUT' : {
48412         name : {
48413             title: "name",
48414             width: 120
48415         },
48416         value : {
48417             title: "Value",
48418             width: 120
48419         },
48420         width : {
48421             title: "Width",
48422             width: 40
48423         }
48424     },
48425     'LABEL' : {
48426         'for' : {
48427             title: "For",
48428             width: 120
48429         }
48430     },
48431     'TEXTAREA' : {
48432         name : {
48433             title: "name",
48434             width: 120
48435         },
48436         rows : {
48437             title: "Rows",
48438             width: 20
48439         },
48440         cols : {
48441             title: "Cols",
48442             width: 20
48443         }
48444     },
48445     'SELECT' : {
48446         name : {
48447             title: "name",
48448             width: 120
48449         },
48450         selectoptions : {
48451             title: "Options",
48452             width: 200
48453         }
48454     },
48455     
48456     // should we really allow this??
48457     // should this just be 
48458     'BODY' : {
48459         title : {
48460             title: "Title",
48461             width: 200,
48462             disabled : true
48463         }
48464     },
48465     /*
48466     'SPAN' : {
48467         'font-family'  : {
48468             title : "Font",
48469             style : 'fontFamily',
48470             displayField: 'display',
48471             optname : 'font-family',
48472             width: 140
48473         }
48474     },
48475     'DIV' : {
48476         'font-family'  : {
48477             title : "Font",
48478             style : 'fontFamily',
48479             displayField: 'display',
48480             optname : 'font-family',
48481             width: 140
48482         }
48483     },
48484      'P' : {
48485         'font-family'  : {
48486             title : "Font",
48487             style : 'fontFamily',
48488             displayField: 'display',
48489             optname : 'font-family',
48490             width: 140
48491         }
48492     },
48493     */
48494     '*' : {
48495         // empty..
48496     }
48497
48498 };
48499
48500 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
48501 Roo.form.HtmlEditor.ToolbarContext.stores = false;
48502
48503 Roo.form.HtmlEditor.ToolbarContext.options = {
48504         'font-family'  : [ 
48505                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
48506                 [ 'Courier New', 'Courier New'],
48507                 [ 'Tahoma', 'Tahoma'],
48508                 [ 'Times New Roman,serif', 'Times'],
48509                 [ 'Verdana','Verdana' ]
48510         ]
48511 };
48512
48513 // fixme - these need to be configurable..
48514  
48515
48516 //Roo.form.HtmlEditor.ToolbarContext.types
48517
48518
48519 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
48520     
48521     tb: false,
48522     
48523     rendered: false,
48524     
48525     editor : false,
48526     editorcore : false,
48527     /**
48528      * @cfg {Object} disable  List of toolbar elements to disable
48529          
48530      */
48531     disable : false,
48532     /**
48533      * @cfg {Object} styles List of styles 
48534      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
48535      *
48536      * These must be defined in the page, so they get rendered correctly..
48537      * .headline { }
48538      * TD.underline { }
48539      * 
48540      */
48541     styles : false,
48542     
48543     options: false,
48544     
48545     toolbars : false,
48546     
48547     init : function(editor)
48548     {
48549         this.editor = editor;
48550         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48551         var editorcore = this.editorcore;
48552         
48553         var fid = editorcore.frameId;
48554         var etb = this;
48555         function btn(id, toggle, handler){
48556             var xid = fid + '-'+ id ;
48557             return {
48558                 id : xid,
48559                 cmd : id,
48560                 cls : 'x-btn-icon x-edit-'+id,
48561                 enableToggle:toggle !== false,
48562                 scope: editorcore, // was editor...
48563                 handler:handler||editorcore.relayBtnCmd,
48564                 clickEvent:'mousedown',
48565                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48566                 tabIndex:-1
48567             };
48568         }
48569         // create a new element.
48570         var wdiv = editor.wrap.createChild({
48571                 tag: 'div'
48572             }, editor.wrap.dom.firstChild.nextSibling, true);
48573         
48574         // can we do this more than once??
48575         
48576          // stop form submits
48577       
48578  
48579         // disable everything...
48580         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
48581         this.toolbars = {};
48582            
48583         for (var i in  ty) {
48584           
48585             this.toolbars[i] = this.buildToolbar(ty[i],i);
48586         }
48587         this.tb = this.toolbars.BODY;
48588         this.tb.el.show();
48589         this.buildFooter();
48590         this.footer.show();
48591         editor.on('hide', function( ) { this.footer.hide() }, this);
48592         editor.on('show', function( ) { this.footer.show() }, this);
48593         
48594          
48595         this.rendered = true;
48596         
48597         // the all the btns;
48598         editor.on('editorevent', this.updateToolbar, this);
48599         // other toolbars need to implement this..
48600         //editor.on('editmodechange', this.updateToolbar, this);
48601     },
48602     
48603     
48604     
48605     /**
48606      * Protected method that will not generally be called directly. It triggers
48607      * a toolbar update by reading the markup state of the current selection in the editor.
48608      *
48609      * Note you can force an update by calling on('editorevent', scope, false)
48610      */
48611     updateToolbar: function(editor ,ev, sel){
48612
48613         //Roo.log(ev);
48614         // capture mouse up - this is handy for selecting images..
48615         // perhaps should go somewhere else...
48616         if(!this.editorcore.activated){
48617              this.editor.onFirstFocus();
48618             return;
48619         }
48620         
48621         
48622         
48623         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
48624         // selectNode - might want to handle IE?
48625         
48626         
48627         
48628         if (ev &&
48629             (ev.type == 'mouseup' || ev.type == 'click' ) &&
48630             ev.target && ev.target.tagName == 'IMG') {
48631             // they have click on an image...
48632             // let's see if we can change the selection...
48633             sel = ev.target;
48634          
48635             var nodeRange = sel.ownerDocument.createRange();
48636             try {
48637                 nodeRange.selectNode(sel);
48638             } catch (e) {
48639                 nodeRange.selectNodeContents(sel);
48640             }
48641             //nodeRange.collapse(true);
48642             var s = this.editorcore.win.getSelection();
48643             s.removeAllRanges();
48644             s.addRange(nodeRange);
48645         }  
48646         
48647       
48648         //var updateFooter = sel ? false : true; 
48649         
48650         
48651         var ans = this.editorcore.getAllAncestors();
48652         
48653         // pick
48654         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
48655         
48656         if (!sel) { 
48657             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
48658             sel = sel ? sel : this.editorcore.doc.body;
48659             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
48660             
48661         }
48662         
48663         var tn = sel.tagName.toUpperCase();
48664         var lastSel = this.tb.selectedNode;
48665         this.tb.selectedNode = sel;
48666         var left_label = tn;
48667         
48668         // ok see if we are editing a block?
48669         
48670         var db = Roo.get(sel).findParent('[data-block]');
48671         var block = false;
48672         if (db) {
48673             block = Roo.htmleditor.Block.factory(db);
48674             if (block) {
48675                 tn = 'BLOCK.' + db.getAttribute('data-block');
48676                 this.tb.selectedNode = db;
48677                 if (typeof(this.toolbars[tn]) == 'undefined') {
48678                    this.toolbars[tn] = this.buildToolbar( block.context,tn ,block.friendly_name);
48679                 }
48680                 left_label = block.friendly_name;
48681             }
48682             
48683                 
48684             
48685         }
48686         
48687         
48688         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
48689             return; // no change?
48690         }
48691         
48692         
48693           
48694         this.tb.el.hide();
48695         ///console.log("show: " + tn);
48696         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
48697         
48698         this.tb.el.show();
48699         // update name
48700         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
48701         
48702         
48703         // update attributes
48704         if (block) {
48705              
48706             this.tb.fields.each(function(e) {
48707                 e.setValue(block[e.attrname]);
48708             });
48709             
48710             
48711         } else  if (this.tb.fields) {
48712             this.tb.fields.each(function(e) {
48713                 if (e.stylename) {
48714                     e.setValue(sel.style[e.stylename]);
48715                     return;
48716                 } 
48717                 e.setValue(sel.getAttribute(e.attrname));
48718             });
48719         }
48720         this.updateToolbarStyles(sel);  
48721        
48722          
48723        
48724         Roo.menu.MenuMgr.hideAll();
48725
48726         
48727         
48728     
48729         // update the footer
48730         //
48731         this.updateFooter(ans);
48732              
48733     },
48734     
48735     updateToolbarStyles : function(sel)
48736     {
48737          var hasStyles = false;
48738         for(var i in this.styles) {
48739             hasStyles = true;
48740             break;
48741         }
48742         
48743         // update styles
48744         if (hasStyles) { 
48745             var st = this.tb.fields.item(0);
48746             
48747             st.store.removeAll();
48748             var cn = sel.className.split(/\s+/);
48749             
48750             var avs = [];
48751             if (this.styles['*']) {
48752                 
48753                 Roo.each(this.styles['*'], function(v) {
48754                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48755                 });
48756             }
48757             if (this.styles[tn]) { 
48758                 Roo.each(this.styles[tn], function(v) {
48759                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48760                 });
48761             }
48762             
48763             st.store.loadData(avs);
48764             st.collapse();
48765             st.setValue(cn);
48766         }
48767     },
48768     
48769      
48770     updateFooter : function(ans)
48771     {
48772         var html = '';
48773         if (ans === false) {
48774             this.footDisp.dom.innerHTML = '';
48775             return;
48776         }
48777         
48778         this.footerEls = ans.reverse();
48779         Roo.each(this.footerEls, function(a,i) {
48780             if (!a) { return; }
48781             html += html.length ? ' &gt; '  :  '';
48782             
48783             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
48784             
48785         });
48786        
48787         // 
48788         var sz = this.footDisp.up('td').getSize();
48789         this.footDisp.dom.style.width = (sz.width -10) + 'px';
48790         this.footDisp.dom.style.marginLeft = '5px';
48791         
48792         this.footDisp.dom.style.overflow = 'hidden';
48793         
48794         this.footDisp.dom.innerHTML = html;
48795             
48796         
48797     },
48798    
48799        
48800     // private
48801     onDestroy : function(){
48802         if(this.rendered){
48803             
48804             this.tb.items.each(function(item){
48805                 if(item.menu){
48806                     item.menu.removeAll();
48807                     if(item.menu.el){
48808                         item.menu.el.destroy();
48809                     }
48810                 }
48811                 item.destroy();
48812             });
48813              
48814         }
48815     },
48816     onFirstFocus: function() {
48817         // need to do this for all the toolbars..
48818         this.tb.items.each(function(item){
48819            item.enable();
48820         });
48821     },
48822     buildToolbar: function(tlist, nm, friendly_name)
48823     {
48824         var editor = this.editor;
48825         var editorcore = this.editorcore;
48826          // create a new element.
48827         var wdiv = editor.wrap.createChild({
48828                 tag: 'div'
48829             }, editor.wrap.dom.firstChild.nextSibling, true);
48830         
48831        
48832         var tb = new Roo.Toolbar(wdiv);
48833         tb.name = nm;
48834         
48835         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
48836         
48837         var styles = Array.from(this.styles);
48838         
48839         
48840         // styles...
48841         if (styles && styles.length) {
48842             
48843             // this needs a multi-select checkbox...
48844             tb.addField( new Roo.form.ComboBox({
48845                 store: new Roo.data.SimpleStore({
48846                     id : 'val',
48847                     fields: ['val', 'selected'],
48848                     data : [] 
48849                 }),
48850                 name : '-roo-edit-className',
48851                 attrname : 'className',
48852                 displayField: 'val',
48853                 typeAhead: false,
48854                 mode: 'local',
48855                 editable : false,
48856                 triggerAction: 'all',
48857                 emptyText:'Select Style',
48858                 selectOnFocus:true,
48859                 width: 130,
48860                 listeners : {
48861                     'select': function(c, r, i) {
48862                         // initial support only for on class per el..
48863                         tb.selectedNode.className =  r ? r.get('val') : '';
48864                         editorcore.syncValue();
48865                     }
48866                 }
48867     
48868             }));
48869         }
48870         
48871         var tbc = Roo.form.HtmlEditor.ToolbarContext;
48872         
48873         
48874         for (var i in tlist) {
48875             
48876             var item = tlist[i];
48877             tb.add(item.title + ":&nbsp;");
48878             
48879             
48880             //optname == used so you can configure the options available..
48881             var opts = item.opts ? item.opts : false;
48882             if (item.optname) { // use the b
48883                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
48884            
48885             }
48886             
48887             if (opts) {
48888                 // opts == pulldown..
48889                 tb.addField( new Roo.form.ComboBox({
48890                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
48891                         id : 'val',
48892                         fields: ['val', 'display'],
48893                         data : opts  
48894                     }),
48895                     name : '-roo-edit-' + i,
48896                     
48897                     attrname : i,
48898                     stylename : item.style ? item.style : false,
48899                     
48900                     displayField: item.displayField ? item.displayField : 'val',
48901                     valueField :  'val',
48902                     typeAhead: false,
48903                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
48904                     editable : false,
48905                     triggerAction: 'all',
48906                     emptyText:'Select',
48907                     selectOnFocus:true,
48908                     width: item.width ? item.width  : 130,
48909                     listeners : {
48910                         'select': function(c, r, i) {
48911                             if (tb.selectedNode.hasAttribute('data-block')) {
48912                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
48913                                 b[c.attrname] = r.get('val');
48914                                 b.updateElement(tb.selectedNode);
48915                                 editorcore.syncValue();
48916                                 return;
48917                             }
48918                             
48919                             if (c.stylename) {
48920                                 tb.selectedNode.style[c.stylename] =  r.get('val');
48921                                 editorcore.syncValue();
48922                                 return;
48923                             }
48924                             if (r === false) {
48925                                 tb.selectedNode.removeAttribute(c.attrname);
48926                                 editorcore.syncValue();
48927                                 return;
48928                             }
48929                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
48930                             editorcore.syncValue();
48931                         }
48932                     }
48933
48934                 }));
48935                 continue;
48936                     
48937                  
48938                 /*
48939                 tb.addField( new Roo.form.TextField({
48940                     name: i,
48941                     width: 100,
48942                     //allowBlank:false,
48943                     value: ''
48944                 }));
48945                 continue;
48946                 */
48947             }
48948             tb.addField( new Roo.form.TextField({
48949                 name: '-roo-edit-' + i,
48950                 attrname : i,
48951                 
48952                 width: item.width,
48953                 //allowBlank:true,
48954                 value: '',
48955                 listeners: {
48956                     'change' : function(f, nv, ov) {
48957                         
48958                         if (tb.selectedNode.hasAttribute('data-block')) {
48959                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
48960                             b[f.attrname] = nv;
48961                             b.updateElement(tb.selectedNode);
48962                             editorcore.syncValue();
48963                             return;
48964                         }
48965                         
48966                         tb.selectedNode.setAttribute(f.attrname, nv);
48967                         editorcore.syncValue();
48968                     }
48969                 }
48970             }));
48971              
48972         }
48973         
48974         var _this = this;
48975         
48976         if(nm == 'BODY'){
48977             tb.addSeparator();
48978         
48979             tb.addButton( {
48980                 text: 'Stylesheets',
48981
48982                 listeners : {
48983                     click : function ()
48984                     {
48985                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
48986                     }
48987                 }
48988             });
48989         }
48990         
48991         tb.addFill();
48992         tb.addButton({
48993             text: 'Remove', // remove the tag, and puts the children outside...
48994     
48995             listeners : {
48996                 click : function ()
48997                 {
48998                     // remove
48999                     // undo does not work.
49000                     var sn = tb.selectedNode;
49001                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
49002                     if (sn.hasAttribute('data-block')) {
49003                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
49004                         sn.parentNode.removeChild(sn);
49005                         
49006                     } else {
49007                         // remove and keep parents.
49008                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
49009                         a.removeTag(sn);
49010                     }
49011                     
49012                     
49013                     var range = editorcore.createRange();
49014         
49015                     range.setStart(stn,0);
49016                     range.setEnd(stn,0); 
49017                     var selection = editorcore.getSelection();
49018                     selection.removeAllRanges();
49019                     selection.addRange(range);
49020                     
49021                     
49022                     //_this.updateToolbar(null, null, pn);
49023                     _this.updateToolbar(null, null, null);
49024                     _this.updateFooter(false);
49025                     
49026                 }
49027             }
49028             
49029                     
49030                 
49031             
49032         });
49033         
49034         
49035         tb.el.on('click', function(e){
49036             e.preventDefault(); // what does this do?
49037         });
49038         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
49039         tb.el.hide();
49040         
49041         // dont need to disable them... as they will get hidden
49042         return tb;
49043          
49044         
49045     },
49046     buildFooter : function()
49047     {
49048         
49049         var fel = this.editor.wrap.createChild();
49050         this.footer = new Roo.Toolbar(fel);
49051         // toolbar has scrolly on left / right?
49052         var footDisp= new Roo.Toolbar.Fill();
49053         var _t = this;
49054         this.footer.add(
49055             {
49056                 text : '&lt;',
49057                 xtype: 'Button',
49058                 handler : function() {
49059                     _t.footDisp.scrollTo('left',0,true)
49060                 }
49061             }
49062         );
49063         this.footer.add( footDisp );
49064         this.footer.add( 
49065             {
49066                 text : '&gt;',
49067                 xtype: 'Button',
49068                 handler : function() {
49069                     // no animation..
49070                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
49071                 }
49072             }
49073         );
49074         var fel = Roo.get(footDisp.el);
49075         fel.addClass('x-editor-context');
49076         this.footDispWrap = fel; 
49077         this.footDispWrap.overflow  = 'hidden';
49078         
49079         this.footDisp = fel.createChild();
49080         this.footDispWrap.on('click', this.onContextClick, this)
49081         
49082         
49083     },
49084     onContextClick : function (ev,dom)
49085     {
49086         ev.preventDefault();
49087         var  cn = dom.className;
49088         //Roo.log(cn);
49089         if (!cn.match(/x-ed-loc-/)) {
49090             return;
49091         }
49092         var n = cn.split('-').pop();
49093         var ans = this.footerEls;
49094         var sel = ans[n];
49095         
49096          // pick
49097         var range = this.editorcore.createRange();
49098         
49099         range.selectNodeContents(sel);
49100         //range.selectNode(sel);
49101         
49102         
49103         var selection = this.editorcore.getSelection();
49104         selection.removeAllRanges();
49105         selection.addRange(range);
49106         
49107         
49108         
49109         this.updateToolbar(null, null, sel);
49110         
49111         
49112     }
49113     
49114     
49115     
49116     
49117     
49118 });
49119
49120
49121
49122
49123
49124 /*
49125  * Based on:
49126  * Ext JS Library 1.1.1
49127  * Copyright(c) 2006-2007, Ext JS, LLC.
49128  *
49129  * Originally Released Under LGPL - original licence link has changed is not relivant.
49130  *
49131  * Fork - LGPL
49132  * <script type="text/javascript">
49133  */
49134  
49135 /**
49136  * @class Roo.form.BasicForm
49137  * @extends Roo.util.Observable
49138  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
49139  * @constructor
49140  * @param {String/HTMLElement/Roo.Element} el The form element or its id
49141  * @param {Object} config Configuration options
49142  */
49143 Roo.form.BasicForm = function(el, config){
49144     this.allItems = [];
49145     this.childForms = [];
49146     Roo.apply(this, config);
49147     /*
49148      * The Roo.form.Field items in this form.
49149      * @type MixedCollection
49150      */
49151      
49152      
49153     this.items = new Roo.util.MixedCollection(false, function(o){
49154         return o.id || (o.id = Roo.id());
49155     });
49156     this.addEvents({
49157         /**
49158          * @event beforeaction
49159          * Fires before any action is performed. Return false to cancel the action.
49160          * @param {Form} this
49161          * @param {Action} action The action to be performed
49162          */
49163         beforeaction: true,
49164         /**
49165          * @event actionfailed
49166          * Fires when an action fails.
49167          * @param {Form} this
49168          * @param {Action} action The action that failed
49169          */
49170         actionfailed : true,
49171         /**
49172          * @event actioncomplete
49173          * Fires when an action is completed.
49174          * @param {Form} this
49175          * @param {Action} action The action that completed
49176          */
49177         actioncomplete : true
49178     });
49179     if(el){
49180         this.initEl(el);
49181     }
49182     Roo.form.BasicForm.superclass.constructor.call(this);
49183     
49184     Roo.form.BasicForm.popover.apply();
49185 };
49186
49187 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
49188     /**
49189      * @cfg {String} method
49190      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
49191      */
49192     /**
49193      * @cfg {DataReader} reader
49194      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
49195      * This is optional as there is built-in support for processing JSON.
49196      */
49197     /**
49198      * @cfg {DataReader} errorReader
49199      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
49200      * This is completely optional as there is built-in support for processing JSON.
49201      */
49202     /**
49203      * @cfg {String} url
49204      * The URL to use for form actions if one isn't supplied in the action options.
49205      */
49206     /**
49207      * @cfg {Boolean} fileUpload
49208      * Set to true if this form is a file upload.
49209      */
49210      
49211     /**
49212      * @cfg {Object} baseParams
49213      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
49214      */
49215      /**
49216      
49217     /**
49218      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
49219      */
49220     timeout: 30,
49221
49222     // private
49223     activeAction : null,
49224
49225     /**
49226      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
49227      * or setValues() data instead of when the form was first created.
49228      */
49229     trackResetOnLoad : false,
49230     
49231     
49232     /**
49233      * childForms - used for multi-tab forms
49234      * @type {Array}
49235      */
49236     childForms : false,
49237     
49238     /**
49239      * allItems - full list of fields.
49240      * @type {Array}
49241      */
49242     allItems : false,
49243     
49244     /**
49245      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
49246      * element by passing it or its id or mask the form itself by passing in true.
49247      * @type Mixed
49248      */
49249     waitMsgTarget : false,
49250     
49251     /**
49252      * @type Boolean
49253      */
49254     disableMask : false,
49255     
49256     /**
49257      * @cfg {Boolean} errorMask (true|false) default false
49258      */
49259     errorMask : false,
49260     
49261     /**
49262      * @cfg {Number} maskOffset Default 100
49263      */
49264     maskOffset : 100,
49265
49266     // private
49267     initEl : function(el){
49268         this.el = Roo.get(el);
49269         this.id = this.el.id || Roo.id();
49270         this.el.on('submit', this.onSubmit, this);
49271         this.el.addClass('x-form');
49272     },
49273
49274     // private
49275     onSubmit : function(e){
49276         e.stopEvent();
49277     },
49278
49279     /**
49280      * Returns true if client-side validation on the form is successful.
49281      * @return Boolean
49282      */
49283     isValid : function(){
49284         var valid = true;
49285         var target = false;
49286         this.items.each(function(f){
49287             if(f.validate()){
49288                 return;
49289             }
49290             
49291             valid = false;
49292                 
49293             if(!target && f.el.isVisible(true)){
49294                 target = f;
49295             }
49296         });
49297         
49298         if(this.errorMask && !valid){
49299             Roo.form.BasicForm.popover.mask(this, target);
49300         }
49301         
49302         return valid;
49303     },
49304     /**
49305      * Returns array of invalid form fields.
49306      * @return Array
49307      */
49308     
49309     invalidFields : function()
49310     {
49311         var ret = [];
49312         this.items.each(function(f){
49313             if(f.validate()){
49314                 return;
49315             }
49316             ret.push(f);
49317             
49318         });
49319         
49320         return ret;
49321     },
49322     
49323     
49324     /**
49325      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
49326      * @return Boolean
49327      */
49328     isDirty : function(){
49329         var dirty = false;
49330         this.items.each(function(f){
49331            if(f.isDirty()){
49332                dirty = true;
49333                return false;
49334            }
49335         });
49336         return dirty;
49337     },
49338     
49339     /**
49340      * Returns true if any fields in this form have changed since their original load. (New version)
49341      * @return Boolean
49342      */
49343     
49344     hasChanged : function()
49345     {
49346         var dirty = false;
49347         this.items.each(function(f){
49348            if(f.hasChanged()){
49349                dirty = true;
49350                return false;
49351            }
49352         });
49353         return dirty;
49354         
49355     },
49356     /**
49357      * Resets all hasChanged to 'false' -
49358      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
49359      * So hasChanged storage is only to be used for this purpose
49360      * @return Boolean
49361      */
49362     resetHasChanged : function()
49363     {
49364         this.items.each(function(f){
49365            f.resetHasChanged();
49366         });
49367         
49368     },
49369     
49370     
49371     /**
49372      * Performs a predefined action (submit or load) or custom actions you define on this form.
49373      * @param {String} actionName The name of the action type
49374      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
49375      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
49376      * accept other config options):
49377      * <pre>
49378 Property          Type             Description
49379 ----------------  ---------------  ----------------------------------------------------------------------------------
49380 url               String           The url for the action (defaults to the form's url)
49381 method            String           The form method to use (defaults to the form's method, or POST if not defined)
49382 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
49383 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
49384                                    validate the form on the client (defaults to false)
49385      * </pre>
49386      * @return {BasicForm} this
49387      */
49388     doAction : function(action, options){
49389         if(typeof action == 'string'){
49390             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
49391         }
49392         if(this.fireEvent('beforeaction', this, action) !== false){
49393             this.beforeAction(action);
49394             action.run.defer(100, action);
49395         }
49396         return this;
49397     },
49398
49399     /**
49400      * Shortcut to do a submit action.
49401      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
49402      * @return {BasicForm} this
49403      */
49404     submit : function(options){
49405         this.doAction('submit', options);
49406         return this;
49407     },
49408
49409     /**
49410      * Shortcut to do a load action.
49411      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
49412      * @return {BasicForm} this
49413      */
49414     load : function(options){
49415         this.doAction('load', options);
49416         return this;
49417     },
49418
49419     /**
49420      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
49421      * @param {Record} record The record to edit
49422      * @return {BasicForm} this
49423      */
49424     updateRecord : function(record){
49425         record.beginEdit();
49426         var fs = record.fields;
49427         fs.each(function(f){
49428             var field = this.findField(f.name);
49429             if(field){
49430                 record.set(f.name, field.getValue());
49431             }
49432         }, this);
49433         record.endEdit();
49434         return this;
49435     },
49436
49437     /**
49438      * Loads an Roo.data.Record into this form.
49439      * @param {Record} record The record to load
49440      * @return {BasicForm} this
49441      */
49442     loadRecord : function(record){
49443         this.setValues(record.data);
49444         return this;
49445     },
49446
49447     // private
49448     beforeAction : function(action){
49449         var o = action.options;
49450         
49451         if(!this.disableMask) {
49452             if(this.waitMsgTarget === true){
49453                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
49454             }else if(this.waitMsgTarget){
49455                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
49456                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
49457             }else {
49458                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
49459             }
49460         }
49461         
49462          
49463     },
49464
49465     // private
49466     afterAction : function(action, success){
49467         this.activeAction = null;
49468         var o = action.options;
49469         
49470         if(!this.disableMask) {
49471             if(this.waitMsgTarget === true){
49472                 this.el.unmask();
49473             }else if(this.waitMsgTarget){
49474                 this.waitMsgTarget.unmask();
49475             }else{
49476                 Roo.MessageBox.updateProgress(1);
49477                 Roo.MessageBox.hide();
49478             }
49479         }
49480         
49481         if(success){
49482             if(o.reset){
49483                 this.reset();
49484             }
49485             Roo.callback(o.success, o.scope, [this, action]);
49486             this.fireEvent('actioncomplete', this, action);
49487             
49488         }else{
49489             
49490             // failure condition..
49491             // we have a scenario where updates need confirming.
49492             // eg. if a locking scenario exists..
49493             // we look for { errors : { needs_confirm : true }} in the response.
49494             if (
49495                 (typeof(action.result) != 'undefined')  &&
49496                 (typeof(action.result.errors) != 'undefined')  &&
49497                 (typeof(action.result.errors.needs_confirm) != 'undefined')
49498            ){
49499                 var _t = this;
49500                 Roo.MessageBox.confirm(
49501                     "Change requires confirmation",
49502                     action.result.errorMsg,
49503                     function(r) {
49504                         if (r != 'yes') {
49505                             return;
49506                         }
49507                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
49508                     }
49509                     
49510                 );
49511                 
49512                 
49513                 
49514                 return;
49515             }
49516             
49517             Roo.callback(o.failure, o.scope, [this, action]);
49518             // show an error message if no failed handler is set..
49519             if (!this.hasListener('actionfailed')) {
49520                 Roo.MessageBox.alert("Error",
49521                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
49522                         action.result.errorMsg :
49523                         "Saving Failed, please check your entries or try again"
49524                 );
49525             }
49526             
49527             this.fireEvent('actionfailed', this, action);
49528         }
49529         
49530     },
49531
49532     /**
49533      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
49534      * @param {String} id The value to search for
49535      * @return Field
49536      */
49537     findField : function(id){
49538         var field = this.items.get(id);
49539         if(!field){
49540             this.items.each(function(f){
49541                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
49542                     field = f;
49543                     return false;
49544                 }
49545             });
49546         }
49547         return field || null;
49548     },
49549
49550     /**
49551      * Add a secondary form to this one, 
49552      * Used to provide tabbed forms. One form is primary, with hidden values 
49553      * which mirror the elements from the other forms.
49554      * 
49555      * @param {Roo.form.Form} form to add.
49556      * 
49557      */
49558     addForm : function(form)
49559     {
49560        
49561         if (this.childForms.indexOf(form) > -1) {
49562             // already added..
49563             return;
49564         }
49565         this.childForms.push(form);
49566         var n = '';
49567         Roo.each(form.allItems, function (fe) {
49568             
49569             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
49570             if (this.findField(n)) { // already added..
49571                 return;
49572             }
49573             var add = new Roo.form.Hidden({
49574                 name : n
49575             });
49576             add.render(this.el);
49577             
49578             this.add( add );
49579         }, this);
49580         
49581     },
49582     /**
49583      * Mark fields in this form invalid in bulk.
49584      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
49585      * @return {BasicForm} this
49586      */
49587     markInvalid : function(errors){
49588         if(errors instanceof Array){
49589             for(var i = 0, len = errors.length; i < len; i++){
49590                 var fieldError = errors[i];
49591                 var f = this.findField(fieldError.id);
49592                 if(f){
49593                     f.markInvalid(fieldError.msg);
49594                 }
49595             }
49596         }else{
49597             var field, id;
49598             for(id in errors){
49599                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
49600                     field.markInvalid(errors[id]);
49601                 }
49602             }
49603         }
49604         Roo.each(this.childForms || [], function (f) {
49605             f.markInvalid(errors);
49606         });
49607         
49608         return this;
49609     },
49610
49611     /**
49612      * Set values for fields in this form in bulk.
49613      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
49614      * @return {BasicForm} this
49615      */
49616     setValues : function(values){
49617         if(values instanceof Array){ // array of objects
49618             for(var i = 0, len = values.length; i < len; i++){
49619                 var v = values[i];
49620                 var f = this.findField(v.id);
49621                 if(f){
49622                     f.setValue(v.value);
49623                     if(this.trackResetOnLoad){
49624                         f.originalValue = f.getValue();
49625                     }
49626                 }
49627             }
49628         }else{ // object hash
49629             var field, id;
49630             for(id in values){
49631                 if(typeof values[id] != 'function' && (field = this.findField(id))){
49632                     
49633                     if (field.setFromData && 
49634                         field.valueField && 
49635                         field.displayField &&
49636                         // combos' with local stores can 
49637                         // be queried via setValue()
49638                         // to set their value..
49639                         (field.store && !field.store.isLocal)
49640                         ) {
49641                         // it's a combo
49642                         var sd = { };
49643                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
49644                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
49645                         field.setFromData(sd);
49646                         
49647                     } else {
49648                         field.setValue(values[id]);
49649                     }
49650                     
49651                     
49652                     if(this.trackResetOnLoad){
49653                         field.originalValue = field.getValue();
49654                     }
49655                 }
49656             }
49657         }
49658         this.resetHasChanged();
49659         
49660         
49661         Roo.each(this.childForms || [], function (f) {
49662             f.setValues(values);
49663             f.resetHasChanged();
49664         });
49665                 
49666         return this;
49667     },
49668  
49669     /**
49670      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
49671      * they are returned as an array.
49672      * @param {Boolean} asString
49673      * @return {Object}
49674      */
49675     getValues : function(asString){
49676         if (this.childForms) {
49677             // copy values from the child forms
49678             Roo.each(this.childForms, function (f) {
49679                 this.setValues(f.getValues());
49680             }, this);
49681         }
49682         
49683         // use formdata
49684         if (typeof(FormData) != 'undefined' && asString !== true) {
49685             // this relies on a 'recent' version of chrome apparently...
49686             try {
49687                 var fd = (new FormData(this.el.dom)).entries();
49688                 var ret = {};
49689                 var ent = fd.next();
49690                 while (!ent.done) {
49691                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
49692                     ent = fd.next();
49693                 };
49694                 return ret;
49695             } catch(e) {
49696                 
49697             }
49698             
49699         }
49700         
49701         
49702         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
49703         if(asString === true){
49704             return fs;
49705         }
49706         return Roo.urlDecode(fs);
49707     },
49708     
49709     /**
49710      * Returns the fields in this form as an object with key/value pairs. 
49711      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
49712      * @return {Object}
49713      */
49714     getFieldValues : function(with_hidden)
49715     {
49716         if (this.childForms) {
49717             // copy values from the child forms
49718             // should this call getFieldValues - probably not as we do not currently copy
49719             // hidden fields when we generate..
49720             Roo.each(this.childForms, function (f) {
49721                 this.setValues(f.getValues());
49722             }, this);
49723         }
49724         
49725         var ret = {};
49726         this.items.each(function(f){
49727             if (!f.getName()) {
49728                 return;
49729             }
49730             var v = f.getValue();
49731             if (f.inputType =='radio') {
49732                 if (typeof(ret[f.getName()]) == 'undefined') {
49733                     ret[f.getName()] = ''; // empty..
49734                 }
49735                 
49736                 if (!f.el.dom.checked) {
49737                     return;
49738                     
49739                 }
49740                 v = f.el.dom.value;
49741                 
49742             }
49743             
49744             // not sure if this supported any more..
49745             if ((typeof(v) == 'object') && f.getRawValue) {
49746                 v = f.getRawValue() ; // dates..
49747             }
49748             // combo boxes where name != hiddenName...
49749             if (f.name != f.getName()) {
49750                 ret[f.name] = f.getRawValue();
49751             }
49752             ret[f.getName()] = v;
49753         });
49754         
49755         return ret;
49756     },
49757
49758     /**
49759      * Clears all invalid messages in this form.
49760      * @return {BasicForm} this
49761      */
49762     clearInvalid : function(){
49763         this.items.each(function(f){
49764            f.clearInvalid();
49765         });
49766         
49767         Roo.each(this.childForms || [], function (f) {
49768             f.clearInvalid();
49769         });
49770         
49771         
49772         return this;
49773     },
49774
49775     /**
49776      * Resets this form.
49777      * @return {BasicForm} this
49778      */
49779     reset : function(){
49780         this.items.each(function(f){
49781             f.reset();
49782         });
49783         
49784         Roo.each(this.childForms || [], function (f) {
49785             f.reset();
49786         });
49787         this.resetHasChanged();
49788         
49789         return this;
49790     },
49791
49792     /**
49793      * Add Roo.form components to this form.
49794      * @param {Field} field1
49795      * @param {Field} field2 (optional)
49796      * @param {Field} etc (optional)
49797      * @return {BasicForm} this
49798      */
49799     add : function(){
49800         this.items.addAll(Array.prototype.slice.call(arguments, 0));
49801         return this;
49802     },
49803
49804
49805     /**
49806      * Removes a field from the items collection (does NOT remove its markup).
49807      * @param {Field} field
49808      * @return {BasicForm} this
49809      */
49810     remove : function(field){
49811         this.items.remove(field);
49812         return this;
49813     },
49814
49815     /**
49816      * Looks at the fields in this form, checks them for an id attribute,
49817      * and calls applyTo on the existing dom element with that id.
49818      * @return {BasicForm} this
49819      */
49820     render : function(){
49821         this.items.each(function(f){
49822             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
49823                 f.applyTo(f.id);
49824             }
49825         });
49826         return this;
49827     },
49828
49829     /**
49830      * Calls {@link Ext#apply} for all fields in this form with the passed object.
49831      * @param {Object} values
49832      * @return {BasicForm} this
49833      */
49834     applyToFields : function(o){
49835         this.items.each(function(f){
49836            Roo.apply(f, o);
49837         });
49838         return this;
49839     },
49840
49841     /**
49842      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
49843      * @param {Object} values
49844      * @return {BasicForm} this
49845      */
49846     applyIfToFields : function(o){
49847         this.items.each(function(f){
49848            Roo.applyIf(f, o);
49849         });
49850         return this;
49851     }
49852 });
49853
49854 // back compat
49855 Roo.BasicForm = Roo.form.BasicForm;
49856
49857 Roo.apply(Roo.form.BasicForm, {
49858     
49859     popover : {
49860         
49861         padding : 5,
49862         
49863         isApplied : false,
49864         
49865         isMasked : false,
49866         
49867         form : false,
49868         
49869         target : false,
49870         
49871         intervalID : false,
49872         
49873         maskEl : false,
49874         
49875         apply : function()
49876         {
49877             if(this.isApplied){
49878                 return;
49879             }
49880             
49881             this.maskEl = {
49882                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
49883                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
49884                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
49885                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
49886             };
49887             
49888             this.maskEl.top.enableDisplayMode("block");
49889             this.maskEl.left.enableDisplayMode("block");
49890             this.maskEl.bottom.enableDisplayMode("block");
49891             this.maskEl.right.enableDisplayMode("block");
49892             
49893             Roo.get(document.body).on('click', function(){
49894                 this.unmask();
49895             }, this);
49896             
49897             Roo.get(document.body).on('touchstart', function(){
49898                 this.unmask();
49899             }, this);
49900             
49901             this.isApplied = true
49902         },
49903         
49904         mask : function(form, target)
49905         {
49906             this.form = form;
49907             
49908             this.target = target;
49909             
49910             if(!this.form.errorMask || !target.el){
49911                 return;
49912             }
49913             
49914             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
49915             
49916             var ot = this.target.el.calcOffsetsTo(scrollable);
49917             
49918             var scrollTo = ot[1] - this.form.maskOffset;
49919             
49920             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
49921             
49922             scrollable.scrollTo('top', scrollTo);
49923             
49924             var el = this.target.wrap || this.target.el;
49925             
49926             var box = el.getBox();
49927             
49928             this.maskEl.top.setStyle('position', 'absolute');
49929             this.maskEl.top.setStyle('z-index', 10000);
49930             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
49931             this.maskEl.top.setLeft(0);
49932             this.maskEl.top.setTop(0);
49933             this.maskEl.top.show();
49934             
49935             this.maskEl.left.setStyle('position', 'absolute');
49936             this.maskEl.left.setStyle('z-index', 10000);
49937             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
49938             this.maskEl.left.setLeft(0);
49939             this.maskEl.left.setTop(box.y - this.padding);
49940             this.maskEl.left.show();
49941
49942             this.maskEl.bottom.setStyle('position', 'absolute');
49943             this.maskEl.bottom.setStyle('z-index', 10000);
49944             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
49945             this.maskEl.bottom.setLeft(0);
49946             this.maskEl.bottom.setTop(box.bottom + this.padding);
49947             this.maskEl.bottom.show();
49948
49949             this.maskEl.right.setStyle('position', 'absolute');
49950             this.maskEl.right.setStyle('z-index', 10000);
49951             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
49952             this.maskEl.right.setLeft(box.right + this.padding);
49953             this.maskEl.right.setTop(box.y - this.padding);
49954             this.maskEl.right.show();
49955
49956             this.intervalID = window.setInterval(function() {
49957                 Roo.form.BasicForm.popover.unmask();
49958             }, 10000);
49959
49960             window.onwheel = function(){ return false;};
49961             
49962             (function(){ this.isMasked = true; }).defer(500, this);
49963             
49964         },
49965         
49966         unmask : function()
49967         {
49968             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
49969                 return;
49970             }
49971             
49972             this.maskEl.top.setStyle('position', 'absolute');
49973             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
49974             this.maskEl.top.hide();
49975
49976             this.maskEl.left.setStyle('position', 'absolute');
49977             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
49978             this.maskEl.left.hide();
49979
49980             this.maskEl.bottom.setStyle('position', 'absolute');
49981             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
49982             this.maskEl.bottom.hide();
49983
49984             this.maskEl.right.setStyle('position', 'absolute');
49985             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
49986             this.maskEl.right.hide();
49987             
49988             window.onwheel = function(){ return true;};
49989             
49990             if(this.intervalID){
49991                 window.clearInterval(this.intervalID);
49992                 this.intervalID = false;
49993             }
49994             
49995             this.isMasked = false;
49996             
49997         }
49998         
49999     }
50000     
50001 });/*
50002  * Based on:
50003  * Ext JS Library 1.1.1
50004  * Copyright(c) 2006-2007, Ext JS, LLC.
50005  *
50006  * Originally Released Under LGPL - original licence link has changed is not relivant.
50007  *
50008  * Fork - LGPL
50009  * <script type="text/javascript">
50010  */
50011
50012 /**
50013  * @class Roo.form.Form
50014  * @extends Roo.form.BasicForm
50015  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50016  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
50017  * @constructor
50018  * @param {Object} config Configuration options
50019  */
50020 Roo.form.Form = function(config){
50021     var xitems =  [];
50022     if (config.items) {
50023         xitems = config.items;
50024         delete config.items;
50025     }
50026    
50027     
50028     Roo.form.Form.superclass.constructor.call(this, null, config);
50029     this.url = this.url || this.action;
50030     if(!this.root){
50031         this.root = new Roo.form.Layout(Roo.applyIf({
50032             id: Roo.id()
50033         }, config));
50034     }
50035     this.active = this.root;
50036     /**
50037      * Array of all the buttons that have been added to this form via {@link addButton}
50038      * @type Array
50039      */
50040     this.buttons = [];
50041     this.allItems = [];
50042     this.addEvents({
50043         /**
50044          * @event clientvalidation
50045          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
50046          * @param {Form} this
50047          * @param {Boolean} valid true if the form has passed client-side validation
50048          */
50049         clientvalidation: true,
50050         /**
50051          * @event rendered
50052          * Fires when the form is rendered
50053          * @param {Roo.form.Form} form
50054          */
50055         rendered : true
50056     });
50057     
50058     if (this.progressUrl) {
50059             // push a hidden field onto the list of fields..
50060             this.addxtype( {
50061                     xns: Roo.form, 
50062                     xtype : 'Hidden', 
50063                     name : 'UPLOAD_IDENTIFIER' 
50064             });
50065         }
50066         
50067     
50068     Roo.each(xitems, this.addxtype, this);
50069     
50070 };
50071
50072 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
50073      /**
50074      * @cfg {Roo.Button} buttons[] buttons at bottom of form
50075      */
50076     
50077     /**
50078      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
50079      */
50080     /**
50081      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
50082      */
50083     /**
50084      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
50085      */
50086     buttonAlign:'center',
50087
50088     /**
50089      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
50090      */
50091     minButtonWidth:75,
50092
50093     /**
50094      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
50095      * This property cascades to child containers if not set.
50096      */
50097     labelAlign:'left',
50098
50099     /**
50100      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
50101      * fires a looping event with that state. This is required to bind buttons to the valid
50102      * state using the config value formBind:true on the button.
50103      */
50104     monitorValid : false,
50105
50106     /**
50107      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
50108      */
50109     monitorPoll : 200,
50110     
50111     /**
50112      * @cfg {String} progressUrl - Url to return progress data 
50113      */
50114     
50115     progressUrl : false,
50116     /**
50117      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
50118      * sending a formdata with extra parameters - eg uploaded elements.
50119      */
50120     
50121     formData : false,
50122     
50123     /**
50124      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
50125      * fields are added and the column is closed. If no fields are passed the column remains open
50126      * until end() is called.
50127      * @param {Object} config The config to pass to the column
50128      * @param {Field} field1 (optional)
50129      * @param {Field} field2 (optional)
50130      * @param {Field} etc (optional)
50131      * @return Column The column container object
50132      */
50133     column : function(c){
50134         var col = new Roo.form.Column(c);
50135         this.start(col);
50136         if(arguments.length > 1){ // duplicate code required because of Opera
50137             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50138             this.end();
50139         }
50140         return col;
50141     },
50142
50143     /**
50144      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
50145      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
50146      * until end() is called.
50147      * @param {Object} config The config to pass to the fieldset
50148      * @param {Field} field1 (optional)
50149      * @param {Field} field2 (optional)
50150      * @param {Field} etc (optional)
50151      * @return FieldSet The fieldset container object
50152      */
50153     fieldset : function(c){
50154         var fs = new Roo.form.FieldSet(c);
50155         this.start(fs);
50156         if(arguments.length > 1){ // duplicate code required because of Opera
50157             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50158             this.end();
50159         }
50160         return fs;
50161     },
50162
50163     /**
50164      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
50165      * fields are added and the container is closed. If no fields are passed the container remains open
50166      * until end() is called.
50167      * @param {Object} config The config to pass to the Layout
50168      * @param {Field} field1 (optional)
50169      * @param {Field} field2 (optional)
50170      * @param {Field} etc (optional)
50171      * @return Layout The container object
50172      */
50173     container : function(c){
50174         var l = new Roo.form.Layout(c);
50175         this.start(l);
50176         if(arguments.length > 1){ // duplicate code required because of Opera
50177             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50178             this.end();
50179         }
50180         return l;
50181     },
50182
50183     /**
50184      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
50185      * @param {Object} container A Roo.form.Layout or subclass of Layout
50186      * @return {Form} this
50187      */
50188     start : function(c){
50189         // cascade label info
50190         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
50191         this.active.stack.push(c);
50192         c.ownerCt = this.active;
50193         this.active = c;
50194         return this;
50195     },
50196
50197     /**
50198      * Closes the current open container
50199      * @return {Form} this
50200      */
50201     end : function(){
50202         if(this.active == this.root){
50203             return this;
50204         }
50205         this.active = this.active.ownerCt;
50206         return this;
50207     },
50208
50209     /**
50210      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
50211      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
50212      * as the label of the field.
50213      * @param {Field} field1
50214      * @param {Field} field2 (optional)
50215      * @param {Field} etc. (optional)
50216      * @return {Form} this
50217      */
50218     add : function(){
50219         this.active.stack.push.apply(this.active.stack, arguments);
50220         this.allItems.push.apply(this.allItems,arguments);
50221         var r = [];
50222         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
50223             if(a[i].isFormField){
50224                 r.push(a[i]);
50225             }
50226         }
50227         if(r.length > 0){
50228             Roo.form.Form.superclass.add.apply(this, r);
50229         }
50230         return this;
50231     },
50232     
50233
50234     
50235     
50236     
50237      /**
50238      * Find any element that has been added to a form, using it's ID or name
50239      * This can include framesets, columns etc. along with regular fields..
50240      * @param {String} id - id or name to find.
50241      
50242      * @return {Element} e - or false if nothing found.
50243      */
50244     findbyId : function(id)
50245     {
50246         var ret = false;
50247         if (!id) {
50248             return ret;
50249         }
50250         Roo.each(this.allItems, function(f){
50251             if (f.id == id || f.name == id ){
50252                 ret = f;
50253                 return false;
50254             }
50255         });
50256         return ret;
50257     },
50258
50259     
50260     
50261     /**
50262      * Render this form into the passed container. This should only be called once!
50263      * @param {String/HTMLElement/Element} container The element this component should be rendered into
50264      * @return {Form} this
50265      */
50266     render : function(ct)
50267     {
50268         
50269         
50270         
50271         ct = Roo.get(ct);
50272         var o = this.autoCreate || {
50273             tag: 'form',
50274             method : this.method || 'POST',
50275             id : this.id || Roo.id()
50276         };
50277         this.initEl(ct.createChild(o));
50278
50279         this.root.render(this.el);
50280         
50281        
50282              
50283         this.items.each(function(f){
50284             f.render('x-form-el-'+f.id);
50285         });
50286
50287         if(this.buttons.length > 0){
50288             // tables are required to maintain order and for correct IE layout
50289             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
50290                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
50291                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
50292             }}, null, true);
50293             var tr = tb.getElementsByTagName('tr')[0];
50294             for(var i = 0, len = this.buttons.length; i < len; i++) {
50295                 var b = this.buttons[i];
50296                 var td = document.createElement('td');
50297                 td.className = 'x-form-btn-td';
50298                 b.render(tr.appendChild(td));
50299             }
50300         }
50301         if(this.monitorValid){ // initialize after render
50302             this.startMonitoring();
50303         }
50304         this.fireEvent('rendered', this);
50305         return this;
50306     },
50307
50308     /**
50309      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
50310      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
50311      * object or a valid Roo.DomHelper element config
50312      * @param {Function} handler The function called when the button is clicked
50313      * @param {Object} scope (optional) The scope of the handler function
50314      * @return {Roo.Button}
50315      */
50316     addButton : function(config, handler, scope){
50317         var bc = {
50318             handler: handler,
50319             scope: scope,
50320             minWidth: this.minButtonWidth,
50321             hideParent:true
50322         };
50323         if(typeof config == "string"){
50324             bc.text = config;
50325         }else{
50326             Roo.apply(bc, config);
50327         }
50328         var btn = new Roo.Button(null, bc);
50329         this.buttons.push(btn);
50330         return btn;
50331     },
50332
50333      /**
50334      * Adds a series of form elements (using the xtype property as the factory method.
50335      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
50336      * @param {Object} config 
50337      */
50338     
50339     addxtype : function()
50340     {
50341         var ar = Array.prototype.slice.call(arguments, 0);
50342         var ret = false;
50343         for(var i = 0; i < ar.length; i++) {
50344             if (!ar[i]) {
50345                 continue; // skip -- if this happends something invalid got sent, we 
50346                 // should ignore it, as basically that interface element will not show up
50347                 // and that should be pretty obvious!!
50348             }
50349             
50350             if (Roo.form[ar[i].xtype]) {
50351                 ar[i].form = this;
50352                 var fe = Roo.factory(ar[i], Roo.form);
50353                 if (!ret) {
50354                     ret = fe;
50355                 }
50356                 fe.form = this;
50357                 if (fe.store) {
50358                     fe.store.form = this;
50359                 }
50360                 if (fe.isLayout) {  
50361                          
50362                     this.start(fe);
50363                     this.allItems.push(fe);
50364                     if (fe.items && fe.addxtype) {
50365                         fe.addxtype.apply(fe, fe.items);
50366                         delete fe.items;
50367                     }
50368                      this.end();
50369                     continue;
50370                 }
50371                 
50372                 
50373                  
50374                 this.add(fe);
50375               //  console.log('adding ' + ar[i].xtype);
50376             }
50377             if (ar[i].xtype == 'Button') {  
50378                 //console.log('adding button');
50379                 //console.log(ar[i]);
50380                 this.addButton(ar[i]);
50381                 this.allItems.push(fe);
50382                 continue;
50383             }
50384             
50385             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
50386                 alert('end is not supported on xtype any more, use items');
50387             //    this.end();
50388             //    //console.log('adding end');
50389             }
50390             
50391         }
50392         return ret;
50393     },
50394     
50395     /**
50396      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
50397      * option "monitorValid"
50398      */
50399     startMonitoring : function(){
50400         if(!this.bound){
50401             this.bound = true;
50402             Roo.TaskMgr.start({
50403                 run : this.bindHandler,
50404                 interval : this.monitorPoll || 200,
50405                 scope: this
50406             });
50407         }
50408     },
50409
50410     /**
50411      * Stops monitoring of the valid state of this form
50412      */
50413     stopMonitoring : function(){
50414         this.bound = false;
50415     },
50416
50417     // private
50418     bindHandler : function(){
50419         if(!this.bound){
50420             return false; // stops binding
50421         }
50422         var valid = true;
50423         this.items.each(function(f){
50424             if(!f.isValid(true)){
50425                 valid = false;
50426                 return false;
50427             }
50428         });
50429         for(var i = 0, len = this.buttons.length; i < len; i++){
50430             var btn = this.buttons[i];
50431             if(btn.formBind === true && btn.disabled === valid){
50432                 btn.setDisabled(!valid);
50433             }
50434         }
50435         this.fireEvent('clientvalidation', this, valid);
50436     }
50437     
50438     
50439     
50440     
50441     
50442     
50443     
50444     
50445 });
50446
50447
50448 // back compat
50449 Roo.Form = Roo.form.Form;
50450 /*
50451  * Based on:
50452  * Ext JS Library 1.1.1
50453  * Copyright(c) 2006-2007, Ext JS, LLC.
50454  *
50455  * Originally Released Under LGPL - original licence link has changed is not relivant.
50456  *
50457  * Fork - LGPL
50458  * <script type="text/javascript">
50459  */
50460
50461 // as we use this in bootstrap.
50462 Roo.namespace('Roo.form');
50463  /**
50464  * @class Roo.form.Action
50465  * Internal Class used to handle form actions
50466  * @constructor
50467  * @param {Roo.form.BasicForm} el The form element or its id
50468  * @param {Object} config Configuration options
50469  */
50470
50471  
50472  
50473 // define the action interface
50474 Roo.form.Action = function(form, options){
50475     this.form = form;
50476     this.options = options || {};
50477 };
50478 /**
50479  * Client Validation Failed
50480  * @const 
50481  */
50482 Roo.form.Action.CLIENT_INVALID = 'client';
50483 /**
50484  * Server Validation Failed
50485  * @const 
50486  */
50487 Roo.form.Action.SERVER_INVALID = 'server';
50488  /**
50489  * Connect to Server Failed
50490  * @const 
50491  */
50492 Roo.form.Action.CONNECT_FAILURE = 'connect';
50493 /**
50494  * Reading Data from Server Failed
50495  * @const 
50496  */
50497 Roo.form.Action.LOAD_FAILURE = 'load';
50498
50499 Roo.form.Action.prototype = {
50500     type : 'default',
50501     failureType : undefined,
50502     response : undefined,
50503     result : undefined,
50504
50505     // interface method
50506     run : function(options){
50507
50508     },
50509
50510     // interface method
50511     success : function(response){
50512
50513     },
50514
50515     // interface method
50516     handleResponse : function(response){
50517
50518     },
50519
50520     // default connection failure
50521     failure : function(response){
50522         
50523         this.response = response;
50524         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50525         this.form.afterAction(this, false);
50526     },
50527
50528     processResponse : function(response){
50529         this.response = response;
50530         if(!response.responseText){
50531             return true;
50532         }
50533         this.result = this.handleResponse(response);
50534         return this.result;
50535     },
50536
50537     // utility functions used internally
50538     getUrl : function(appendParams){
50539         var url = this.options.url || this.form.url || this.form.el.dom.action;
50540         if(appendParams){
50541             var p = this.getParams();
50542             if(p){
50543                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
50544             }
50545         }
50546         return url;
50547     },
50548
50549     getMethod : function(){
50550         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
50551     },
50552
50553     getParams : function(){
50554         var bp = this.form.baseParams;
50555         var p = this.options.params;
50556         if(p){
50557             if(typeof p == "object"){
50558                 p = Roo.urlEncode(Roo.applyIf(p, bp));
50559             }else if(typeof p == 'string' && bp){
50560                 p += '&' + Roo.urlEncode(bp);
50561             }
50562         }else if(bp){
50563             p = Roo.urlEncode(bp);
50564         }
50565         return p;
50566     },
50567
50568     createCallback : function(){
50569         return {
50570             success: this.success,
50571             failure: this.failure,
50572             scope: this,
50573             timeout: (this.form.timeout*1000),
50574             upload: this.form.fileUpload ? this.success : undefined
50575         };
50576     }
50577 };
50578
50579 Roo.form.Action.Submit = function(form, options){
50580     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
50581 };
50582
50583 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
50584     type : 'submit',
50585
50586     haveProgress : false,
50587     uploadComplete : false,
50588     
50589     // uploadProgress indicator.
50590     uploadProgress : function()
50591     {
50592         if (!this.form.progressUrl) {
50593             return;
50594         }
50595         
50596         if (!this.haveProgress) {
50597             Roo.MessageBox.progress("Uploading", "Uploading");
50598         }
50599         if (this.uploadComplete) {
50600            Roo.MessageBox.hide();
50601            return;
50602         }
50603         
50604         this.haveProgress = true;
50605    
50606         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
50607         
50608         var c = new Roo.data.Connection();
50609         c.request({
50610             url : this.form.progressUrl,
50611             params: {
50612                 id : uid
50613             },
50614             method: 'GET',
50615             success : function(req){
50616                //console.log(data);
50617                 var rdata = false;
50618                 var edata;
50619                 try  {
50620                    rdata = Roo.decode(req.responseText)
50621                 } catch (e) {
50622                     Roo.log("Invalid data from server..");
50623                     Roo.log(edata);
50624                     return;
50625                 }
50626                 if (!rdata || !rdata.success) {
50627                     Roo.log(rdata);
50628                     Roo.MessageBox.alert(Roo.encode(rdata));
50629                     return;
50630                 }
50631                 var data = rdata.data;
50632                 
50633                 if (this.uploadComplete) {
50634                    Roo.MessageBox.hide();
50635                    return;
50636                 }
50637                    
50638                 if (data){
50639                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
50640                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
50641                     );
50642                 }
50643                 this.uploadProgress.defer(2000,this);
50644             },
50645        
50646             failure: function(data) {
50647                 Roo.log('progress url failed ');
50648                 Roo.log(data);
50649             },
50650             scope : this
50651         });
50652            
50653     },
50654     
50655     
50656     run : function()
50657     {
50658         // run get Values on the form, so it syncs any secondary forms.
50659         this.form.getValues();
50660         
50661         var o = this.options;
50662         var method = this.getMethod();
50663         var isPost = method == 'POST';
50664         if(o.clientValidation === false || this.form.isValid()){
50665             
50666             if (this.form.progressUrl) {
50667                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
50668                     (new Date() * 1) + '' + Math.random());
50669                     
50670             } 
50671             
50672             
50673             Roo.Ajax.request(Roo.apply(this.createCallback(), {
50674                 form:this.form.el.dom,
50675                 url:this.getUrl(!isPost),
50676                 method: method,
50677                 params:isPost ? this.getParams() : null,
50678                 isUpload: this.form.fileUpload,
50679                 formData : this.form.formData
50680             }));
50681             
50682             this.uploadProgress();
50683
50684         }else if (o.clientValidation !== false){ // client validation failed
50685             this.failureType = Roo.form.Action.CLIENT_INVALID;
50686             this.form.afterAction(this, false);
50687         }
50688     },
50689
50690     success : function(response)
50691     {
50692         this.uploadComplete= true;
50693         if (this.haveProgress) {
50694             Roo.MessageBox.hide();
50695         }
50696         
50697         
50698         var result = this.processResponse(response);
50699         if(result === true || result.success){
50700             this.form.afterAction(this, true);
50701             return;
50702         }
50703         if(result.errors){
50704             this.form.markInvalid(result.errors);
50705             this.failureType = Roo.form.Action.SERVER_INVALID;
50706         }
50707         this.form.afterAction(this, false);
50708     },
50709     failure : function(response)
50710     {
50711         this.uploadComplete= true;
50712         if (this.haveProgress) {
50713             Roo.MessageBox.hide();
50714         }
50715         
50716         this.response = response;
50717         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50718         this.form.afterAction(this, false);
50719     },
50720     
50721     handleResponse : function(response){
50722         if(this.form.errorReader){
50723             var rs = this.form.errorReader.read(response);
50724             var errors = [];
50725             if(rs.records){
50726                 for(var i = 0, len = rs.records.length; i < len; i++) {
50727                     var r = rs.records[i];
50728                     errors[i] = r.data;
50729                 }
50730             }
50731             if(errors.length < 1){
50732                 errors = null;
50733             }
50734             return {
50735                 success : rs.success,
50736                 errors : errors
50737             };
50738         }
50739         var ret = false;
50740         try {
50741             ret = Roo.decode(response.responseText);
50742         } catch (e) {
50743             ret = {
50744                 success: false,
50745                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
50746                 errors : []
50747             };
50748         }
50749         return ret;
50750         
50751     }
50752 });
50753
50754
50755 Roo.form.Action.Load = function(form, options){
50756     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
50757     this.reader = this.form.reader;
50758 };
50759
50760 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
50761     type : 'load',
50762
50763     run : function(){
50764         
50765         Roo.Ajax.request(Roo.apply(
50766                 this.createCallback(), {
50767                     method:this.getMethod(),
50768                     url:this.getUrl(false),
50769                     params:this.getParams()
50770         }));
50771     },
50772
50773     success : function(response){
50774         
50775         var result = this.processResponse(response);
50776         if(result === true || !result.success || !result.data){
50777             this.failureType = Roo.form.Action.LOAD_FAILURE;
50778             this.form.afterAction(this, false);
50779             return;
50780         }
50781         this.form.clearInvalid();
50782         this.form.setValues(result.data);
50783         this.form.afterAction(this, true);
50784     },
50785
50786     handleResponse : function(response){
50787         if(this.form.reader){
50788             var rs = this.form.reader.read(response);
50789             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
50790             return {
50791                 success : rs.success,
50792                 data : data
50793             };
50794         }
50795         return Roo.decode(response.responseText);
50796     }
50797 });
50798
50799 Roo.form.Action.ACTION_TYPES = {
50800     'load' : Roo.form.Action.Load,
50801     'submit' : Roo.form.Action.Submit
50802 };/*
50803  * Based on:
50804  * Ext JS Library 1.1.1
50805  * Copyright(c) 2006-2007, Ext JS, LLC.
50806  *
50807  * Originally Released Under LGPL - original licence link has changed is not relivant.
50808  *
50809  * Fork - LGPL
50810  * <script type="text/javascript">
50811  */
50812  
50813 /**
50814  * @class Roo.form.Layout
50815  * @extends Roo.Component
50816  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50817  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
50818  * @constructor
50819  * @param {Object} config Configuration options
50820  */
50821 Roo.form.Layout = function(config){
50822     var xitems = [];
50823     if (config.items) {
50824         xitems = config.items;
50825         delete config.items;
50826     }
50827     Roo.form.Layout.superclass.constructor.call(this, config);
50828     this.stack = [];
50829     Roo.each(xitems, this.addxtype, this);
50830      
50831 };
50832
50833 Roo.extend(Roo.form.Layout, Roo.Component, {
50834     /**
50835      * @cfg {String/Object} autoCreate
50836      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
50837      */
50838     /**
50839      * @cfg {String/Object/Function} style
50840      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
50841      * a function which returns such a specification.
50842      */
50843     /**
50844      * @cfg {String} labelAlign
50845      * Valid values are "left," "top" and "right" (defaults to "left")
50846      */
50847     /**
50848      * @cfg {Number} labelWidth
50849      * Fixed width in pixels of all field labels (defaults to undefined)
50850      */
50851     /**
50852      * @cfg {Boolean} clear
50853      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
50854      */
50855     clear : true,
50856     /**
50857      * @cfg {String} labelSeparator
50858      * The separator to use after field labels (defaults to ':')
50859      */
50860     labelSeparator : ':',
50861     /**
50862      * @cfg {Boolean} hideLabels
50863      * True to suppress the display of field labels in this layout (defaults to false)
50864      */
50865     hideLabels : false,
50866
50867     // private
50868     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
50869     
50870     isLayout : true,
50871     
50872     // private
50873     onRender : function(ct, position){
50874         if(this.el){ // from markup
50875             this.el = Roo.get(this.el);
50876         }else {  // generate
50877             var cfg = this.getAutoCreate();
50878             this.el = ct.createChild(cfg, position);
50879         }
50880         if(this.style){
50881             this.el.applyStyles(this.style);
50882         }
50883         if(this.labelAlign){
50884             this.el.addClass('x-form-label-'+this.labelAlign);
50885         }
50886         if(this.hideLabels){
50887             this.labelStyle = "display:none";
50888             this.elementStyle = "padding-left:0;";
50889         }else{
50890             if(typeof this.labelWidth == 'number'){
50891                 this.labelStyle = "width:"+this.labelWidth+"px;";
50892                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
50893             }
50894             if(this.labelAlign == 'top'){
50895                 this.labelStyle = "width:auto;";
50896                 this.elementStyle = "padding-left:0;";
50897             }
50898         }
50899         var stack = this.stack;
50900         var slen = stack.length;
50901         if(slen > 0){
50902             if(!this.fieldTpl){
50903                 var t = new Roo.Template(
50904                     '<div class="x-form-item {5}">',
50905                         '<label for="{0}" style="{2}">{1}{4}</label>',
50906                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50907                         '</div>',
50908                     '</div><div class="x-form-clear-left"></div>'
50909                 );
50910                 t.disableFormats = true;
50911                 t.compile();
50912                 Roo.form.Layout.prototype.fieldTpl = t;
50913             }
50914             for(var i = 0; i < slen; i++) {
50915                 if(stack[i].isFormField){
50916                     this.renderField(stack[i]);
50917                 }else{
50918                     this.renderComponent(stack[i]);
50919                 }
50920             }
50921         }
50922         if(this.clear){
50923             this.el.createChild({cls:'x-form-clear'});
50924         }
50925     },
50926
50927     // private
50928     renderField : function(f){
50929         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
50930                f.id, //0
50931                f.fieldLabel, //1
50932                f.labelStyle||this.labelStyle||'', //2
50933                this.elementStyle||'', //3
50934                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
50935                f.itemCls||this.itemCls||''  //5
50936        ], true).getPrevSibling());
50937     },
50938
50939     // private
50940     renderComponent : function(c){
50941         c.render(c.isLayout ? this.el : this.el.createChild());    
50942     },
50943     /**
50944      * Adds a object form elements (using the xtype property as the factory method.)
50945      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
50946      * @param {Object} config 
50947      */
50948     addxtype : function(o)
50949     {
50950         // create the lement.
50951         o.form = this.form;
50952         var fe = Roo.factory(o, Roo.form);
50953         this.form.allItems.push(fe);
50954         this.stack.push(fe);
50955         
50956         if (fe.isFormField) {
50957             this.form.items.add(fe);
50958         }
50959          
50960         return fe;
50961     }
50962 });
50963
50964 /**
50965  * @class Roo.form.Column
50966  * @extends Roo.form.Layout
50967  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
50968  * @constructor
50969  * @param {Object} config Configuration options
50970  */
50971 Roo.form.Column = function(config){
50972     Roo.form.Column.superclass.constructor.call(this, config);
50973 };
50974
50975 Roo.extend(Roo.form.Column, Roo.form.Layout, {
50976     /**
50977      * @cfg {Number/String} width
50978      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50979      */
50980     /**
50981      * @cfg {String/Object} autoCreate
50982      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
50983      */
50984
50985     // private
50986     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
50987
50988     // private
50989     onRender : function(ct, position){
50990         Roo.form.Column.superclass.onRender.call(this, ct, position);
50991         if(this.width){
50992             this.el.setWidth(this.width);
50993         }
50994     }
50995 });
50996
50997
50998 /**
50999  * @class Roo.form.Row
51000  * @extends Roo.form.Layout
51001  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51002  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
51003  * @constructor
51004  * @param {Object} config Configuration options
51005  */
51006
51007  
51008 Roo.form.Row = function(config){
51009     Roo.form.Row.superclass.constructor.call(this, config);
51010 };
51011  
51012 Roo.extend(Roo.form.Row, Roo.form.Layout, {
51013       /**
51014      * @cfg {Number/String} width
51015      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51016      */
51017     /**
51018      * @cfg {Number/String} height
51019      * The fixed height of the column in pixels or CSS value (defaults to "auto")
51020      */
51021     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
51022     
51023     padWidth : 20,
51024     // private
51025     onRender : function(ct, position){
51026         //console.log('row render');
51027         if(!this.rowTpl){
51028             var t = new Roo.Template(
51029                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
51030                     '<label for="{0}" style="{2}">{1}{4}</label>',
51031                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51032                     '</div>',
51033                 '</div>'
51034             );
51035             t.disableFormats = true;
51036             t.compile();
51037             Roo.form.Layout.prototype.rowTpl = t;
51038         }
51039         this.fieldTpl = this.rowTpl;
51040         
51041         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
51042         var labelWidth = 100;
51043         
51044         if ((this.labelAlign != 'top')) {
51045             if (typeof this.labelWidth == 'number') {
51046                 labelWidth = this.labelWidth
51047             }
51048             this.padWidth =  20 + labelWidth;
51049             
51050         }
51051         
51052         Roo.form.Column.superclass.onRender.call(this, ct, position);
51053         if(this.width){
51054             this.el.setWidth(this.width);
51055         }
51056         if(this.height){
51057             this.el.setHeight(this.height);
51058         }
51059     },
51060     
51061     // private
51062     renderField : function(f){
51063         f.fieldEl = this.fieldTpl.append(this.el, [
51064                f.id, f.fieldLabel,
51065                f.labelStyle||this.labelStyle||'',
51066                this.elementStyle||'',
51067                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
51068                f.itemCls||this.itemCls||'',
51069                f.width ? f.width + this.padWidth : 160 + this.padWidth
51070        ],true);
51071     }
51072 });
51073  
51074
51075 /**
51076  * @class Roo.form.FieldSet
51077  * @extends Roo.form.Layout
51078  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51079  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
51080  * @constructor
51081  * @param {Object} config Configuration options
51082  */
51083 Roo.form.FieldSet = function(config){
51084     Roo.form.FieldSet.superclass.constructor.call(this, config);
51085 };
51086
51087 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
51088     /**
51089      * @cfg {String} legend
51090      * The text to display as the legend for the FieldSet (defaults to '')
51091      */
51092     /**
51093      * @cfg {String/Object} autoCreate
51094      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
51095      */
51096
51097     // private
51098     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
51099
51100     // private
51101     onRender : function(ct, position){
51102         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
51103         if(this.legend){
51104             this.setLegend(this.legend);
51105         }
51106     },
51107
51108     // private
51109     setLegend : function(text){
51110         if(this.rendered){
51111             this.el.child('legend').update(text);
51112         }
51113     }
51114 });/*
51115  * Based on:
51116  * Ext JS Library 1.1.1
51117  * Copyright(c) 2006-2007, Ext JS, LLC.
51118  *
51119  * Originally Released Under LGPL - original licence link has changed is not relivant.
51120  *
51121  * Fork - LGPL
51122  * <script type="text/javascript">
51123  */
51124 /**
51125  * @class Roo.form.VTypes
51126  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
51127  * @static
51128  */
51129 Roo.form.VTypes = function(){
51130     // closure these in so they are only created once.
51131     var alpha = /^[a-zA-Z_]+$/;
51132     var alphanum = /^[a-zA-Z0-9_]+$/;
51133     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
51134     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
51135
51136     // All these messages and functions are configurable
51137     return {
51138         /**
51139          * The function used to validate email addresses
51140          * @param {String} value The email address
51141          */
51142         'email' : function(v){
51143             return email.test(v);
51144         },
51145         /**
51146          * The error text to display when the email validation function returns false
51147          * @type String
51148          */
51149         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
51150         /**
51151          * The keystroke filter mask to be applied on email input
51152          * @type RegExp
51153          */
51154         'emailMask' : /[a-z0-9_\.\-@]/i,
51155
51156         /**
51157          * The function used to validate URLs
51158          * @param {String} value The URL
51159          */
51160         'url' : function(v){
51161             return url.test(v);
51162         },
51163         /**
51164          * The error text to display when the url validation function returns false
51165          * @type String
51166          */
51167         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
51168         
51169         /**
51170          * The function used to validate alpha values
51171          * @param {String} value The value
51172          */
51173         'alpha' : function(v){
51174             return alpha.test(v);
51175         },
51176         /**
51177          * The error text to display when the alpha validation function returns false
51178          * @type String
51179          */
51180         'alphaText' : 'This field should only contain letters and _',
51181         /**
51182          * The keystroke filter mask to be applied on alpha input
51183          * @type RegExp
51184          */
51185         'alphaMask' : /[a-z_]/i,
51186
51187         /**
51188          * The function used to validate alphanumeric values
51189          * @param {String} value The value
51190          */
51191         'alphanum' : function(v){
51192             return alphanum.test(v);
51193         },
51194         /**
51195          * The error text to display when the alphanumeric validation function returns false
51196          * @type String
51197          */
51198         'alphanumText' : 'This field should only contain letters, numbers and _',
51199         /**
51200          * The keystroke filter mask to be applied on alphanumeric input
51201          * @type RegExp
51202          */
51203         'alphanumMask' : /[a-z0-9_]/i
51204     };
51205 }();//<script type="text/javascript">
51206
51207 /**
51208  * @class Roo.form.FCKeditor
51209  * @extends Roo.form.TextArea
51210  * Wrapper around the FCKEditor http://www.fckeditor.net
51211  * @constructor
51212  * Creates a new FCKeditor
51213  * @param {Object} config Configuration options
51214  */
51215 Roo.form.FCKeditor = function(config){
51216     Roo.form.FCKeditor.superclass.constructor.call(this, config);
51217     this.addEvents({
51218          /**
51219          * @event editorinit
51220          * Fired when the editor is initialized - you can add extra handlers here..
51221          * @param {FCKeditor} this
51222          * @param {Object} the FCK object.
51223          */
51224         editorinit : true
51225     });
51226     
51227     
51228 };
51229 Roo.form.FCKeditor.editors = { };
51230 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
51231 {
51232     //defaultAutoCreate : {
51233     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
51234     //},
51235     // private
51236     /**
51237      * @cfg {Object} fck options - see fck manual for details.
51238      */
51239     fckconfig : false,
51240     
51241     /**
51242      * @cfg {Object} fck toolbar set (Basic or Default)
51243      */
51244     toolbarSet : 'Basic',
51245     /**
51246      * @cfg {Object} fck BasePath
51247      */ 
51248     basePath : '/fckeditor/',
51249     
51250     
51251     frame : false,
51252     
51253     value : '',
51254     
51255    
51256     onRender : function(ct, position)
51257     {
51258         if(!this.el){
51259             this.defaultAutoCreate = {
51260                 tag: "textarea",
51261                 style:"width:300px;height:60px;",
51262                 autocomplete: "new-password"
51263             };
51264         }
51265         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
51266         /*
51267         if(this.grow){
51268             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
51269             if(this.preventScrollbars){
51270                 this.el.setStyle("overflow", "hidden");
51271             }
51272             this.el.setHeight(this.growMin);
51273         }
51274         */
51275         //console.log('onrender' + this.getId() );
51276         Roo.form.FCKeditor.editors[this.getId()] = this;
51277          
51278
51279         this.replaceTextarea() ;
51280         
51281     },
51282     
51283     getEditor : function() {
51284         return this.fckEditor;
51285     },
51286     /**
51287      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
51288      * @param {Mixed} value The value to set
51289      */
51290     
51291     
51292     setValue : function(value)
51293     {
51294         //console.log('setValue: ' + value);
51295         
51296         if(typeof(value) == 'undefined') { // not sure why this is happending...
51297             return;
51298         }
51299         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
51300         
51301         //if(!this.el || !this.getEditor()) {
51302         //    this.value = value;
51303             //this.setValue.defer(100,this,[value]);    
51304         //    return;
51305         //} 
51306         
51307         if(!this.getEditor()) {
51308             return;
51309         }
51310         
51311         this.getEditor().SetData(value);
51312         
51313         //
51314
51315     },
51316
51317     /**
51318      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
51319      * @return {Mixed} value The field value
51320      */
51321     getValue : function()
51322     {
51323         
51324         if (this.frame && this.frame.dom.style.display == 'none') {
51325             return Roo.form.FCKeditor.superclass.getValue.call(this);
51326         }
51327         
51328         if(!this.el || !this.getEditor()) {
51329            
51330            // this.getValue.defer(100,this); 
51331             return this.value;
51332         }
51333        
51334         
51335         var value=this.getEditor().GetData();
51336         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
51337         return Roo.form.FCKeditor.superclass.getValue.call(this);
51338         
51339
51340     },
51341
51342     /**
51343      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
51344      * @return {Mixed} value The field value
51345      */
51346     getRawValue : function()
51347     {
51348         if (this.frame && this.frame.dom.style.display == 'none') {
51349             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
51350         }
51351         
51352         if(!this.el || !this.getEditor()) {
51353             //this.getRawValue.defer(100,this); 
51354             return this.value;
51355             return;
51356         }
51357         
51358         
51359         
51360         var value=this.getEditor().GetData();
51361         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
51362         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
51363          
51364     },
51365     
51366     setSize : function(w,h) {
51367         
51368         
51369         
51370         //if (this.frame && this.frame.dom.style.display == 'none') {
51371         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
51372         //    return;
51373         //}
51374         //if(!this.el || !this.getEditor()) {
51375         //    this.setSize.defer(100,this, [w,h]); 
51376         //    return;
51377         //}
51378         
51379         
51380         
51381         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
51382         
51383         this.frame.dom.setAttribute('width', w);
51384         this.frame.dom.setAttribute('height', h);
51385         this.frame.setSize(w,h);
51386         
51387     },
51388     
51389     toggleSourceEdit : function(value) {
51390         
51391       
51392          
51393         this.el.dom.style.display = value ? '' : 'none';
51394         this.frame.dom.style.display = value ?  'none' : '';
51395         
51396     },
51397     
51398     
51399     focus: function(tag)
51400     {
51401         if (this.frame.dom.style.display == 'none') {
51402             return Roo.form.FCKeditor.superclass.focus.call(this);
51403         }
51404         if(!this.el || !this.getEditor()) {
51405             this.focus.defer(100,this, [tag]); 
51406             return;
51407         }
51408         
51409         
51410         
51411         
51412         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
51413         this.getEditor().Focus();
51414         if (tgs.length) {
51415             if (!this.getEditor().Selection.GetSelection()) {
51416                 this.focus.defer(100,this, [tag]); 
51417                 return;
51418             }
51419             
51420             
51421             var r = this.getEditor().EditorDocument.createRange();
51422             r.setStart(tgs[0],0);
51423             r.setEnd(tgs[0],0);
51424             this.getEditor().Selection.GetSelection().removeAllRanges();
51425             this.getEditor().Selection.GetSelection().addRange(r);
51426             this.getEditor().Focus();
51427         }
51428         
51429     },
51430     
51431     
51432     
51433     replaceTextarea : function()
51434     {
51435         if ( document.getElementById( this.getId() + '___Frame' ) ) {
51436             return ;
51437         }
51438         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
51439         //{
51440             // We must check the elements firstly using the Id and then the name.
51441         var oTextarea = document.getElementById( this.getId() );
51442         
51443         var colElementsByName = document.getElementsByName( this.getId() ) ;
51444          
51445         oTextarea.style.display = 'none' ;
51446
51447         if ( oTextarea.tabIndex ) {            
51448             this.TabIndex = oTextarea.tabIndex ;
51449         }
51450         
51451         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
51452         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
51453         this.frame = Roo.get(this.getId() + '___Frame')
51454     },
51455     
51456     _getConfigHtml : function()
51457     {
51458         var sConfig = '' ;
51459
51460         for ( var o in this.fckconfig ) {
51461             sConfig += sConfig.length > 0  ? '&amp;' : '';
51462             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
51463         }
51464
51465         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
51466     },
51467     
51468     
51469     _getIFrameHtml : function()
51470     {
51471         var sFile = 'fckeditor.html' ;
51472         /* no idea what this is about..
51473         try
51474         {
51475             if ( (/fcksource=true/i).test( window.top.location.search ) )
51476                 sFile = 'fckeditor.original.html' ;
51477         }
51478         catch (e) { 
51479         */
51480
51481         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
51482         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
51483         
51484         
51485         var html = '<iframe id="' + this.getId() +
51486             '___Frame" src="' + sLink +
51487             '" width="' + this.width +
51488             '" height="' + this.height + '"' +
51489             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
51490             ' frameborder="0" scrolling="no"></iframe>' ;
51491
51492         return html ;
51493     },
51494     
51495     _insertHtmlBefore : function( html, element )
51496     {
51497         if ( element.insertAdjacentHTML )       {
51498             // IE
51499             element.insertAdjacentHTML( 'beforeBegin', html ) ;
51500         } else { // Gecko
51501             var oRange = document.createRange() ;
51502             oRange.setStartBefore( element ) ;
51503             var oFragment = oRange.createContextualFragment( html );
51504             element.parentNode.insertBefore( oFragment, element ) ;
51505         }
51506     }
51507     
51508     
51509   
51510     
51511     
51512     
51513     
51514
51515 });
51516
51517 //Roo.reg('fckeditor', Roo.form.FCKeditor);
51518
51519 function FCKeditor_OnComplete(editorInstance){
51520     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
51521     f.fckEditor = editorInstance;
51522     //console.log("loaded");
51523     f.fireEvent('editorinit', f, editorInstance);
51524
51525   
51526
51527  
51528
51529
51530
51531
51532
51533
51534
51535
51536
51537
51538
51539
51540
51541
51542
51543 //<script type="text/javascript">
51544 /**
51545  * @class Roo.form.GridField
51546  * @extends Roo.form.Field
51547  * Embed a grid (or editable grid into a form)
51548  * STATUS ALPHA
51549  * 
51550  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
51551  * it needs 
51552  * xgrid.store = Roo.data.Store
51553  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
51554  * xgrid.store.reader = Roo.data.JsonReader 
51555  * 
51556  * 
51557  * @constructor
51558  * Creates a new GridField
51559  * @param {Object} config Configuration options
51560  */
51561 Roo.form.GridField = function(config){
51562     Roo.form.GridField.superclass.constructor.call(this, config);
51563      
51564 };
51565
51566 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
51567     /**
51568      * @cfg {Number} width  - used to restrict width of grid..
51569      */
51570     width : 100,
51571     /**
51572      * @cfg {Number} height - used to restrict height of grid..
51573      */
51574     height : 50,
51575      /**
51576      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
51577          * 
51578          *}
51579      */
51580     xgrid : false, 
51581     /**
51582      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51583      * {tag: "input", type: "checkbox", autocomplete: "off"})
51584      */
51585    // defaultAutoCreate : { tag: 'div' },
51586     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
51587     /**
51588      * @cfg {String} addTitle Text to include for adding a title.
51589      */
51590     addTitle : false,
51591     //
51592     onResize : function(){
51593         Roo.form.Field.superclass.onResize.apply(this, arguments);
51594     },
51595
51596     initEvents : function(){
51597         // Roo.form.Checkbox.superclass.initEvents.call(this);
51598         // has no events...
51599        
51600     },
51601
51602
51603     getResizeEl : function(){
51604         return this.wrap;
51605     },
51606
51607     getPositionEl : function(){
51608         return this.wrap;
51609     },
51610
51611     // private
51612     onRender : function(ct, position){
51613         
51614         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
51615         var style = this.style;
51616         delete this.style;
51617         
51618         Roo.form.GridField.superclass.onRender.call(this, ct, position);
51619         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
51620         this.viewEl = this.wrap.createChild({ tag: 'div' });
51621         if (style) {
51622             this.viewEl.applyStyles(style);
51623         }
51624         if (this.width) {
51625             this.viewEl.setWidth(this.width);
51626         }
51627         if (this.height) {
51628             this.viewEl.setHeight(this.height);
51629         }
51630         //if(this.inputValue !== undefined){
51631         //this.setValue(this.value);
51632         
51633         
51634         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
51635         
51636         
51637         this.grid.render();
51638         this.grid.getDataSource().on('remove', this.refreshValue, this);
51639         this.grid.getDataSource().on('update', this.refreshValue, this);
51640         this.grid.on('afteredit', this.refreshValue, this);
51641  
51642     },
51643      
51644     
51645     /**
51646      * Sets the value of the item. 
51647      * @param {String} either an object  or a string..
51648      */
51649     setValue : function(v){
51650         //this.value = v;
51651         v = v || []; // empty set..
51652         // this does not seem smart - it really only affects memoryproxy grids..
51653         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
51654             var ds = this.grid.getDataSource();
51655             // assumes a json reader..
51656             var data = {}
51657             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
51658             ds.loadData( data);
51659         }
51660         // clear selection so it does not get stale.
51661         if (this.grid.sm) { 
51662             this.grid.sm.clearSelections();
51663         }
51664         
51665         Roo.form.GridField.superclass.setValue.call(this, v);
51666         this.refreshValue();
51667         // should load data in the grid really....
51668     },
51669     
51670     // private
51671     refreshValue: function() {
51672          var val = [];
51673         this.grid.getDataSource().each(function(r) {
51674             val.push(r.data);
51675         });
51676         this.el.dom.value = Roo.encode(val);
51677     }
51678     
51679      
51680     
51681     
51682 });/*
51683  * Based on:
51684  * Ext JS Library 1.1.1
51685  * Copyright(c) 2006-2007, Ext JS, LLC.
51686  *
51687  * Originally Released Under LGPL - original licence link has changed is not relivant.
51688  *
51689  * Fork - LGPL
51690  * <script type="text/javascript">
51691  */
51692 /**
51693  * @class Roo.form.DisplayField
51694  * @extends Roo.form.Field
51695  * A generic Field to display non-editable data.
51696  * @cfg {Boolean} closable (true|false) default false
51697  * @constructor
51698  * Creates a new Display Field item.
51699  * @param {Object} config Configuration options
51700  */
51701 Roo.form.DisplayField = function(config){
51702     Roo.form.DisplayField.superclass.constructor.call(this, config);
51703     
51704     this.addEvents({
51705         /**
51706          * @event close
51707          * Fires after the click the close btn
51708              * @param {Roo.form.DisplayField} this
51709              */
51710         close : true
51711     });
51712 };
51713
51714 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
51715     inputType:      'hidden',
51716     allowBlank:     true,
51717     readOnly:         true,
51718     
51719  
51720     /**
51721      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51722      */
51723     focusClass : undefined,
51724     /**
51725      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51726      */
51727     fieldClass: 'x-form-field',
51728     
51729      /**
51730      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
51731      */
51732     valueRenderer: undefined,
51733     
51734     width: 100,
51735     /**
51736      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51737      * {tag: "input", type: "checkbox", autocomplete: "off"})
51738      */
51739      
51740  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
51741  
51742     closable : false,
51743     
51744     onResize : function(){
51745         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
51746         
51747     },
51748
51749     initEvents : function(){
51750         // Roo.form.Checkbox.superclass.initEvents.call(this);
51751         // has no events...
51752         
51753         if(this.closable){
51754             this.closeEl.on('click', this.onClose, this);
51755         }
51756        
51757     },
51758
51759
51760     getResizeEl : function(){
51761         return this.wrap;
51762     },
51763
51764     getPositionEl : function(){
51765         return this.wrap;
51766     },
51767
51768     // private
51769     onRender : function(ct, position){
51770         
51771         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
51772         //if(this.inputValue !== undefined){
51773         this.wrap = this.el.wrap();
51774         
51775         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
51776         
51777         if(this.closable){
51778             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
51779         }
51780         
51781         if (this.bodyStyle) {
51782             this.viewEl.applyStyles(this.bodyStyle);
51783         }
51784         //this.viewEl.setStyle('padding', '2px');
51785         
51786         this.setValue(this.value);
51787         
51788     },
51789 /*
51790     // private
51791     initValue : Roo.emptyFn,
51792
51793   */
51794
51795         // private
51796     onClick : function(){
51797         
51798     },
51799
51800     /**
51801      * Sets the checked state of the checkbox.
51802      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
51803      */
51804     setValue : function(v){
51805         this.value = v;
51806         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
51807         // this might be called before we have a dom element..
51808         if (!this.viewEl) {
51809             return;
51810         }
51811         this.viewEl.dom.innerHTML = html;
51812         Roo.form.DisplayField.superclass.setValue.call(this, v);
51813
51814     },
51815     
51816     onClose : function(e)
51817     {
51818         e.preventDefault();
51819         
51820         this.fireEvent('close', this);
51821     }
51822 });/*
51823  * 
51824  * Licence- LGPL
51825  * 
51826  */
51827
51828 /**
51829  * @class Roo.form.DayPicker
51830  * @extends Roo.form.Field
51831  * A Day picker show [M] [T] [W] ....
51832  * @constructor
51833  * Creates a new Day Picker
51834  * @param {Object} config Configuration options
51835  */
51836 Roo.form.DayPicker= function(config){
51837     Roo.form.DayPicker.superclass.constructor.call(this, config);
51838      
51839 };
51840
51841 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
51842     /**
51843      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51844      */
51845     focusClass : undefined,
51846     /**
51847      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51848      */
51849     fieldClass: "x-form-field",
51850    
51851     /**
51852      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51853      * {tag: "input", type: "checkbox", autocomplete: "off"})
51854      */
51855     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
51856     
51857    
51858     actionMode : 'viewEl', 
51859     //
51860     // private
51861  
51862     inputType : 'hidden',
51863     
51864      
51865     inputElement: false, // real input element?
51866     basedOn: false, // ????
51867     
51868     isFormField: true, // not sure where this is needed!!!!
51869
51870     onResize : function(){
51871         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
51872         if(!this.boxLabel){
51873             this.el.alignTo(this.wrap, 'c-c');
51874         }
51875     },
51876
51877     initEvents : function(){
51878         Roo.form.Checkbox.superclass.initEvents.call(this);
51879         this.el.on("click", this.onClick,  this);
51880         this.el.on("change", this.onClick,  this);
51881     },
51882
51883
51884     getResizeEl : function(){
51885         return this.wrap;
51886     },
51887
51888     getPositionEl : function(){
51889         return this.wrap;
51890     },
51891
51892     
51893     // private
51894     onRender : function(ct, position){
51895         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
51896        
51897         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
51898         
51899         var r1 = '<table><tr>';
51900         var r2 = '<tr class="x-form-daypick-icons">';
51901         for (var i=0; i < 7; i++) {
51902             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
51903             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
51904         }
51905         
51906         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
51907         viewEl.select('img').on('click', this.onClick, this);
51908         this.viewEl = viewEl;   
51909         
51910         
51911         // this will not work on Chrome!!!
51912         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
51913         this.el.on('propertychange', this.setFromHidden,  this);  //ie
51914         
51915         
51916           
51917
51918     },
51919
51920     // private
51921     initValue : Roo.emptyFn,
51922
51923     /**
51924      * Returns the checked state of the checkbox.
51925      * @return {Boolean} True if checked, else false
51926      */
51927     getValue : function(){
51928         return this.el.dom.value;
51929         
51930     },
51931
51932         // private
51933     onClick : function(e){ 
51934         //this.setChecked(!this.checked);
51935         Roo.get(e.target).toggleClass('x-menu-item-checked');
51936         this.refreshValue();
51937         //if(this.el.dom.checked != this.checked){
51938         //    this.setValue(this.el.dom.checked);
51939        // }
51940     },
51941     
51942     // private
51943     refreshValue : function()
51944     {
51945         var val = '';
51946         this.viewEl.select('img',true).each(function(e,i,n)  {
51947             val += e.is(".x-menu-item-checked") ? String(n) : '';
51948         });
51949         this.setValue(val, true);
51950     },
51951
51952     /**
51953      * Sets the checked state of the checkbox.
51954      * On is always based on a string comparison between inputValue and the param.
51955      * @param {Boolean/String} value - the value to set 
51956      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
51957      */
51958     setValue : function(v,suppressEvent){
51959         if (!this.el.dom) {
51960             return;
51961         }
51962         var old = this.el.dom.value ;
51963         this.el.dom.value = v;
51964         if (suppressEvent) {
51965             return ;
51966         }
51967          
51968         // update display..
51969         this.viewEl.select('img',true).each(function(e,i,n)  {
51970             
51971             var on = e.is(".x-menu-item-checked");
51972             var newv = v.indexOf(String(n)) > -1;
51973             if (on != newv) {
51974                 e.toggleClass('x-menu-item-checked');
51975             }
51976             
51977         });
51978         
51979         
51980         this.fireEvent('change', this, v, old);
51981         
51982         
51983     },
51984    
51985     // handle setting of hidden value by some other method!!?!?
51986     setFromHidden: function()
51987     {
51988         if(!this.el){
51989             return;
51990         }
51991         //console.log("SET FROM HIDDEN");
51992         //alert('setFrom hidden');
51993         this.setValue(this.el.dom.value);
51994     },
51995     
51996     onDestroy : function()
51997     {
51998         if(this.viewEl){
51999             Roo.get(this.viewEl).remove();
52000         }
52001          
52002         Roo.form.DayPicker.superclass.onDestroy.call(this);
52003     }
52004
52005 });/*
52006  * RooJS Library 1.1.1
52007  * Copyright(c) 2008-2011  Alan Knowles
52008  *
52009  * License - LGPL
52010  */
52011  
52012
52013 /**
52014  * @class Roo.form.ComboCheck
52015  * @extends Roo.form.ComboBox
52016  * A combobox for multiple select items.
52017  *
52018  * FIXME - could do with a reset button..
52019  * 
52020  * @constructor
52021  * Create a new ComboCheck
52022  * @param {Object} config Configuration options
52023  */
52024 Roo.form.ComboCheck = function(config){
52025     Roo.form.ComboCheck.superclass.constructor.call(this, config);
52026     // should verify some data...
52027     // like
52028     // hiddenName = required..
52029     // displayField = required
52030     // valudField == required
52031     var req= [ 'hiddenName', 'displayField', 'valueField' ];
52032     var _t = this;
52033     Roo.each(req, function(e) {
52034         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
52035             throw "Roo.form.ComboCheck : missing value for: " + e;
52036         }
52037     });
52038     
52039     
52040 };
52041
52042 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
52043      
52044      
52045     editable : false,
52046      
52047     selectedClass: 'x-menu-item-checked', 
52048     
52049     // private
52050     onRender : function(ct, position){
52051         var _t = this;
52052         
52053         
52054         
52055         if(!this.tpl){
52056             var cls = 'x-combo-list';
52057
52058             
52059             this.tpl =  new Roo.Template({
52060                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
52061                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
52062                    '<span>{' + this.displayField + '}</span>' +
52063                     '</div>' 
52064                 
52065             });
52066         }
52067  
52068         
52069         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
52070         this.view.singleSelect = false;
52071         this.view.multiSelect = true;
52072         this.view.toggleSelect = true;
52073         this.pageTb.add(new Roo.Toolbar.Fill(), {
52074             
52075             text: 'Done',
52076             handler: function()
52077             {
52078                 _t.collapse();
52079             }
52080         });
52081     },
52082     
52083     onViewOver : function(e, t){
52084         // do nothing...
52085         return;
52086         
52087     },
52088     
52089     onViewClick : function(doFocus,index){
52090         return;
52091         
52092     },
52093     select: function () {
52094         //Roo.log("SELECT CALLED");
52095     },
52096      
52097     selectByValue : function(xv, scrollIntoView){
52098         var ar = this.getValueArray();
52099         var sels = [];
52100         
52101         Roo.each(ar, function(v) {
52102             if(v === undefined || v === null){
52103                 return;
52104             }
52105             var r = this.findRecord(this.valueField, v);
52106             if(r){
52107                 sels.push(this.store.indexOf(r))
52108                 
52109             }
52110         },this);
52111         this.view.select(sels);
52112         return false;
52113     },
52114     
52115     
52116     
52117     onSelect : function(record, index){
52118        // Roo.log("onselect Called");
52119        // this is only called by the clear button now..
52120         this.view.clearSelections();
52121         this.setValue('[]');
52122         if (this.value != this.valueBefore) {
52123             this.fireEvent('change', this, this.value, this.valueBefore);
52124             this.valueBefore = this.value;
52125         }
52126     },
52127     getValueArray : function()
52128     {
52129         var ar = [] ;
52130         
52131         try {
52132             //Roo.log(this.value);
52133             if (typeof(this.value) == 'undefined') {
52134                 return [];
52135             }
52136             var ar = Roo.decode(this.value);
52137             return  ar instanceof Array ? ar : []; //?? valid?
52138             
52139         } catch(e) {
52140             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
52141             return [];
52142         }
52143          
52144     },
52145     expand : function ()
52146     {
52147         
52148         Roo.form.ComboCheck.superclass.expand.call(this);
52149         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
52150         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
52151         
52152
52153     },
52154     
52155     collapse : function(){
52156         Roo.form.ComboCheck.superclass.collapse.call(this);
52157         var sl = this.view.getSelectedIndexes();
52158         var st = this.store;
52159         var nv = [];
52160         var tv = [];
52161         var r;
52162         Roo.each(sl, function(i) {
52163             r = st.getAt(i);
52164             nv.push(r.get(this.valueField));
52165         },this);
52166         this.setValue(Roo.encode(nv));
52167         if (this.value != this.valueBefore) {
52168
52169             this.fireEvent('change', this, this.value, this.valueBefore);
52170             this.valueBefore = this.value;
52171         }
52172         
52173     },
52174     
52175     setValue : function(v){
52176         // Roo.log(v);
52177         this.value = v;
52178         
52179         var vals = this.getValueArray();
52180         var tv = [];
52181         Roo.each(vals, function(k) {
52182             var r = this.findRecord(this.valueField, k);
52183             if(r){
52184                 tv.push(r.data[this.displayField]);
52185             }else if(this.valueNotFoundText !== undefined){
52186                 tv.push( this.valueNotFoundText );
52187             }
52188         },this);
52189        // Roo.log(tv);
52190         
52191         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
52192         this.hiddenField.value = v;
52193         this.value = v;
52194     }
52195     
52196 });/*
52197  * Based on:
52198  * Ext JS Library 1.1.1
52199  * Copyright(c) 2006-2007, Ext JS, LLC.
52200  *
52201  * Originally Released Under LGPL - original licence link has changed is not relivant.
52202  *
52203  * Fork - LGPL
52204  * <script type="text/javascript">
52205  */
52206  
52207 /**
52208  * @class Roo.form.Signature
52209  * @extends Roo.form.Field
52210  * Signature field.  
52211  * @constructor
52212  * 
52213  * @param {Object} config Configuration options
52214  */
52215
52216 Roo.form.Signature = function(config){
52217     Roo.form.Signature.superclass.constructor.call(this, config);
52218     
52219     this.addEvents({// not in used??
52220          /**
52221          * @event confirm
52222          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
52223              * @param {Roo.form.Signature} combo This combo box
52224              */
52225         'confirm' : true,
52226         /**
52227          * @event reset
52228          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
52229              * @param {Roo.form.ComboBox} combo This combo box
52230              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
52231              */
52232         'reset' : true
52233     });
52234 };
52235
52236 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
52237     /**
52238      * @cfg {Object} labels Label to use when rendering a form.
52239      * defaults to 
52240      * labels : { 
52241      *      clear : "Clear",
52242      *      confirm : "Confirm"
52243      *  }
52244      */
52245     labels : { 
52246         clear : "Clear",
52247         confirm : "Confirm"
52248     },
52249     /**
52250      * @cfg {Number} width The signature panel width (defaults to 300)
52251      */
52252     width: 300,
52253     /**
52254      * @cfg {Number} height The signature panel height (defaults to 100)
52255      */
52256     height : 100,
52257     /**
52258      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
52259      */
52260     allowBlank : false,
52261     
52262     //private
52263     // {Object} signPanel The signature SVG panel element (defaults to {})
52264     signPanel : {},
52265     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
52266     isMouseDown : false,
52267     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
52268     isConfirmed : false,
52269     // {String} signatureTmp SVG mapping string (defaults to empty string)
52270     signatureTmp : '',
52271     
52272     
52273     defaultAutoCreate : { // modified by initCompnoent..
52274         tag: "input",
52275         type:"hidden"
52276     },
52277
52278     // private
52279     onRender : function(ct, position){
52280         
52281         Roo.form.Signature.superclass.onRender.call(this, ct, position);
52282         
52283         this.wrap = this.el.wrap({
52284             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
52285         });
52286         
52287         this.createToolbar(this);
52288         this.signPanel = this.wrap.createChild({
52289                 tag: 'div',
52290                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
52291             }, this.el
52292         );
52293             
52294         this.svgID = Roo.id();
52295         this.svgEl = this.signPanel.createChild({
52296               xmlns : 'http://www.w3.org/2000/svg',
52297               tag : 'svg',
52298               id : this.svgID + "-svg",
52299               width: this.width,
52300               height: this.height,
52301               viewBox: '0 0 '+this.width+' '+this.height,
52302               cn : [
52303                 {
52304                     tag: "rect",
52305                     id: this.svgID + "-svg-r",
52306                     width: this.width,
52307                     height: this.height,
52308                     fill: "#ffa"
52309                 },
52310                 {
52311                     tag: "line",
52312                     id: this.svgID + "-svg-l",
52313                     x1: "0", // start
52314                     y1: (this.height*0.8), // start set the line in 80% of height
52315                     x2: this.width, // end
52316                     y2: (this.height*0.8), // end set the line in 80% of height
52317                     'stroke': "#666",
52318                     'stroke-width': "1",
52319                     'stroke-dasharray': "3",
52320                     'shape-rendering': "crispEdges",
52321                     'pointer-events': "none"
52322                 },
52323                 {
52324                     tag: "path",
52325                     id: this.svgID + "-svg-p",
52326                     'stroke': "navy",
52327                     'stroke-width': "3",
52328                     'fill': "none",
52329                     'pointer-events': 'none'
52330                 }
52331               ]
52332         });
52333         this.createSVG();
52334         this.svgBox = this.svgEl.dom.getScreenCTM();
52335     },
52336     createSVG : function(){ 
52337         var svg = this.signPanel;
52338         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
52339         var t = this;
52340
52341         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
52342         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
52343         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
52344         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
52345         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
52346         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
52347         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
52348         
52349     },
52350     isTouchEvent : function(e){
52351         return e.type.match(/^touch/);
52352     },
52353     getCoords : function (e) {
52354         var pt    = this.svgEl.dom.createSVGPoint();
52355         pt.x = e.clientX; 
52356         pt.y = e.clientY;
52357         if (this.isTouchEvent(e)) {
52358             pt.x =  e.targetTouches[0].clientX;
52359             pt.y = e.targetTouches[0].clientY;
52360         }
52361         var a = this.svgEl.dom.getScreenCTM();
52362         var b = a.inverse();
52363         var mx = pt.matrixTransform(b);
52364         return mx.x + ',' + mx.y;
52365     },
52366     //mouse event headler 
52367     down : function (e) {
52368         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
52369         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
52370         
52371         this.isMouseDown = true;
52372         
52373         e.preventDefault();
52374     },
52375     move : function (e) {
52376         if (this.isMouseDown) {
52377             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
52378             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
52379         }
52380         
52381         e.preventDefault();
52382     },
52383     up : function (e) {
52384         this.isMouseDown = false;
52385         var sp = this.signatureTmp.split(' ');
52386         
52387         if(sp.length > 1){
52388             if(!sp[sp.length-2].match(/^L/)){
52389                 sp.pop();
52390                 sp.pop();
52391                 sp.push("");
52392                 this.signatureTmp = sp.join(" ");
52393             }
52394         }
52395         if(this.getValue() != this.signatureTmp){
52396             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52397             this.isConfirmed = false;
52398         }
52399         e.preventDefault();
52400     },
52401     
52402     /**
52403      * Protected method that will not generally be called directly. It
52404      * is called when the editor creates its toolbar. Override this method if you need to
52405      * add custom toolbar buttons.
52406      * @param {HtmlEditor} editor
52407      */
52408     createToolbar : function(editor){
52409          function btn(id, toggle, handler){
52410             var xid = fid + '-'+ id ;
52411             return {
52412                 id : xid,
52413                 cmd : id,
52414                 cls : 'x-btn-icon x-edit-'+id,
52415                 enableToggle:toggle !== false,
52416                 scope: editor, // was editor...
52417                 handler:handler||editor.relayBtnCmd,
52418                 clickEvent:'mousedown',
52419                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52420                 tabIndex:-1
52421             };
52422         }
52423         
52424         
52425         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52426         this.tb = tb;
52427         this.tb.add(
52428            {
52429                 cls : ' x-signature-btn x-signature-'+id,
52430                 scope: editor, // was editor...
52431                 handler: this.reset,
52432                 clickEvent:'mousedown',
52433                 text: this.labels.clear
52434             },
52435             {
52436                  xtype : 'Fill',
52437                  xns: Roo.Toolbar
52438             }, 
52439             {
52440                 cls : '  x-signature-btn x-signature-'+id,
52441                 scope: editor, // was editor...
52442                 handler: this.confirmHandler,
52443                 clickEvent:'mousedown',
52444                 text: this.labels.confirm
52445             }
52446         );
52447     
52448     },
52449     //public
52450     /**
52451      * when user is clicked confirm then show this image.....
52452      * 
52453      * @return {String} Image Data URI
52454      */
52455     getImageDataURI : function(){
52456         var svg = this.svgEl.dom.parentNode.innerHTML;
52457         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
52458         return src; 
52459     },
52460     /**
52461      * 
52462      * @return {Boolean} this.isConfirmed
52463      */
52464     getConfirmed : function(){
52465         return this.isConfirmed;
52466     },
52467     /**
52468      * 
52469      * @return {Number} this.width
52470      */
52471     getWidth : function(){
52472         return this.width;
52473     },
52474     /**
52475      * 
52476      * @return {Number} this.height
52477      */
52478     getHeight : function(){
52479         return this.height;
52480     },
52481     // private
52482     getSignature : function(){
52483         return this.signatureTmp;
52484     },
52485     // private
52486     reset : function(){
52487         this.signatureTmp = '';
52488         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52489         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
52490         this.isConfirmed = false;
52491         Roo.form.Signature.superclass.reset.call(this);
52492     },
52493     setSignature : function(s){
52494         this.signatureTmp = s;
52495         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52496         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
52497         this.setValue(s);
52498         this.isConfirmed = false;
52499         Roo.form.Signature.superclass.reset.call(this);
52500     }, 
52501     test : function(){
52502 //        Roo.log(this.signPanel.dom.contentWindow.up())
52503     },
52504     //private
52505     setConfirmed : function(){
52506         
52507         
52508         
52509 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
52510     },
52511     // private
52512     confirmHandler : function(){
52513         if(!this.getSignature()){
52514             return;
52515         }
52516         
52517         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
52518         this.setValue(this.getSignature());
52519         this.isConfirmed = true;
52520         
52521         this.fireEvent('confirm', this);
52522     },
52523     // private
52524     // Subclasses should provide the validation implementation by overriding this
52525     validateValue : function(value){
52526         if(this.allowBlank){
52527             return true;
52528         }
52529         
52530         if(this.isConfirmed){
52531             return true;
52532         }
52533         return false;
52534     }
52535 });/*
52536  * Based on:
52537  * Ext JS Library 1.1.1
52538  * Copyright(c) 2006-2007, Ext JS, LLC.
52539  *
52540  * Originally Released Under LGPL - original licence link has changed is not relivant.
52541  *
52542  * Fork - LGPL
52543  * <script type="text/javascript">
52544  */
52545  
52546
52547 /**
52548  * @class Roo.form.ComboBox
52549  * @extends Roo.form.TriggerField
52550  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
52551  * @constructor
52552  * Create a new ComboBox.
52553  * @param {Object} config Configuration options
52554  */
52555 Roo.form.Select = function(config){
52556     Roo.form.Select.superclass.constructor.call(this, config);
52557      
52558 };
52559
52560 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
52561     /**
52562      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
52563      */
52564     /**
52565      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
52566      * rendering into an Roo.Editor, defaults to false)
52567      */
52568     /**
52569      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
52570      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
52571      */
52572     /**
52573      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
52574      */
52575     /**
52576      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
52577      * the dropdown list (defaults to undefined, with no header element)
52578      */
52579
52580      /**
52581      * @cfg {String/Roo.Template} tpl The template to use to render the output
52582      */
52583      
52584     // private
52585     defaultAutoCreate : {tag: "select"  },
52586     /**
52587      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
52588      */
52589     listWidth: undefined,
52590     /**
52591      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
52592      * mode = 'remote' or 'text' if mode = 'local')
52593      */
52594     displayField: undefined,
52595     /**
52596      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
52597      * mode = 'remote' or 'value' if mode = 'local'). 
52598      * Note: use of a valueField requires the user make a selection
52599      * in order for a value to be mapped.
52600      */
52601     valueField: undefined,
52602     
52603     
52604     /**
52605      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
52606      * field's data value (defaults to the underlying DOM element's name)
52607      */
52608     hiddenName: undefined,
52609     /**
52610      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
52611      */
52612     listClass: '',
52613     /**
52614      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
52615      */
52616     selectedClass: 'x-combo-selected',
52617     /**
52618      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
52619      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
52620      * which displays a downward arrow icon).
52621      */
52622     triggerClass : 'x-form-arrow-trigger',
52623     /**
52624      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
52625      */
52626     shadow:'sides',
52627     /**
52628      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
52629      * anchor positions (defaults to 'tl-bl')
52630      */
52631     listAlign: 'tl-bl?',
52632     /**
52633      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
52634      */
52635     maxHeight: 300,
52636     /**
52637      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
52638      * query specified by the allQuery config option (defaults to 'query')
52639      */
52640     triggerAction: 'query',
52641     /**
52642      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
52643      * (defaults to 4, does not apply if editable = false)
52644      */
52645     minChars : 4,
52646     /**
52647      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
52648      * delay (typeAheadDelay) if it matches a known value (defaults to false)
52649      */
52650     typeAhead: false,
52651     /**
52652      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
52653      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
52654      */
52655     queryDelay: 500,
52656     /**
52657      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
52658      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
52659      */
52660     pageSize: 0,
52661     /**
52662      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
52663      * when editable = true (defaults to false)
52664      */
52665     selectOnFocus:false,
52666     /**
52667      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
52668      */
52669     queryParam: 'query',
52670     /**
52671      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
52672      * when mode = 'remote' (defaults to 'Loading...')
52673      */
52674     loadingText: 'Loading...',
52675     /**
52676      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
52677      */
52678     resizable: false,
52679     /**
52680      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
52681      */
52682     handleHeight : 8,
52683     /**
52684      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
52685      * traditional select (defaults to true)
52686      */
52687     editable: true,
52688     /**
52689      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
52690      */
52691     allQuery: '',
52692     /**
52693      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
52694      */
52695     mode: 'remote',
52696     /**
52697      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
52698      * listWidth has a higher value)
52699      */
52700     minListWidth : 70,
52701     /**
52702      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
52703      * allow the user to set arbitrary text into the field (defaults to false)
52704      */
52705     forceSelection:false,
52706     /**
52707      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
52708      * if typeAhead = true (defaults to 250)
52709      */
52710     typeAheadDelay : 250,
52711     /**
52712      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
52713      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
52714      */
52715     valueNotFoundText : undefined,
52716     
52717     /**
52718      * @cfg {String} defaultValue The value displayed after loading the store.
52719      */
52720     defaultValue: '',
52721     
52722     /**
52723      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
52724      */
52725     blockFocus : false,
52726     
52727     /**
52728      * @cfg {Boolean} disableClear Disable showing of clear button.
52729      */
52730     disableClear : false,
52731     /**
52732      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
52733      */
52734     alwaysQuery : false,
52735     
52736     //private
52737     addicon : false,
52738     editicon: false,
52739     
52740     // element that contains real text value.. (when hidden is used..)
52741      
52742     // private
52743     onRender : function(ct, position){
52744         Roo.form.Field.prototype.onRender.call(this, ct, position);
52745         
52746         if(this.store){
52747             this.store.on('beforeload', this.onBeforeLoad, this);
52748             this.store.on('load', this.onLoad, this);
52749             this.store.on('loadexception', this.onLoadException, this);
52750             this.store.load({});
52751         }
52752         
52753         
52754         
52755     },
52756
52757     // private
52758     initEvents : function(){
52759         //Roo.form.ComboBox.superclass.initEvents.call(this);
52760  
52761     },
52762
52763     onDestroy : function(){
52764        
52765         if(this.store){
52766             this.store.un('beforeload', this.onBeforeLoad, this);
52767             this.store.un('load', this.onLoad, this);
52768             this.store.un('loadexception', this.onLoadException, this);
52769         }
52770         //Roo.form.ComboBox.superclass.onDestroy.call(this);
52771     },
52772
52773     // private
52774     fireKey : function(e){
52775         if(e.isNavKeyPress() && !this.list.isVisible()){
52776             this.fireEvent("specialkey", this, e);
52777         }
52778     },
52779
52780     // private
52781     onResize: function(w, h){
52782         
52783         return; 
52784     
52785         
52786     },
52787
52788     /**
52789      * Allow or prevent the user from directly editing the field text.  If false is passed,
52790      * the user will only be able to select from the items defined in the dropdown list.  This method
52791      * is the runtime equivalent of setting the 'editable' config option at config time.
52792      * @param {Boolean} value True to allow the user to directly edit the field text
52793      */
52794     setEditable : function(value){
52795          
52796     },
52797
52798     // private
52799     onBeforeLoad : function(){
52800         
52801         Roo.log("Select before load");
52802         return;
52803     
52804         this.innerList.update(this.loadingText ?
52805                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
52806         //this.restrictHeight();
52807         this.selectedIndex = -1;
52808     },
52809
52810     // private
52811     onLoad : function(){
52812
52813     
52814         var dom = this.el.dom;
52815         dom.innerHTML = '';
52816          var od = dom.ownerDocument;
52817          
52818         if (this.emptyText) {
52819             var op = od.createElement('option');
52820             op.setAttribute('value', '');
52821             op.innerHTML = String.format('{0}', this.emptyText);
52822             dom.appendChild(op);
52823         }
52824         if(this.store.getCount() > 0){
52825            
52826             var vf = this.valueField;
52827             var df = this.displayField;
52828             this.store.data.each(function(r) {
52829                 // which colmsn to use... testing - cdoe / title..
52830                 var op = od.createElement('option');
52831                 op.setAttribute('value', r.data[vf]);
52832                 op.innerHTML = String.format('{0}', r.data[df]);
52833                 dom.appendChild(op);
52834             });
52835             if (typeof(this.defaultValue != 'undefined')) {
52836                 this.setValue(this.defaultValue);
52837             }
52838             
52839              
52840         }else{
52841             //this.onEmptyResults();
52842         }
52843         //this.el.focus();
52844     },
52845     // private
52846     onLoadException : function()
52847     {
52848         dom.innerHTML = '';
52849             
52850         Roo.log("Select on load exception");
52851         return;
52852     
52853         this.collapse();
52854         Roo.log(this.store.reader.jsonData);
52855         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52856             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52857         }
52858         
52859         
52860     },
52861     // private
52862     onTypeAhead : function(){
52863          
52864     },
52865
52866     // private
52867     onSelect : function(record, index){
52868         Roo.log('on select?');
52869         return;
52870         if(this.fireEvent('beforeselect', this, record, index) !== false){
52871             this.setFromData(index > -1 ? record.data : false);
52872             this.collapse();
52873             this.fireEvent('select', this, record, index);
52874         }
52875     },
52876
52877     /**
52878      * Returns the currently selected field value or empty string if no value is set.
52879      * @return {String} value The selected value
52880      */
52881     getValue : function(){
52882         var dom = this.el.dom;
52883         this.value = dom.options[dom.selectedIndex].value;
52884         return this.value;
52885         
52886     },
52887
52888     /**
52889      * Clears any text/value currently set in the field
52890      */
52891     clearValue : function(){
52892         this.value = '';
52893         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
52894         
52895     },
52896
52897     /**
52898      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
52899      * will be displayed in the field.  If the value does not match the data value of an existing item,
52900      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
52901      * Otherwise the field will be blank (although the value will still be set).
52902      * @param {String} value The value to match
52903      */
52904     setValue : function(v){
52905         var d = this.el.dom;
52906         for (var i =0; i < d.options.length;i++) {
52907             if (v == d.options[i].value) {
52908                 d.selectedIndex = i;
52909                 this.value = v;
52910                 return;
52911             }
52912         }
52913         this.clearValue();
52914     },
52915     /**
52916      * @property {Object} the last set data for the element
52917      */
52918     
52919     lastData : false,
52920     /**
52921      * Sets the value of the field based on a object which is related to the record format for the store.
52922      * @param {Object} value the value to set as. or false on reset?
52923      */
52924     setFromData : function(o){
52925         Roo.log('setfrom data?');
52926          
52927         
52928         
52929     },
52930     // private
52931     reset : function(){
52932         this.clearValue();
52933     },
52934     // private
52935     findRecord : function(prop, value){
52936         
52937         return false;
52938     
52939         var record;
52940         if(this.store.getCount() > 0){
52941             this.store.each(function(r){
52942                 if(r.data[prop] == value){
52943                     record = r;
52944                     return false;
52945                 }
52946                 return true;
52947             });
52948         }
52949         return record;
52950     },
52951     
52952     getName: function()
52953     {
52954         // returns hidden if it's set..
52955         if (!this.rendered) {return ''};
52956         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
52957         
52958     },
52959      
52960
52961     
52962
52963     // private
52964     onEmptyResults : function(){
52965         Roo.log('empty results');
52966         //this.collapse();
52967     },
52968
52969     /**
52970      * Returns true if the dropdown list is expanded, else false.
52971      */
52972     isExpanded : function(){
52973         return false;
52974     },
52975
52976     /**
52977      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
52978      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52979      * @param {String} value The data value of the item to select
52980      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52981      * selected item if it is not currently in view (defaults to true)
52982      * @return {Boolean} True if the value matched an item in the list, else false
52983      */
52984     selectByValue : function(v, scrollIntoView){
52985         Roo.log('select By Value');
52986         return false;
52987     
52988         if(v !== undefined && v !== null){
52989             var r = this.findRecord(this.valueField || this.displayField, v);
52990             if(r){
52991                 this.select(this.store.indexOf(r), scrollIntoView);
52992                 return true;
52993             }
52994         }
52995         return false;
52996     },
52997
52998     /**
52999      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
53000      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53001      * @param {Number} index The zero-based index of the list item to select
53002      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53003      * selected item if it is not currently in view (defaults to true)
53004      */
53005     select : function(index, scrollIntoView){
53006         Roo.log('select ');
53007         return  ;
53008         
53009         this.selectedIndex = index;
53010         this.view.select(index);
53011         if(scrollIntoView !== false){
53012             var el = this.view.getNode(index);
53013             if(el){
53014                 this.innerList.scrollChildIntoView(el, false);
53015             }
53016         }
53017     },
53018
53019       
53020
53021     // private
53022     validateBlur : function(){
53023         
53024         return;
53025         
53026     },
53027
53028     // private
53029     initQuery : function(){
53030         this.doQuery(this.getRawValue());
53031     },
53032
53033     // private
53034     doForce : function(){
53035         if(this.el.dom.value.length > 0){
53036             this.el.dom.value =
53037                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
53038              
53039         }
53040     },
53041
53042     /**
53043      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
53044      * query allowing the query action to be canceled if needed.
53045      * @param {String} query The SQL query to execute
53046      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
53047      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
53048      * saved in the current store (defaults to false)
53049      */
53050     doQuery : function(q, forceAll){
53051         
53052         Roo.log('doQuery?');
53053         if(q === undefined || q === null){
53054             q = '';
53055         }
53056         var qe = {
53057             query: q,
53058             forceAll: forceAll,
53059             combo: this,
53060             cancel:false
53061         };
53062         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
53063             return false;
53064         }
53065         q = qe.query;
53066         forceAll = qe.forceAll;
53067         if(forceAll === true || (q.length >= this.minChars)){
53068             if(this.lastQuery != q || this.alwaysQuery){
53069                 this.lastQuery = q;
53070                 if(this.mode == 'local'){
53071                     this.selectedIndex = -1;
53072                     if(forceAll){
53073                         this.store.clearFilter();
53074                     }else{
53075                         this.store.filter(this.displayField, q);
53076                     }
53077                     this.onLoad();
53078                 }else{
53079                     this.store.baseParams[this.queryParam] = q;
53080                     this.store.load({
53081                         params: this.getParams(q)
53082                     });
53083                     this.expand();
53084                 }
53085             }else{
53086                 this.selectedIndex = -1;
53087                 this.onLoad();   
53088             }
53089         }
53090     },
53091
53092     // private
53093     getParams : function(q){
53094         var p = {};
53095         //p[this.queryParam] = q;
53096         if(this.pageSize){
53097             p.start = 0;
53098             p.limit = this.pageSize;
53099         }
53100         return p;
53101     },
53102
53103     /**
53104      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
53105      */
53106     collapse : function(){
53107         
53108     },
53109
53110     // private
53111     collapseIf : function(e){
53112         
53113     },
53114
53115     /**
53116      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
53117      */
53118     expand : function(){
53119         
53120     } ,
53121
53122     // private
53123      
53124
53125     /** 
53126     * @cfg {Boolean} grow 
53127     * @hide 
53128     */
53129     /** 
53130     * @cfg {Number} growMin 
53131     * @hide 
53132     */
53133     /** 
53134     * @cfg {Number} growMax 
53135     * @hide 
53136     */
53137     /**
53138      * @hide
53139      * @method autoSize
53140      */
53141     
53142     setWidth : function()
53143     {
53144         
53145     },
53146     getResizeEl : function(){
53147         return this.el;
53148     }
53149 });//<script type="text/javasscript">
53150  
53151
53152 /**
53153  * @class Roo.DDView
53154  * A DnD enabled version of Roo.View.
53155  * @param {Element/String} container The Element in which to create the View.
53156  * @param {String} tpl The template string used to create the markup for each element of the View
53157  * @param {Object} config The configuration properties. These include all the config options of
53158  * {@link Roo.View} plus some specific to this class.<br>
53159  * <p>
53160  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
53161  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
53162  * <p>
53163  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
53164 .x-view-drag-insert-above {
53165         border-top:1px dotted #3366cc;
53166 }
53167 .x-view-drag-insert-below {
53168         border-bottom:1px dotted #3366cc;
53169 }
53170 </code></pre>
53171  * 
53172  */
53173  
53174 Roo.DDView = function(container, tpl, config) {
53175     Roo.DDView.superclass.constructor.apply(this, arguments);
53176     this.getEl().setStyle("outline", "0px none");
53177     this.getEl().unselectable();
53178     if (this.dragGroup) {
53179         this.setDraggable(this.dragGroup.split(","));
53180     }
53181     if (this.dropGroup) {
53182         this.setDroppable(this.dropGroup.split(","));
53183     }
53184     if (this.deletable) {
53185         this.setDeletable();
53186     }
53187     this.isDirtyFlag = false;
53188         this.addEvents({
53189                 "drop" : true
53190         });
53191 };
53192
53193 Roo.extend(Roo.DDView, Roo.View, {
53194 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
53195 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
53196 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
53197 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
53198
53199         isFormField: true,
53200
53201         reset: Roo.emptyFn,
53202         
53203         clearInvalid: Roo.form.Field.prototype.clearInvalid,
53204
53205         validate: function() {
53206                 return true;
53207         },
53208         
53209         destroy: function() {
53210                 this.purgeListeners();
53211                 this.getEl.removeAllListeners();
53212                 this.getEl().remove();
53213                 if (this.dragZone) {
53214                         if (this.dragZone.destroy) {
53215                                 this.dragZone.destroy();
53216                         }
53217                 }
53218                 if (this.dropZone) {
53219                         if (this.dropZone.destroy) {
53220                                 this.dropZone.destroy();
53221                         }
53222                 }
53223         },
53224
53225 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
53226         getName: function() {
53227                 return this.name;
53228         },
53229
53230 /**     Loads the View from a JSON string representing the Records to put into the Store. */
53231         setValue: function(v) {
53232                 if (!this.store) {
53233                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
53234                 }
53235                 var data = {};
53236                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
53237                 this.store.proxy = new Roo.data.MemoryProxy(data);
53238                 this.store.load();
53239         },
53240
53241 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
53242         getValue: function() {
53243                 var result = '(';
53244                 this.store.each(function(rec) {
53245                         result += rec.id + ',';
53246                 });
53247                 return result.substr(0, result.length - 1) + ')';
53248         },
53249         
53250         getIds: function() {
53251                 var i = 0, result = new Array(this.store.getCount());
53252                 this.store.each(function(rec) {
53253                         result[i++] = rec.id;
53254                 });
53255                 return result;
53256         },
53257         
53258         isDirty: function() {
53259                 return this.isDirtyFlag;
53260         },
53261
53262 /**
53263  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
53264  *      whole Element becomes the target, and this causes the drop gesture to append.
53265  */
53266     getTargetFromEvent : function(e) {
53267                 var target = e.getTarget();
53268                 while ((target !== null) && (target.parentNode != this.el.dom)) {
53269                 target = target.parentNode;
53270                 }
53271                 if (!target) {
53272                         target = this.el.dom.lastChild || this.el.dom;
53273                 }
53274                 return target;
53275     },
53276
53277 /**
53278  *      Create the drag data which consists of an object which has the property "ddel" as
53279  *      the drag proxy element. 
53280  */
53281     getDragData : function(e) {
53282         var target = this.findItemFromChild(e.getTarget());
53283                 if(target) {
53284                         this.handleSelection(e);
53285                         var selNodes = this.getSelectedNodes();
53286             var dragData = {
53287                 source: this,
53288                 copy: this.copy || (this.allowCopy && e.ctrlKey),
53289                 nodes: selNodes,
53290                 records: []
53291                         };
53292                         var selectedIndices = this.getSelectedIndexes();
53293                         for (var i = 0; i < selectedIndices.length; i++) {
53294                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
53295                         }
53296                         if (selNodes.length == 1) {
53297                                 dragData.ddel = target.cloneNode(true); // the div element
53298                         } else {
53299                                 var div = document.createElement('div'); // create the multi element drag "ghost"
53300                                 div.className = 'multi-proxy';
53301                                 for (var i = 0, len = selNodes.length; i < len; i++) {
53302                                         div.appendChild(selNodes[i].cloneNode(true));
53303                                 }
53304                                 dragData.ddel = div;
53305                         }
53306             //console.log(dragData)
53307             //console.log(dragData.ddel.innerHTML)
53308                         return dragData;
53309                 }
53310         //console.log('nodragData')
53311                 return false;
53312     },
53313     
53314 /**     Specify to which ddGroup items in this DDView may be dragged. */
53315     setDraggable: function(ddGroup) {
53316         if (ddGroup instanceof Array) {
53317                 Roo.each(ddGroup, this.setDraggable, this);
53318                 return;
53319         }
53320         if (this.dragZone) {
53321                 this.dragZone.addToGroup(ddGroup);
53322         } else {
53323                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
53324                                 containerScroll: true,
53325                                 ddGroup: ddGroup 
53326
53327                         });
53328 //                      Draggability implies selection. DragZone's mousedown selects the element.
53329                         if (!this.multiSelect) { this.singleSelect = true; }
53330
53331 //                      Wire the DragZone's handlers up to methods in *this*
53332                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
53333                 }
53334     },
53335
53336 /**     Specify from which ddGroup this DDView accepts drops. */
53337     setDroppable: function(ddGroup) {
53338         if (ddGroup instanceof Array) {
53339                 Roo.each(ddGroup, this.setDroppable, this);
53340                 return;
53341         }
53342         if (this.dropZone) {
53343                 this.dropZone.addToGroup(ddGroup);
53344         } else {
53345                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
53346                                 containerScroll: true,
53347                                 ddGroup: ddGroup
53348                         });
53349
53350 //                      Wire the DropZone's handlers up to methods in *this*
53351                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
53352                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
53353                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
53354                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
53355                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
53356                 }
53357     },
53358
53359 /**     Decide whether to drop above or below a View node. */
53360     getDropPoint : function(e, n, dd){
53361         if (n == this.el.dom) { return "above"; }
53362                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
53363                 var c = t + (b - t) / 2;
53364                 var y = Roo.lib.Event.getPageY(e);
53365                 if(y <= c) {
53366                         return "above";
53367                 }else{
53368                         return "below";
53369                 }
53370     },
53371
53372     onNodeEnter : function(n, dd, e, data){
53373                 return false;
53374     },
53375     
53376     onNodeOver : function(n, dd, e, data){
53377                 var pt = this.getDropPoint(e, n, dd);
53378                 // set the insert point style on the target node
53379                 var dragElClass = this.dropNotAllowed;
53380                 if (pt) {
53381                         var targetElClass;
53382                         if (pt == "above"){
53383                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
53384                                 targetElClass = "x-view-drag-insert-above";
53385                         } else {
53386                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
53387                                 targetElClass = "x-view-drag-insert-below";
53388                         }
53389                         if (this.lastInsertClass != targetElClass){
53390                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
53391                                 this.lastInsertClass = targetElClass;
53392                         }
53393                 }
53394                 return dragElClass;
53395         },
53396
53397     onNodeOut : function(n, dd, e, data){
53398                 this.removeDropIndicators(n);
53399     },
53400
53401     onNodeDrop : function(n, dd, e, data){
53402         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
53403                 return false;
53404         }
53405         var pt = this.getDropPoint(e, n, dd);
53406                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
53407                 if (pt == "below") { insertAt++; }
53408                 for (var i = 0; i < data.records.length; i++) {
53409                         var r = data.records[i];
53410                         var dup = this.store.getById(r.id);
53411                         if (dup && (dd != this.dragZone)) {
53412                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
53413                         } else {
53414                                 if (data.copy) {
53415                                         this.store.insert(insertAt++, r.copy());
53416                                 } else {
53417                                         data.source.isDirtyFlag = true;
53418                                         r.store.remove(r);
53419                                         this.store.insert(insertAt++, r);
53420                                 }
53421                                 this.isDirtyFlag = true;
53422                         }
53423                 }
53424                 this.dragZone.cachedTarget = null;
53425                 return true;
53426     },
53427
53428     removeDropIndicators : function(n){
53429                 if(n){
53430                         Roo.fly(n).removeClass([
53431                                 "x-view-drag-insert-above",
53432                                 "x-view-drag-insert-below"]);
53433                         this.lastInsertClass = "_noclass";
53434                 }
53435     },
53436
53437 /**
53438  *      Utility method. Add a delete option to the DDView's context menu.
53439  *      @param {String} imageUrl The URL of the "delete" icon image.
53440  */
53441         setDeletable: function(imageUrl) {
53442                 if (!this.singleSelect && !this.multiSelect) {
53443                         this.singleSelect = true;
53444                 }
53445                 var c = this.getContextMenu();
53446                 this.contextMenu.on("itemclick", function(item) {
53447                         switch (item.id) {
53448                                 case "delete":
53449                                         this.remove(this.getSelectedIndexes());
53450                                         break;
53451                         }
53452                 }, this);
53453                 this.contextMenu.add({
53454                         icon: imageUrl,
53455                         id: "delete",
53456                         text: 'Delete'
53457                 });
53458         },
53459         
53460 /**     Return the context menu for this DDView. */
53461         getContextMenu: function() {
53462                 if (!this.contextMenu) {
53463 //                      Create the View's context menu
53464                         this.contextMenu = new Roo.menu.Menu({
53465                                 id: this.id + "-contextmenu"
53466                         });
53467                         this.el.on("contextmenu", this.showContextMenu, this);
53468                 }
53469                 return this.contextMenu;
53470         },
53471         
53472         disableContextMenu: function() {
53473                 if (this.contextMenu) {
53474                         this.el.un("contextmenu", this.showContextMenu, this);
53475                 }
53476         },
53477
53478         showContextMenu: function(e, item) {
53479         item = this.findItemFromChild(e.getTarget());
53480                 if (item) {
53481                         e.stopEvent();
53482                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
53483                         this.contextMenu.showAt(e.getXY());
53484             }
53485     },
53486
53487 /**
53488  *      Remove {@link Roo.data.Record}s at the specified indices.
53489  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
53490  */
53491     remove: function(selectedIndices) {
53492                 selectedIndices = [].concat(selectedIndices);
53493                 for (var i = 0; i < selectedIndices.length; i++) {
53494                         var rec = this.store.getAt(selectedIndices[i]);
53495                         this.store.remove(rec);
53496                 }
53497     },
53498
53499 /**
53500  *      Double click fires the event, but also, if this is draggable, and there is only one other
53501  *      related DropZone, it transfers the selected node.
53502  */
53503     onDblClick : function(e){
53504         var item = this.findItemFromChild(e.getTarget());
53505         if(item){
53506             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
53507                 return false;
53508             }
53509             if (this.dragGroup) {
53510                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
53511                     while (targets.indexOf(this.dropZone) > -1) {
53512                             targets.remove(this.dropZone);
53513                                 }
53514                     if (targets.length == 1) {
53515                                         this.dragZone.cachedTarget = null;
53516                         var el = Roo.get(targets[0].getEl());
53517                         var box = el.getBox(true);
53518                         targets[0].onNodeDrop(el.dom, {
53519                                 target: el.dom,
53520                                 xy: [box.x, box.y + box.height - 1]
53521                         }, null, this.getDragData(e));
53522                     }
53523                 }
53524         }
53525     },
53526     
53527     handleSelection: function(e) {
53528                 this.dragZone.cachedTarget = null;
53529         var item = this.findItemFromChild(e.getTarget());
53530         if (!item) {
53531                 this.clearSelections(true);
53532                 return;
53533         }
53534                 if (item && (this.multiSelect || this.singleSelect)){
53535                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
53536                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
53537                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
53538                                 this.unselect(item);
53539                         } else {
53540                                 this.select(item, this.multiSelect && e.ctrlKey);
53541                                 this.lastSelection = item;
53542                         }
53543                 }
53544     },
53545
53546     onItemClick : function(item, index, e){
53547                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
53548                         return false;
53549                 }
53550                 return true;
53551     },
53552
53553     unselect : function(nodeInfo, suppressEvent){
53554                 var node = this.getNode(nodeInfo);
53555                 if(node && this.isSelected(node)){
53556                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
53557                                 Roo.fly(node).removeClass(this.selectedClass);
53558                                 this.selections.remove(node);
53559                                 if(!suppressEvent){
53560                                         this.fireEvent("selectionchange", this, this.selections);
53561                                 }
53562                         }
53563                 }
53564     }
53565 });
53566 /*
53567  * Based on:
53568  * Ext JS Library 1.1.1
53569  * Copyright(c) 2006-2007, Ext JS, LLC.
53570  *
53571  * Originally Released Under LGPL - original licence link has changed is not relivant.
53572  *
53573  * Fork - LGPL
53574  * <script type="text/javascript">
53575  */
53576  
53577 /**
53578  * @class Roo.LayoutManager
53579  * @extends Roo.util.Observable
53580  * Base class for layout managers.
53581  */
53582 Roo.LayoutManager = function(container, config){
53583     Roo.LayoutManager.superclass.constructor.call(this);
53584     this.el = Roo.get(container);
53585     // ie scrollbar fix
53586     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
53587         document.body.scroll = "no";
53588     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
53589         this.el.position('relative');
53590     }
53591     this.id = this.el.id;
53592     this.el.addClass("x-layout-container");
53593     /** false to disable window resize monitoring @type Boolean */
53594     this.monitorWindowResize = true;
53595     this.regions = {};
53596     this.addEvents({
53597         /**
53598          * @event layout
53599          * Fires when a layout is performed. 
53600          * @param {Roo.LayoutManager} this
53601          */
53602         "layout" : true,
53603         /**
53604          * @event regionresized
53605          * Fires when the user resizes a region. 
53606          * @param {Roo.LayoutRegion} region The resized region
53607          * @param {Number} newSize The new size (width for east/west, height for north/south)
53608          */
53609         "regionresized" : true,
53610         /**
53611          * @event regioncollapsed
53612          * Fires when a region is collapsed. 
53613          * @param {Roo.LayoutRegion} region The collapsed region
53614          */
53615         "regioncollapsed" : true,
53616         /**
53617          * @event regionexpanded
53618          * Fires when a region is expanded.  
53619          * @param {Roo.LayoutRegion} region The expanded region
53620          */
53621         "regionexpanded" : true
53622     });
53623     this.updating = false;
53624     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53625 };
53626
53627 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
53628     /**
53629      * Returns true if this layout is currently being updated
53630      * @return {Boolean}
53631      */
53632     isUpdating : function(){
53633         return this.updating; 
53634     },
53635     
53636     /**
53637      * Suspend the LayoutManager from doing auto-layouts while
53638      * making multiple add or remove calls
53639      */
53640     beginUpdate : function(){
53641         this.updating = true;    
53642     },
53643     
53644     /**
53645      * Restore auto-layouts and optionally disable the manager from performing a layout
53646      * @param {Boolean} noLayout true to disable a layout update 
53647      */
53648     endUpdate : function(noLayout){
53649         this.updating = false;
53650         if(!noLayout){
53651             this.layout();
53652         }    
53653     },
53654     
53655     layout: function(){
53656         
53657     },
53658     
53659     onRegionResized : function(region, newSize){
53660         this.fireEvent("regionresized", region, newSize);
53661         this.layout();
53662     },
53663     
53664     onRegionCollapsed : function(region){
53665         this.fireEvent("regioncollapsed", region);
53666     },
53667     
53668     onRegionExpanded : function(region){
53669         this.fireEvent("regionexpanded", region);
53670     },
53671         
53672     /**
53673      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
53674      * performs box-model adjustments.
53675      * @return {Object} The size as an object {width: (the width), height: (the height)}
53676      */
53677     getViewSize : function(){
53678         var size;
53679         if(this.el.dom != document.body){
53680             size = this.el.getSize();
53681         }else{
53682             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
53683         }
53684         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
53685         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
53686         return size;
53687     },
53688     
53689     /**
53690      * Returns the Element this layout is bound to.
53691      * @return {Roo.Element}
53692      */
53693     getEl : function(){
53694         return this.el;
53695     },
53696     
53697     /**
53698      * Returns the specified region.
53699      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
53700      * @return {Roo.LayoutRegion}
53701      */
53702     getRegion : function(target){
53703         return this.regions[target.toLowerCase()];
53704     },
53705     
53706     onWindowResize : function(){
53707         if(this.monitorWindowResize){
53708             this.layout();
53709         }
53710     }
53711 });/*
53712  * Based on:
53713  * Ext JS Library 1.1.1
53714  * Copyright(c) 2006-2007, Ext JS, LLC.
53715  *
53716  * Originally Released Under LGPL - original licence link has changed is not relivant.
53717  *
53718  * Fork - LGPL
53719  * <script type="text/javascript">
53720  */
53721 /**
53722  * @class Roo.BorderLayout
53723  * @extends Roo.LayoutManager
53724  * @children Roo.ContentPanel
53725  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
53726  * please see: <br><br>
53727  * <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>
53728  * <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>
53729  * Example:
53730  <pre><code>
53731  var layout = new Roo.BorderLayout(document.body, {
53732     north: {
53733         initialSize: 25,
53734         titlebar: false
53735     },
53736     west: {
53737         split:true,
53738         initialSize: 200,
53739         minSize: 175,
53740         maxSize: 400,
53741         titlebar: true,
53742         collapsible: true
53743     },
53744     east: {
53745         split:true,
53746         initialSize: 202,
53747         minSize: 175,
53748         maxSize: 400,
53749         titlebar: true,
53750         collapsible: true
53751     },
53752     south: {
53753         split:true,
53754         initialSize: 100,
53755         minSize: 100,
53756         maxSize: 200,
53757         titlebar: true,
53758         collapsible: true
53759     },
53760     center: {
53761         titlebar: true,
53762         autoScroll:true,
53763         resizeTabs: true,
53764         minTabWidth: 50,
53765         preferredTabWidth: 150
53766     }
53767 });
53768
53769 // shorthand
53770 var CP = Roo.ContentPanel;
53771
53772 layout.beginUpdate();
53773 layout.add("north", new CP("north", "North"));
53774 layout.add("south", new CP("south", {title: "South", closable: true}));
53775 layout.add("west", new CP("west", {title: "West"}));
53776 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
53777 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
53778 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
53779 layout.getRegion("center").showPanel("center1");
53780 layout.endUpdate();
53781 </code></pre>
53782
53783 <b>The container the layout is rendered into can be either the body element or any other element.
53784 If it is not the body element, the container needs to either be an absolute positioned element,
53785 or you will need to add "position:relative" to the css of the container.  You will also need to specify
53786 the container size if it is not the body element.</b>
53787
53788 * @constructor
53789 * Create a new BorderLayout
53790 * @param {String/HTMLElement/Element} container The container this layout is bound to
53791 * @param {Object} config Configuration options
53792  */
53793 Roo.BorderLayout = function(container, config){
53794     config = config || {};
53795     Roo.BorderLayout.superclass.constructor.call(this, container, config);
53796     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
53797     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
53798         var target = this.factory.validRegions[i];
53799         if(config[target]){
53800             this.addRegion(target, config[target]);
53801         }
53802     }
53803 };
53804
53805 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
53806         
53807         /**
53808          * @cfg {Roo.LayoutRegion} east
53809          */
53810         /**
53811          * @cfg {Roo.LayoutRegion} west
53812          */
53813         /**
53814          * @cfg {Roo.LayoutRegion} north
53815          */
53816         /**
53817          * @cfg {Roo.LayoutRegion} south
53818          */
53819         /**
53820          * @cfg {Roo.LayoutRegion} center
53821          */
53822     /**
53823      * Creates and adds a new region if it doesn't already exist.
53824      * @param {String} target The target region key (north, south, east, west or center).
53825      * @param {Object} config The regions config object
53826      * @return {BorderLayoutRegion} The new region
53827      */
53828     addRegion : function(target, config){
53829         if(!this.regions[target]){
53830             var r = this.factory.create(target, this, config);
53831             this.bindRegion(target, r);
53832         }
53833         return this.regions[target];
53834     },
53835
53836     // private (kinda)
53837     bindRegion : function(name, r){
53838         this.regions[name] = r;
53839         r.on("visibilitychange", this.layout, this);
53840         r.on("paneladded", this.layout, this);
53841         r.on("panelremoved", this.layout, this);
53842         r.on("invalidated", this.layout, this);
53843         r.on("resized", this.onRegionResized, this);
53844         r.on("collapsed", this.onRegionCollapsed, this);
53845         r.on("expanded", this.onRegionExpanded, this);
53846     },
53847
53848     /**
53849      * Performs a layout update.
53850      */
53851     layout : function(){
53852         if(this.updating) {
53853             return;
53854         }
53855         var size = this.getViewSize();
53856         var w = size.width;
53857         var h = size.height;
53858         var centerW = w;
53859         var centerH = h;
53860         var centerY = 0;
53861         var centerX = 0;
53862         //var x = 0, y = 0;
53863
53864         var rs = this.regions;
53865         var north = rs["north"];
53866         var south = rs["south"]; 
53867         var west = rs["west"];
53868         var east = rs["east"];
53869         var center = rs["center"];
53870         //if(this.hideOnLayout){ // not supported anymore
53871             //c.el.setStyle("display", "none");
53872         //}
53873         if(north && north.isVisible()){
53874             var b = north.getBox();
53875             var m = north.getMargins();
53876             b.width = w - (m.left+m.right);
53877             b.x = m.left;
53878             b.y = m.top;
53879             centerY = b.height + b.y + m.bottom;
53880             centerH -= centerY;
53881             north.updateBox(this.safeBox(b));
53882         }
53883         if(south && south.isVisible()){
53884             var b = south.getBox();
53885             var m = south.getMargins();
53886             b.width = w - (m.left+m.right);
53887             b.x = m.left;
53888             var totalHeight = (b.height + m.top + m.bottom);
53889             b.y = h - totalHeight + m.top;
53890             centerH -= totalHeight;
53891             south.updateBox(this.safeBox(b));
53892         }
53893         if(west && west.isVisible()){
53894             var b = west.getBox();
53895             var m = west.getMargins();
53896             b.height = centerH - (m.top+m.bottom);
53897             b.x = m.left;
53898             b.y = centerY + m.top;
53899             var totalWidth = (b.width + m.left + m.right);
53900             centerX += totalWidth;
53901             centerW -= totalWidth;
53902             west.updateBox(this.safeBox(b));
53903         }
53904         if(east && east.isVisible()){
53905             var b = east.getBox();
53906             var m = east.getMargins();
53907             b.height = centerH - (m.top+m.bottom);
53908             var totalWidth = (b.width + m.left + m.right);
53909             b.x = w - totalWidth + m.left;
53910             b.y = centerY + m.top;
53911             centerW -= totalWidth;
53912             east.updateBox(this.safeBox(b));
53913         }
53914         if(center){
53915             var m = center.getMargins();
53916             var centerBox = {
53917                 x: centerX + m.left,
53918                 y: centerY + m.top,
53919                 width: centerW - (m.left+m.right),
53920                 height: centerH - (m.top+m.bottom)
53921             };
53922             //if(this.hideOnLayout){
53923                 //center.el.setStyle("display", "block");
53924             //}
53925             center.updateBox(this.safeBox(centerBox));
53926         }
53927         this.el.repaint();
53928         this.fireEvent("layout", this);
53929     },
53930
53931     // private
53932     safeBox : function(box){
53933         box.width = Math.max(0, box.width);
53934         box.height = Math.max(0, box.height);
53935         return box;
53936     },
53937
53938     /**
53939      * Adds a ContentPanel (or subclass) to this layout.
53940      * @param {String} target The target region key (north, south, east, west or center).
53941      * @param {Roo.ContentPanel} panel The panel to add
53942      * @return {Roo.ContentPanel} The added panel
53943      */
53944     add : function(target, panel){
53945          
53946         target = target.toLowerCase();
53947         return this.regions[target].add(panel);
53948     },
53949
53950     /**
53951      * Remove a ContentPanel (or subclass) to this layout.
53952      * @param {String} target The target region key (north, south, east, west or center).
53953      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
53954      * @return {Roo.ContentPanel} The removed panel
53955      */
53956     remove : function(target, panel){
53957         target = target.toLowerCase();
53958         return this.regions[target].remove(panel);
53959     },
53960
53961     /**
53962      * Searches all regions for a panel with the specified id
53963      * @param {String} panelId
53964      * @return {Roo.ContentPanel} The panel or null if it wasn't found
53965      */
53966     findPanel : function(panelId){
53967         var rs = this.regions;
53968         for(var target in rs){
53969             if(typeof rs[target] != "function"){
53970                 var p = rs[target].getPanel(panelId);
53971                 if(p){
53972                     return p;
53973                 }
53974             }
53975         }
53976         return null;
53977     },
53978
53979     /**
53980      * Searches all regions for a panel with the specified id and activates (shows) it.
53981      * @param {String/ContentPanel} panelId The panels id or the panel itself
53982      * @return {Roo.ContentPanel} The shown panel or null
53983      */
53984     showPanel : function(panelId) {
53985       var rs = this.regions;
53986       for(var target in rs){
53987          var r = rs[target];
53988          if(typeof r != "function"){
53989             if(r.hasPanel(panelId)){
53990                return r.showPanel(panelId);
53991             }
53992          }
53993       }
53994       return null;
53995    },
53996
53997    /**
53998      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
53999      * @param {Roo.state.Provider} provider (optional) An alternate state provider
54000      */
54001     restoreState : function(provider){
54002         if(!provider){
54003             provider = Roo.state.Manager;
54004         }
54005         var sm = new Roo.LayoutStateManager();
54006         sm.init(this, provider);
54007     },
54008
54009     /**
54010      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
54011      * object should contain properties for each region to add ContentPanels to, and each property's value should be
54012      * a valid ContentPanel config object.  Example:
54013      * <pre><code>
54014 // Create the main layout
54015 var layout = new Roo.BorderLayout('main-ct', {
54016     west: {
54017         split:true,
54018         minSize: 175,
54019         titlebar: true
54020     },
54021     center: {
54022         title:'Components'
54023     }
54024 }, 'main-ct');
54025
54026 // Create and add multiple ContentPanels at once via configs
54027 layout.batchAdd({
54028    west: {
54029        id: 'source-files',
54030        autoCreate:true,
54031        title:'Ext Source Files',
54032        autoScroll:true,
54033        fitToFrame:true
54034    },
54035    center : {
54036        el: cview,
54037        autoScroll:true,
54038        fitToFrame:true,
54039        toolbar: tb,
54040        resizeEl:'cbody'
54041    }
54042 });
54043 </code></pre>
54044      * @param {Object} regions An object containing ContentPanel configs by region name
54045      */
54046     batchAdd : function(regions){
54047         this.beginUpdate();
54048         for(var rname in regions){
54049             var lr = this.regions[rname];
54050             if(lr){
54051                 this.addTypedPanels(lr, regions[rname]);
54052             }
54053         }
54054         this.endUpdate();
54055     },
54056
54057     // private
54058     addTypedPanels : function(lr, ps){
54059         if(typeof ps == 'string'){
54060             lr.add(new Roo.ContentPanel(ps));
54061         }
54062         else if(ps instanceof Array){
54063             for(var i =0, len = ps.length; i < len; i++){
54064                 this.addTypedPanels(lr, ps[i]);
54065             }
54066         }
54067         else if(!ps.events){ // raw config?
54068             var el = ps.el;
54069             delete ps.el; // prevent conflict
54070             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
54071         }
54072         else {  // panel object assumed!
54073             lr.add(ps);
54074         }
54075     },
54076     /**
54077      * Adds a xtype elements to the layout.
54078      * <pre><code>
54079
54080 layout.addxtype({
54081        xtype : 'ContentPanel',
54082        region: 'west',
54083        items: [ .... ]
54084    }
54085 );
54086
54087 layout.addxtype({
54088         xtype : 'NestedLayoutPanel',
54089         region: 'west',
54090         layout: {
54091            center: { },
54092            west: { }   
54093         },
54094         items : [ ... list of content panels or nested layout panels.. ]
54095    }
54096 );
54097 </code></pre>
54098      * @param {Object} cfg Xtype definition of item to add.
54099      */
54100     addxtype : function(cfg)
54101     {
54102         // basically accepts a pannel...
54103         // can accept a layout region..!?!?
54104         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
54105         
54106         if (!cfg.xtype.match(/Panel$/)) {
54107             return false;
54108         }
54109         var ret = false;
54110         
54111         if (typeof(cfg.region) == 'undefined') {
54112             Roo.log("Failed to add Panel, region was not set");
54113             Roo.log(cfg);
54114             return false;
54115         }
54116         var region = cfg.region;
54117         delete cfg.region;
54118         
54119           
54120         var xitems = [];
54121         if (cfg.items) {
54122             xitems = cfg.items;
54123             delete cfg.items;
54124         }
54125         var nb = false;
54126         
54127         switch(cfg.xtype) 
54128         {
54129             case 'ContentPanel':  // ContentPanel (el, cfg)
54130             case 'ScrollPanel':  // ContentPanel (el, cfg)
54131             case 'ViewPanel': 
54132                 if(cfg.autoCreate) {
54133                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54134                 } else {
54135                     var el = this.el.createChild();
54136                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
54137                 }
54138                 
54139                 this.add(region, ret);
54140                 break;
54141             
54142             
54143             case 'TreePanel': // our new panel!
54144                 cfg.el = this.el.createChild();
54145                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54146                 this.add(region, ret);
54147                 break;
54148             
54149             case 'NestedLayoutPanel': 
54150                 // create a new Layout (which is  a Border Layout...
54151                 var el = this.el.createChild();
54152                 var clayout = cfg.layout;
54153                 delete cfg.layout;
54154                 clayout.items   = clayout.items  || [];
54155                 // replace this exitems with the clayout ones..
54156                 xitems = clayout.items;
54157                  
54158                 
54159                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
54160                     cfg.background = false;
54161                 }
54162                 var layout = new Roo.BorderLayout(el, clayout);
54163                 
54164                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
54165                 //console.log('adding nested layout panel '  + cfg.toSource());
54166                 this.add(region, ret);
54167                 nb = {}; /// find first...
54168                 break;
54169                 
54170             case 'GridPanel': 
54171             
54172                 // needs grid and region
54173                 
54174                 //var el = this.getRegion(region).el.createChild();
54175                 var el = this.el.createChild();
54176                 // create the grid first...
54177                 
54178                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
54179                 delete cfg.grid;
54180                 if (region == 'center' && this.active ) {
54181                     cfg.background = false;
54182                 }
54183                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
54184                 
54185                 this.add(region, ret);
54186                 if (cfg.background) {
54187                     ret.on('activate', function(gp) {
54188                         if (!gp.grid.rendered) {
54189                             gp.grid.render();
54190                         }
54191                     });
54192                 } else {
54193                     grid.render();
54194                 }
54195                 break;
54196            
54197            
54198            
54199                 
54200                 
54201                 
54202             default:
54203                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
54204                     
54205                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54206                     this.add(region, ret);
54207                 } else {
54208                 
54209                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
54210                     return null;
54211                 }
54212                 
54213              // GridPanel (grid, cfg)
54214             
54215         }
54216         this.beginUpdate();
54217         // add children..
54218         var region = '';
54219         var abn = {};
54220         Roo.each(xitems, function(i)  {
54221             region = nb && i.region ? i.region : false;
54222             
54223             var add = ret.addxtype(i);
54224            
54225             if (region) {
54226                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
54227                 if (!i.background) {
54228                     abn[region] = nb[region] ;
54229                 }
54230             }
54231             
54232         });
54233         this.endUpdate();
54234
54235         // make the last non-background panel active..
54236         //if (nb) { Roo.log(abn); }
54237         if (nb) {
54238             
54239             for(var r in abn) {
54240                 region = this.getRegion(r);
54241                 if (region) {
54242                     // tried using nb[r], but it does not work..
54243                      
54244                     region.showPanel(abn[r]);
54245                    
54246                 }
54247             }
54248         }
54249         return ret;
54250         
54251     }
54252 });
54253
54254 /**
54255  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
54256  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
54257  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
54258  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
54259  * <pre><code>
54260 // shorthand
54261 var CP = Roo.ContentPanel;
54262
54263 var layout = Roo.BorderLayout.create({
54264     north: {
54265         initialSize: 25,
54266         titlebar: false,
54267         panels: [new CP("north", "North")]
54268     },
54269     west: {
54270         split:true,
54271         initialSize: 200,
54272         minSize: 175,
54273         maxSize: 400,
54274         titlebar: true,
54275         collapsible: true,
54276         panels: [new CP("west", {title: "West"})]
54277     },
54278     east: {
54279         split:true,
54280         initialSize: 202,
54281         minSize: 175,
54282         maxSize: 400,
54283         titlebar: true,
54284         collapsible: true,
54285         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
54286     },
54287     south: {
54288         split:true,
54289         initialSize: 100,
54290         minSize: 100,
54291         maxSize: 200,
54292         titlebar: true,
54293         collapsible: true,
54294         panels: [new CP("south", {title: "South", closable: true})]
54295     },
54296     center: {
54297         titlebar: true,
54298         autoScroll:true,
54299         resizeTabs: true,
54300         minTabWidth: 50,
54301         preferredTabWidth: 150,
54302         panels: [
54303             new CP("center1", {title: "Close Me", closable: true}),
54304             new CP("center2", {title: "Center Panel", closable: false})
54305         ]
54306     }
54307 }, document.body);
54308
54309 layout.getRegion("center").showPanel("center1");
54310 </code></pre>
54311  * @param config
54312  * @param targetEl
54313  */
54314 Roo.BorderLayout.create = function(config, targetEl){
54315     var layout = new Roo.BorderLayout(targetEl || document.body, config);
54316     layout.beginUpdate();
54317     var regions = Roo.BorderLayout.RegionFactory.validRegions;
54318     for(var j = 0, jlen = regions.length; j < jlen; j++){
54319         var lr = regions[j];
54320         if(layout.regions[lr] && config[lr].panels){
54321             var r = layout.regions[lr];
54322             var ps = config[lr].panels;
54323             layout.addTypedPanels(r, ps);
54324         }
54325     }
54326     layout.endUpdate();
54327     return layout;
54328 };
54329
54330 // private
54331 Roo.BorderLayout.RegionFactory = {
54332     // private
54333     validRegions : ["north","south","east","west","center"],
54334
54335     // private
54336     create : function(target, mgr, config){
54337         target = target.toLowerCase();
54338         if(config.lightweight || config.basic){
54339             return new Roo.BasicLayoutRegion(mgr, config, target);
54340         }
54341         switch(target){
54342             case "north":
54343                 return new Roo.NorthLayoutRegion(mgr, config);
54344             case "south":
54345                 return new Roo.SouthLayoutRegion(mgr, config);
54346             case "east":
54347                 return new Roo.EastLayoutRegion(mgr, config);
54348             case "west":
54349                 return new Roo.WestLayoutRegion(mgr, config);
54350             case "center":
54351                 return new Roo.CenterLayoutRegion(mgr, config);
54352         }
54353         throw 'Layout region "'+target+'" not supported.';
54354     }
54355 };/*
54356  * Based on:
54357  * Ext JS Library 1.1.1
54358  * Copyright(c) 2006-2007, Ext JS, LLC.
54359  *
54360  * Originally Released Under LGPL - original licence link has changed is not relivant.
54361  *
54362  * Fork - LGPL
54363  * <script type="text/javascript">
54364  */
54365  
54366 /**
54367  * @class Roo.BasicLayoutRegion
54368  * @extends Roo.util.Observable
54369  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
54370  * and does not have a titlebar, tabs or any other features. All it does is size and position 
54371  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
54372  */
54373 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
54374     this.mgr = mgr;
54375     this.position  = pos;
54376     this.events = {
54377         /**
54378          * @scope Roo.BasicLayoutRegion
54379          */
54380         
54381         /**
54382          * @event beforeremove
54383          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
54384          * @param {Roo.LayoutRegion} this
54385          * @param {Roo.ContentPanel} panel The panel
54386          * @param {Object} e The cancel event object
54387          */
54388         "beforeremove" : true,
54389         /**
54390          * @event invalidated
54391          * Fires when the layout for this region is changed.
54392          * @param {Roo.LayoutRegion} this
54393          */
54394         "invalidated" : true,
54395         /**
54396          * @event visibilitychange
54397          * Fires when this region is shown or hidden 
54398          * @param {Roo.LayoutRegion} this
54399          * @param {Boolean} visibility true or false
54400          */
54401         "visibilitychange" : true,
54402         /**
54403          * @event paneladded
54404          * Fires when a panel is added. 
54405          * @param {Roo.LayoutRegion} this
54406          * @param {Roo.ContentPanel} panel The panel
54407          */
54408         "paneladded" : true,
54409         /**
54410          * @event panelremoved
54411          * Fires when a panel is removed. 
54412          * @param {Roo.LayoutRegion} this
54413          * @param {Roo.ContentPanel} panel The panel
54414          */
54415         "panelremoved" : true,
54416         /**
54417          * @event beforecollapse
54418          * Fires when this region before collapse.
54419          * @param {Roo.LayoutRegion} this
54420          */
54421         "beforecollapse" : true,
54422         /**
54423          * @event collapsed
54424          * Fires when this region is collapsed.
54425          * @param {Roo.LayoutRegion} this
54426          */
54427         "collapsed" : true,
54428         /**
54429          * @event expanded
54430          * Fires when this region is expanded.
54431          * @param {Roo.LayoutRegion} this
54432          */
54433         "expanded" : true,
54434         /**
54435          * @event slideshow
54436          * Fires when this region is slid into view.
54437          * @param {Roo.LayoutRegion} this
54438          */
54439         "slideshow" : true,
54440         /**
54441          * @event slidehide
54442          * Fires when this region slides out of view. 
54443          * @param {Roo.LayoutRegion} this
54444          */
54445         "slidehide" : true,
54446         /**
54447          * @event panelactivated
54448          * Fires when a panel is activated. 
54449          * @param {Roo.LayoutRegion} this
54450          * @param {Roo.ContentPanel} panel The activated panel
54451          */
54452         "panelactivated" : true,
54453         /**
54454          * @event resized
54455          * Fires when the user resizes this region. 
54456          * @param {Roo.LayoutRegion} this
54457          * @param {Number} newSize The new size (width for east/west, height for north/south)
54458          */
54459         "resized" : true
54460     };
54461     /** A collection of panels in this region. @type Roo.util.MixedCollection */
54462     this.panels = new Roo.util.MixedCollection();
54463     this.panels.getKey = this.getPanelId.createDelegate(this);
54464     this.box = null;
54465     this.activePanel = null;
54466     // ensure listeners are added...
54467     
54468     if (config.listeners || config.events) {
54469         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
54470             listeners : config.listeners || {},
54471             events : config.events || {}
54472         });
54473     }
54474     
54475     if(skipConfig !== true){
54476         this.applyConfig(config);
54477     }
54478 };
54479
54480 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
54481     getPanelId : function(p){
54482         return p.getId();
54483     },
54484     
54485     applyConfig : function(config){
54486         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54487         this.config = config;
54488         
54489     },
54490     
54491     /**
54492      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
54493      * the width, for horizontal (north, south) the height.
54494      * @param {Number} newSize The new width or height
54495      */
54496     resizeTo : function(newSize){
54497         var el = this.el ? this.el :
54498                  (this.activePanel ? this.activePanel.getEl() : null);
54499         if(el){
54500             switch(this.position){
54501                 case "east":
54502                 case "west":
54503                     el.setWidth(newSize);
54504                     this.fireEvent("resized", this, newSize);
54505                 break;
54506                 case "north":
54507                 case "south":
54508                     el.setHeight(newSize);
54509                     this.fireEvent("resized", this, newSize);
54510                 break;                
54511             }
54512         }
54513     },
54514     
54515     getBox : function(){
54516         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
54517     },
54518     
54519     getMargins : function(){
54520         return this.margins;
54521     },
54522     
54523     updateBox : function(box){
54524         this.box = box;
54525         var el = this.activePanel.getEl();
54526         el.dom.style.left = box.x + "px";
54527         el.dom.style.top = box.y + "px";
54528         this.activePanel.setSize(box.width, box.height);
54529     },
54530     
54531     /**
54532      * Returns the container element for this region.
54533      * @return {Roo.Element}
54534      */
54535     getEl : function(){
54536         return this.activePanel;
54537     },
54538     
54539     /**
54540      * Returns true if this region is currently visible.
54541      * @return {Boolean}
54542      */
54543     isVisible : function(){
54544         return this.activePanel ? true : false;
54545     },
54546     
54547     setActivePanel : function(panel){
54548         panel = this.getPanel(panel);
54549         if(this.activePanel && this.activePanel != panel){
54550             this.activePanel.setActiveState(false);
54551             this.activePanel.getEl().setLeftTop(-10000,-10000);
54552         }
54553         this.activePanel = panel;
54554         panel.setActiveState(true);
54555         if(this.box){
54556             panel.setSize(this.box.width, this.box.height);
54557         }
54558         this.fireEvent("panelactivated", this, panel);
54559         this.fireEvent("invalidated");
54560     },
54561     
54562     /**
54563      * Show the specified panel.
54564      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
54565      * @return {Roo.ContentPanel} The shown panel or null
54566      */
54567     showPanel : function(panel){
54568         if(panel = this.getPanel(panel)){
54569             this.setActivePanel(panel);
54570         }
54571         return panel;
54572     },
54573     
54574     /**
54575      * Get the active panel for this region.
54576      * @return {Roo.ContentPanel} The active panel or null
54577      */
54578     getActivePanel : function(){
54579         return this.activePanel;
54580     },
54581     
54582     /**
54583      * Add the passed ContentPanel(s)
54584      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54585      * @return {Roo.ContentPanel} The panel added (if only one was added)
54586      */
54587     add : function(panel){
54588         if(arguments.length > 1){
54589             for(var i = 0, len = arguments.length; i < len; i++) {
54590                 this.add(arguments[i]);
54591             }
54592             return null;
54593         }
54594         if(this.hasPanel(panel)){
54595             this.showPanel(panel);
54596             return panel;
54597         }
54598         var el = panel.getEl();
54599         if(el.dom.parentNode != this.mgr.el.dom){
54600             this.mgr.el.dom.appendChild(el.dom);
54601         }
54602         if(panel.setRegion){
54603             panel.setRegion(this);
54604         }
54605         this.panels.add(panel);
54606         el.setStyle("position", "absolute");
54607         if(!panel.background){
54608             this.setActivePanel(panel);
54609             if(this.config.initialSize && this.panels.getCount()==1){
54610                 this.resizeTo(this.config.initialSize);
54611             }
54612         }
54613         this.fireEvent("paneladded", this, panel);
54614         return panel;
54615     },
54616     
54617     /**
54618      * Returns true if the panel is in this region.
54619      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54620      * @return {Boolean}
54621      */
54622     hasPanel : function(panel){
54623         if(typeof panel == "object"){ // must be panel obj
54624             panel = panel.getId();
54625         }
54626         return this.getPanel(panel) ? true : false;
54627     },
54628     
54629     /**
54630      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54631      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54632      * @param {Boolean} preservePanel Overrides the config preservePanel option
54633      * @return {Roo.ContentPanel} The panel that was removed
54634      */
54635     remove : function(panel, preservePanel){
54636         panel = this.getPanel(panel);
54637         if(!panel){
54638             return null;
54639         }
54640         var e = {};
54641         this.fireEvent("beforeremove", this, panel, e);
54642         if(e.cancel === true){
54643             return null;
54644         }
54645         var panelId = panel.getId();
54646         this.panels.removeKey(panelId);
54647         return panel;
54648     },
54649     
54650     /**
54651      * Returns the panel specified or null if it's not in this region.
54652      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54653      * @return {Roo.ContentPanel}
54654      */
54655     getPanel : function(id){
54656         if(typeof id == "object"){ // must be panel obj
54657             return id;
54658         }
54659         return this.panels.get(id);
54660     },
54661     
54662     /**
54663      * Returns this regions position (north/south/east/west/center).
54664      * @return {String} 
54665      */
54666     getPosition: function(){
54667         return this.position;    
54668     }
54669 });/*
54670  * Based on:
54671  * Ext JS Library 1.1.1
54672  * Copyright(c) 2006-2007, Ext JS, LLC.
54673  *
54674  * Originally Released Under LGPL - original licence link has changed is not relivant.
54675  *
54676  * Fork - LGPL
54677  * <script type="text/javascript">
54678  */
54679  
54680 /**
54681  * @class Roo.LayoutRegion
54682  * @extends Roo.BasicLayoutRegion
54683  * This class represents a region in a layout manager.
54684  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
54685  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
54686  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
54687  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
54688  * @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})
54689  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
54690  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
54691  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
54692  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
54693  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
54694  * @cfg {String}    title           The title for the region (overrides panel titles)
54695  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
54696  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
54697  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
54698  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
54699  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
54700  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
54701  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
54702  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
54703  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
54704  * @cfg {Boolean}   showPin         True to show a pin button
54705  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
54706  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
54707  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
54708  * @cfg {Number}    width           For East/West panels
54709  * @cfg {Number}    height          For North/South panels
54710  * @cfg {Boolean}   split           To show the splitter
54711  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
54712  */
54713 Roo.LayoutRegion = function(mgr, config, pos){
54714     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
54715     var dh = Roo.DomHelper;
54716     /** This region's container element 
54717     * @type Roo.Element */
54718     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
54719     /** This region's title element 
54720     * @type Roo.Element */
54721
54722     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
54723         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
54724         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
54725     ]}, true);
54726     this.titleEl.enableDisplayMode();
54727     /** This region's title text element 
54728     * @type HTMLElement */
54729     this.titleTextEl = this.titleEl.dom.firstChild;
54730     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
54731     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
54732     this.closeBtn.enableDisplayMode();
54733     this.closeBtn.on("click", this.closeClicked, this);
54734     this.closeBtn.hide();
54735
54736     this.createBody(config);
54737     this.visible = true;
54738     this.collapsed = false;
54739
54740     if(config.hideWhenEmpty){
54741         this.hide();
54742         this.on("paneladded", this.validateVisibility, this);
54743         this.on("panelremoved", this.validateVisibility, this);
54744     }
54745     this.applyConfig(config);
54746 };
54747
54748 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
54749
54750     createBody : function(){
54751         /** This region's body element 
54752         * @type Roo.Element */
54753         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
54754     },
54755
54756     applyConfig : function(c){
54757         if(c.collapsible && this.position != "center" && !this.collapsedEl){
54758             var dh = Roo.DomHelper;
54759             if(c.titlebar !== false){
54760                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
54761                 this.collapseBtn.on("click", this.collapse, this);
54762                 this.collapseBtn.enableDisplayMode();
54763
54764                 if(c.showPin === true || this.showPin){
54765                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
54766                     this.stickBtn.enableDisplayMode();
54767                     this.stickBtn.on("click", this.expand, this);
54768                     this.stickBtn.hide();
54769                 }
54770             }
54771             /** This region's collapsed element
54772             * @type Roo.Element */
54773             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
54774                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
54775             ]}, true);
54776             if(c.floatable !== false){
54777                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
54778                this.collapsedEl.on("click", this.collapseClick, this);
54779             }
54780
54781             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
54782                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
54783                    id: "message", unselectable: "on", style:{"float":"left"}});
54784                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
54785              }
54786             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
54787             this.expandBtn.on("click", this.expand, this);
54788         }
54789         if(this.collapseBtn){
54790             this.collapseBtn.setVisible(c.collapsible == true);
54791         }
54792         this.cmargins = c.cmargins || this.cmargins ||
54793                          (this.position == "west" || this.position == "east" ?
54794                              {top: 0, left: 2, right:2, bottom: 0} :
54795                              {top: 2, left: 0, right:0, bottom: 2});
54796         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54797         this.bottomTabs = c.tabPosition != "top";
54798         this.autoScroll = c.autoScroll || false;
54799         if(this.autoScroll){
54800             this.bodyEl.setStyle("overflow", "auto");
54801         }else{
54802             this.bodyEl.setStyle("overflow", "hidden");
54803         }
54804         //if(c.titlebar !== false){
54805             if((!c.titlebar && !c.title) || c.titlebar === false){
54806                 this.titleEl.hide();
54807             }else{
54808                 this.titleEl.show();
54809                 if(c.title){
54810                     this.titleTextEl.innerHTML = c.title;
54811                 }
54812             }
54813         //}
54814         this.duration = c.duration || .30;
54815         this.slideDuration = c.slideDuration || .45;
54816         this.config = c;
54817         if(c.collapsed){
54818             this.collapse(true);
54819         }
54820         if(c.hidden){
54821             this.hide();
54822         }
54823     },
54824     /**
54825      * Returns true if this region is currently visible.
54826      * @return {Boolean}
54827      */
54828     isVisible : function(){
54829         return this.visible;
54830     },
54831
54832     /**
54833      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
54834      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
54835      */
54836     setCollapsedTitle : function(title){
54837         title = title || "&#160;";
54838         if(this.collapsedTitleTextEl){
54839             this.collapsedTitleTextEl.innerHTML = title;
54840         }
54841     },
54842
54843     getBox : function(){
54844         var b;
54845         if(!this.collapsed){
54846             b = this.el.getBox(false, true);
54847         }else{
54848             b = this.collapsedEl.getBox(false, true);
54849         }
54850         return b;
54851     },
54852
54853     getMargins : function(){
54854         return this.collapsed ? this.cmargins : this.margins;
54855     },
54856
54857     highlight : function(){
54858         this.el.addClass("x-layout-panel-dragover");
54859     },
54860
54861     unhighlight : function(){
54862         this.el.removeClass("x-layout-panel-dragover");
54863     },
54864
54865     updateBox : function(box){
54866         this.box = box;
54867         if(!this.collapsed){
54868             this.el.dom.style.left = box.x + "px";
54869             this.el.dom.style.top = box.y + "px";
54870             this.updateBody(box.width, box.height);
54871         }else{
54872             this.collapsedEl.dom.style.left = box.x + "px";
54873             this.collapsedEl.dom.style.top = box.y + "px";
54874             this.collapsedEl.setSize(box.width, box.height);
54875         }
54876         if(this.tabs){
54877             this.tabs.autoSizeTabs();
54878         }
54879     },
54880
54881     updateBody : function(w, h){
54882         if(w !== null){
54883             this.el.setWidth(w);
54884             w -= this.el.getBorderWidth("rl");
54885             if(this.config.adjustments){
54886                 w += this.config.adjustments[0];
54887             }
54888         }
54889         if(h !== null){
54890             this.el.setHeight(h);
54891             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
54892             h -= this.el.getBorderWidth("tb");
54893             if(this.config.adjustments){
54894                 h += this.config.adjustments[1];
54895             }
54896             this.bodyEl.setHeight(h);
54897             if(this.tabs){
54898                 h = this.tabs.syncHeight(h);
54899             }
54900         }
54901         if(this.panelSize){
54902             w = w !== null ? w : this.panelSize.width;
54903             h = h !== null ? h : this.panelSize.height;
54904         }
54905         if(this.activePanel){
54906             var el = this.activePanel.getEl();
54907             w = w !== null ? w : el.getWidth();
54908             h = h !== null ? h : el.getHeight();
54909             this.panelSize = {width: w, height: h};
54910             this.activePanel.setSize(w, h);
54911         }
54912         if(Roo.isIE && this.tabs){
54913             this.tabs.el.repaint();
54914         }
54915     },
54916
54917     /**
54918      * Returns the container element for this region.
54919      * @return {Roo.Element}
54920      */
54921     getEl : function(){
54922         return this.el;
54923     },
54924
54925     /**
54926      * Hides this region.
54927      */
54928     hide : function(){
54929         if(!this.collapsed){
54930             this.el.dom.style.left = "-2000px";
54931             this.el.hide();
54932         }else{
54933             this.collapsedEl.dom.style.left = "-2000px";
54934             this.collapsedEl.hide();
54935         }
54936         this.visible = false;
54937         this.fireEvent("visibilitychange", this, false);
54938     },
54939
54940     /**
54941      * Shows this region if it was previously hidden.
54942      */
54943     show : function(){
54944         if(!this.collapsed){
54945             this.el.show();
54946         }else{
54947             this.collapsedEl.show();
54948         }
54949         this.visible = true;
54950         this.fireEvent("visibilitychange", this, true);
54951     },
54952
54953     closeClicked : function(){
54954         if(this.activePanel){
54955             this.remove(this.activePanel);
54956         }
54957     },
54958
54959     collapseClick : function(e){
54960         if(this.isSlid){
54961            e.stopPropagation();
54962            this.slideIn();
54963         }else{
54964            e.stopPropagation();
54965            this.slideOut();
54966         }
54967     },
54968
54969     /**
54970      * Collapses this region.
54971      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
54972      */
54973     collapse : function(skipAnim, skipCheck){
54974         if(this.collapsed) {
54975             return;
54976         }
54977         
54978         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
54979             
54980             this.collapsed = true;
54981             if(this.split){
54982                 this.split.el.hide();
54983             }
54984             if(this.config.animate && skipAnim !== true){
54985                 this.fireEvent("invalidated", this);
54986                 this.animateCollapse();
54987             }else{
54988                 this.el.setLocation(-20000,-20000);
54989                 this.el.hide();
54990                 this.collapsedEl.show();
54991                 this.fireEvent("collapsed", this);
54992                 this.fireEvent("invalidated", this);
54993             }
54994         }
54995         
54996     },
54997
54998     animateCollapse : function(){
54999         // overridden
55000     },
55001
55002     /**
55003      * Expands this region if it was previously collapsed.
55004      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
55005      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
55006      */
55007     expand : function(e, skipAnim){
55008         if(e) {
55009             e.stopPropagation();
55010         }
55011         if(!this.collapsed || this.el.hasActiveFx()) {
55012             return;
55013         }
55014         if(this.isSlid){
55015             this.afterSlideIn();
55016             skipAnim = true;
55017         }
55018         this.collapsed = false;
55019         if(this.config.animate && skipAnim !== true){
55020             this.animateExpand();
55021         }else{
55022             this.el.show();
55023             if(this.split){
55024                 this.split.el.show();
55025             }
55026             this.collapsedEl.setLocation(-2000,-2000);
55027             this.collapsedEl.hide();
55028             this.fireEvent("invalidated", this);
55029             this.fireEvent("expanded", this);
55030         }
55031     },
55032
55033     animateExpand : function(){
55034         // overridden
55035     },
55036
55037     initTabs : function()
55038     {
55039         this.bodyEl.setStyle("overflow", "hidden");
55040         var ts = new Roo.TabPanel(
55041                 this.bodyEl.dom,
55042                 {
55043                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
55044                     disableTooltips: this.config.disableTabTips,
55045                     toolbar : this.config.toolbar
55046                 }
55047         );
55048         if(this.config.hideTabs){
55049             ts.stripWrap.setDisplayed(false);
55050         }
55051         this.tabs = ts;
55052         ts.resizeTabs = this.config.resizeTabs === true;
55053         ts.minTabWidth = this.config.minTabWidth || 40;
55054         ts.maxTabWidth = this.config.maxTabWidth || 250;
55055         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
55056         ts.monitorResize = false;
55057         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55058         ts.bodyEl.addClass('x-layout-tabs-body');
55059         this.panels.each(this.initPanelAsTab, this);
55060     },
55061
55062     initPanelAsTab : function(panel){
55063         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
55064                     this.config.closeOnTab && panel.isClosable());
55065         if(panel.tabTip !== undefined){
55066             ti.setTooltip(panel.tabTip);
55067         }
55068         ti.on("activate", function(){
55069               this.setActivePanel(panel);
55070         }, this);
55071         if(this.config.closeOnTab){
55072             ti.on("beforeclose", function(t, e){
55073                 e.cancel = true;
55074                 this.remove(panel);
55075             }, this);
55076         }
55077         return ti;
55078     },
55079
55080     updatePanelTitle : function(panel, title){
55081         if(this.activePanel == panel){
55082             this.updateTitle(title);
55083         }
55084         if(this.tabs){
55085             var ti = this.tabs.getTab(panel.getEl().id);
55086             ti.setText(title);
55087             if(panel.tabTip !== undefined){
55088                 ti.setTooltip(panel.tabTip);
55089             }
55090         }
55091     },
55092
55093     updateTitle : function(title){
55094         if(this.titleTextEl && !this.config.title){
55095             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
55096         }
55097     },
55098
55099     setActivePanel : function(panel){
55100         panel = this.getPanel(panel);
55101         if(this.activePanel && this.activePanel != panel){
55102             this.activePanel.setActiveState(false);
55103         }
55104         this.activePanel = panel;
55105         panel.setActiveState(true);
55106         if(this.panelSize){
55107             panel.setSize(this.panelSize.width, this.panelSize.height);
55108         }
55109         if(this.closeBtn){
55110             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
55111         }
55112         this.updateTitle(panel.getTitle());
55113         if(this.tabs){
55114             this.fireEvent("invalidated", this);
55115         }
55116         this.fireEvent("panelactivated", this, panel);
55117     },
55118
55119     /**
55120      * Shows the specified panel.
55121      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
55122      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
55123      */
55124     showPanel : function(panel)
55125     {
55126         panel = this.getPanel(panel);
55127         if(panel){
55128             if(this.tabs){
55129                 var tab = this.tabs.getTab(panel.getEl().id);
55130                 if(tab.isHidden()){
55131                     this.tabs.unhideTab(tab.id);
55132                 }
55133                 tab.activate();
55134             }else{
55135                 this.setActivePanel(panel);
55136             }
55137         }
55138         return panel;
55139     },
55140
55141     /**
55142      * Get the active panel for this region.
55143      * @return {Roo.ContentPanel} The active panel or null
55144      */
55145     getActivePanel : function(){
55146         return this.activePanel;
55147     },
55148
55149     validateVisibility : function(){
55150         if(this.panels.getCount() < 1){
55151             this.updateTitle("&#160;");
55152             this.closeBtn.hide();
55153             this.hide();
55154         }else{
55155             if(!this.isVisible()){
55156                 this.show();
55157             }
55158         }
55159     },
55160
55161     /**
55162      * Adds the passed ContentPanel(s) to this region.
55163      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55164      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
55165      */
55166     add : function(panel){
55167         if(arguments.length > 1){
55168             for(var i = 0, len = arguments.length; i < len; i++) {
55169                 this.add(arguments[i]);
55170             }
55171             return null;
55172         }
55173         if(this.hasPanel(panel)){
55174             this.showPanel(panel);
55175             return panel;
55176         }
55177         panel.setRegion(this);
55178         this.panels.add(panel);
55179         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
55180             this.bodyEl.dom.appendChild(panel.getEl().dom);
55181             if(panel.background !== true){
55182                 this.setActivePanel(panel);
55183             }
55184             this.fireEvent("paneladded", this, panel);
55185             return panel;
55186         }
55187         if(!this.tabs){
55188             this.initTabs();
55189         }else{
55190             this.initPanelAsTab(panel);
55191         }
55192         if(panel.background !== true){
55193             this.tabs.activate(panel.getEl().id);
55194         }
55195         this.fireEvent("paneladded", this, panel);
55196         return panel;
55197     },
55198
55199     /**
55200      * Hides the tab for the specified panel.
55201      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55202      */
55203     hidePanel : function(panel){
55204         if(this.tabs && (panel = this.getPanel(panel))){
55205             this.tabs.hideTab(panel.getEl().id);
55206         }
55207     },
55208
55209     /**
55210      * Unhides the tab for a previously hidden panel.
55211      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55212      */
55213     unhidePanel : function(panel){
55214         if(this.tabs && (panel = this.getPanel(panel))){
55215             this.tabs.unhideTab(panel.getEl().id);
55216         }
55217     },
55218
55219     clearPanels : function(){
55220         while(this.panels.getCount() > 0){
55221              this.remove(this.panels.first());
55222         }
55223     },
55224
55225     /**
55226      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55227      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55228      * @param {Boolean} preservePanel Overrides the config preservePanel option
55229      * @return {Roo.ContentPanel} The panel that was removed
55230      */
55231     remove : function(panel, preservePanel){
55232         panel = this.getPanel(panel);
55233         if(!panel){
55234             return null;
55235         }
55236         var e = {};
55237         this.fireEvent("beforeremove", this, panel, e);
55238         if(e.cancel === true){
55239             return null;
55240         }
55241         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
55242         var panelId = panel.getId();
55243         this.panels.removeKey(panelId);
55244         if(preservePanel){
55245             document.body.appendChild(panel.getEl().dom);
55246         }
55247         if(this.tabs){
55248             this.tabs.removeTab(panel.getEl().id);
55249         }else if (!preservePanel){
55250             this.bodyEl.dom.removeChild(panel.getEl().dom);
55251         }
55252         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
55253             var p = this.panels.first();
55254             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
55255             tempEl.appendChild(p.getEl().dom);
55256             this.bodyEl.update("");
55257             this.bodyEl.dom.appendChild(p.getEl().dom);
55258             tempEl = null;
55259             this.updateTitle(p.getTitle());
55260             this.tabs = null;
55261             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55262             this.setActivePanel(p);
55263         }
55264         panel.setRegion(null);
55265         if(this.activePanel == panel){
55266             this.activePanel = null;
55267         }
55268         if(this.config.autoDestroy !== false && preservePanel !== true){
55269             try{panel.destroy();}catch(e){}
55270         }
55271         this.fireEvent("panelremoved", this, panel);
55272         return panel;
55273     },
55274
55275     /**
55276      * Returns the TabPanel component used by this region
55277      * @return {Roo.TabPanel}
55278      */
55279     getTabs : function(){
55280         return this.tabs;
55281     },
55282
55283     createTool : function(parentEl, className){
55284         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
55285             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
55286         btn.addClassOnOver("x-layout-tools-button-over");
55287         return btn;
55288     }
55289 });/*
55290  * Based on:
55291  * Ext JS Library 1.1.1
55292  * Copyright(c) 2006-2007, Ext JS, LLC.
55293  *
55294  * Originally Released Under LGPL - original licence link has changed is not relivant.
55295  *
55296  * Fork - LGPL
55297  * <script type="text/javascript">
55298  */
55299  
55300
55301
55302 /**
55303  * @class Roo.SplitLayoutRegion
55304  * @extends Roo.LayoutRegion
55305  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
55306  */
55307 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
55308     this.cursor = cursor;
55309     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
55310 };
55311
55312 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
55313     splitTip : "Drag to resize.",
55314     collapsibleSplitTip : "Drag to resize. Double click to hide.",
55315     useSplitTips : false,
55316
55317     applyConfig : function(config){
55318         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
55319         if(config.split){
55320             if(!this.split){
55321                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
55322                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
55323                 /** The SplitBar for this region 
55324                 * @type Roo.SplitBar */
55325                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
55326                 this.split.on("moved", this.onSplitMove, this);
55327                 this.split.useShim = config.useShim === true;
55328                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
55329                 if(this.useSplitTips){
55330                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
55331                 }
55332                 if(config.collapsible){
55333                     this.split.el.on("dblclick", this.collapse,  this);
55334                 }
55335             }
55336             if(typeof config.minSize != "undefined"){
55337                 this.split.minSize = config.minSize;
55338             }
55339             if(typeof config.maxSize != "undefined"){
55340                 this.split.maxSize = config.maxSize;
55341             }
55342             if(config.hideWhenEmpty || config.hidden || config.collapsed){
55343                 this.hideSplitter();
55344             }
55345         }
55346     },
55347
55348     getHMaxSize : function(){
55349          var cmax = this.config.maxSize || 10000;
55350          var center = this.mgr.getRegion("center");
55351          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
55352     },
55353
55354     getVMaxSize : function(){
55355          var cmax = this.config.maxSize || 10000;
55356          var center = this.mgr.getRegion("center");
55357          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
55358     },
55359
55360     onSplitMove : function(split, newSize){
55361         this.fireEvent("resized", this, newSize);
55362     },
55363     
55364     /** 
55365      * Returns the {@link Roo.SplitBar} for this region.
55366      * @return {Roo.SplitBar}
55367      */
55368     getSplitBar : function(){
55369         return this.split;
55370     },
55371     
55372     hide : function(){
55373         this.hideSplitter();
55374         Roo.SplitLayoutRegion.superclass.hide.call(this);
55375     },
55376
55377     hideSplitter : function(){
55378         if(this.split){
55379             this.split.el.setLocation(-2000,-2000);
55380             this.split.el.hide();
55381         }
55382     },
55383
55384     show : function(){
55385         if(this.split){
55386             this.split.el.show();
55387         }
55388         Roo.SplitLayoutRegion.superclass.show.call(this);
55389     },
55390     
55391     beforeSlide: function(){
55392         if(Roo.isGecko){// firefox overflow auto bug workaround
55393             this.bodyEl.clip();
55394             if(this.tabs) {
55395                 this.tabs.bodyEl.clip();
55396             }
55397             if(this.activePanel){
55398                 this.activePanel.getEl().clip();
55399                 
55400                 if(this.activePanel.beforeSlide){
55401                     this.activePanel.beforeSlide();
55402                 }
55403             }
55404         }
55405     },
55406     
55407     afterSlide : function(){
55408         if(Roo.isGecko){// firefox overflow auto bug workaround
55409             this.bodyEl.unclip();
55410             if(this.tabs) {
55411                 this.tabs.bodyEl.unclip();
55412             }
55413             if(this.activePanel){
55414                 this.activePanel.getEl().unclip();
55415                 if(this.activePanel.afterSlide){
55416                     this.activePanel.afterSlide();
55417                 }
55418             }
55419         }
55420     },
55421
55422     initAutoHide : function(){
55423         if(this.autoHide !== false){
55424             if(!this.autoHideHd){
55425                 var st = new Roo.util.DelayedTask(this.slideIn, this);
55426                 this.autoHideHd = {
55427                     "mouseout": function(e){
55428                         if(!e.within(this.el, true)){
55429                             st.delay(500);
55430                         }
55431                     },
55432                     "mouseover" : function(e){
55433                         st.cancel();
55434                     },
55435                     scope : this
55436                 };
55437             }
55438             this.el.on(this.autoHideHd);
55439         }
55440     },
55441
55442     clearAutoHide : function(){
55443         if(this.autoHide !== false){
55444             this.el.un("mouseout", this.autoHideHd.mouseout);
55445             this.el.un("mouseover", this.autoHideHd.mouseover);
55446         }
55447     },
55448
55449     clearMonitor : function(){
55450         Roo.get(document).un("click", this.slideInIf, this);
55451     },
55452
55453     // these names are backwards but not changed for compat
55454     slideOut : function(){
55455         if(this.isSlid || this.el.hasActiveFx()){
55456             return;
55457         }
55458         this.isSlid = true;
55459         if(this.collapseBtn){
55460             this.collapseBtn.hide();
55461         }
55462         this.closeBtnState = this.closeBtn.getStyle('display');
55463         this.closeBtn.hide();
55464         if(this.stickBtn){
55465             this.stickBtn.show();
55466         }
55467         this.el.show();
55468         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
55469         this.beforeSlide();
55470         this.el.setStyle("z-index", 10001);
55471         this.el.slideIn(this.getSlideAnchor(), {
55472             callback: function(){
55473                 this.afterSlide();
55474                 this.initAutoHide();
55475                 Roo.get(document).on("click", this.slideInIf, this);
55476                 this.fireEvent("slideshow", this);
55477             },
55478             scope: this,
55479             block: true
55480         });
55481     },
55482
55483     afterSlideIn : function(){
55484         this.clearAutoHide();
55485         this.isSlid = false;
55486         this.clearMonitor();
55487         this.el.setStyle("z-index", "");
55488         if(this.collapseBtn){
55489             this.collapseBtn.show();
55490         }
55491         this.closeBtn.setStyle('display', this.closeBtnState);
55492         if(this.stickBtn){
55493             this.stickBtn.hide();
55494         }
55495         this.fireEvent("slidehide", this);
55496     },
55497
55498     slideIn : function(cb){
55499         if(!this.isSlid || this.el.hasActiveFx()){
55500             Roo.callback(cb);
55501             return;
55502         }
55503         this.isSlid = false;
55504         this.beforeSlide();
55505         this.el.slideOut(this.getSlideAnchor(), {
55506             callback: function(){
55507                 this.el.setLeftTop(-10000, -10000);
55508                 this.afterSlide();
55509                 this.afterSlideIn();
55510                 Roo.callback(cb);
55511             },
55512             scope: this,
55513             block: true
55514         });
55515     },
55516     
55517     slideInIf : function(e){
55518         if(!e.within(this.el)){
55519             this.slideIn();
55520         }
55521     },
55522
55523     animateCollapse : function(){
55524         this.beforeSlide();
55525         this.el.setStyle("z-index", 20000);
55526         var anchor = this.getSlideAnchor();
55527         this.el.slideOut(anchor, {
55528             callback : function(){
55529                 this.el.setStyle("z-index", "");
55530                 this.collapsedEl.slideIn(anchor, {duration:.3});
55531                 this.afterSlide();
55532                 this.el.setLocation(-10000,-10000);
55533                 this.el.hide();
55534                 this.fireEvent("collapsed", this);
55535             },
55536             scope: this,
55537             block: true
55538         });
55539     },
55540
55541     animateExpand : function(){
55542         this.beforeSlide();
55543         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
55544         this.el.setStyle("z-index", 20000);
55545         this.collapsedEl.hide({
55546             duration:.1
55547         });
55548         this.el.slideIn(this.getSlideAnchor(), {
55549             callback : function(){
55550                 this.el.setStyle("z-index", "");
55551                 this.afterSlide();
55552                 if(this.split){
55553                     this.split.el.show();
55554                 }
55555                 this.fireEvent("invalidated", this);
55556                 this.fireEvent("expanded", this);
55557             },
55558             scope: this,
55559             block: true
55560         });
55561     },
55562
55563     anchors : {
55564         "west" : "left",
55565         "east" : "right",
55566         "north" : "top",
55567         "south" : "bottom"
55568     },
55569
55570     sanchors : {
55571         "west" : "l",
55572         "east" : "r",
55573         "north" : "t",
55574         "south" : "b"
55575     },
55576
55577     canchors : {
55578         "west" : "tl-tr",
55579         "east" : "tr-tl",
55580         "north" : "tl-bl",
55581         "south" : "bl-tl"
55582     },
55583
55584     getAnchor : function(){
55585         return this.anchors[this.position];
55586     },
55587
55588     getCollapseAnchor : function(){
55589         return this.canchors[this.position];
55590     },
55591
55592     getSlideAnchor : function(){
55593         return this.sanchors[this.position];
55594     },
55595
55596     getAlignAdj : function(){
55597         var cm = this.cmargins;
55598         switch(this.position){
55599             case "west":
55600                 return [0, 0];
55601             break;
55602             case "east":
55603                 return [0, 0];
55604             break;
55605             case "north":
55606                 return [0, 0];
55607             break;
55608             case "south":
55609                 return [0, 0];
55610             break;
55611         }
55612     },
55613
55614     getExpandAdj : function(){
55615         var c = this.collapsedEl, cm = this.cmargins;
55616         switch(this.position){
55617             case "west":
55618                 return [-(cm.right+c.getWidth()+cm.left), 0];
55619             break;
55620             case "east":
55621                 return [cm.right+c.getWidth()+cm.left, 0];
55622             break;
55623             case "north":
55624                 return [0, -(cm.top+cm.bottom+c.getHeight())];
55625             break;
55626             case "south":
55627                 return [0, cm.top+cm.bottom+c.getHeight()];
55628             break;
55629         }
55630     }
55631 });/*
55632  * Based on:
55633  * Ext JS Library 1.1.1
55634  * Copyright(c) 2006-2007, Ext JS, LLC.
55635  *
55636  * Originally Released Under LGPL - original licence link has changed is not relivant.
55637  *
55638  * Fork - LGPL
55639  * <script type="text/javascript">
55640  */
55641 /*
55642  * These classes are private internal classes
55643  */
55644 Roo.CenterLayoutRegion = function(mgr, config){
55645     Roo.LayoutRegion.call(this, mgr, config, "center");
55646     this.visible = true;
55647     this.minWidth = config.minWidth || 20;
55648     this.minHeight = config.minHeight || 20;
55649 };
55650
55651 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
55652     hide : function(){
55653         // center panel can't be hidden
55654     },
55655     
55656     show : function(){
55657         // center panel can't be hidden
55658     },
55659     
55660     getMinWidth: function(){
55661         return this.minWidth;
55662     },
55663     
55664     getMinHeight: function(){
55665         return this.minHeight;
55666     }
55667 });
55668
55669
55670 Roo.NorthLayoutRegion = function(mgr, config){
55671     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
55672     if(this.split){
55673         this.split.placement = Roo.SplitBar.TOP;
55674         this.split.orientation = Roo.SplitBar.VERTICAL;
55675         this.split.el.addClass("x-layout-split-v");
55676     }
55677     var size = config.initialSize || config.height;
55678     if(typeof size != "undefined"){
55679         this.el.setHeight(size);
55680     }
55681 };
55682 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
55683     orientation: Roo.SplitBar.VERTICAL,
55684     getBox : function(){
55685         if(this.collapsed){
55686             return this.collapsedEl.getBox();
55687         }
55688         var box = this.el.getBox();
55689         if(this.split){
55690             box.height += this.split.el.getHeight();
55691         }
55692         return box;
55693     },
55694     
55695     updateBox : function(box){
55696         if(this.split && !this.collapsed){
55697             box.height -= this.split.el.getHeight();
55698             this.split.el.setLeft(box.x);
55699             this.split.el.setTop(box.y+box.height);
55700             this.split.el.setWidth(box.width);
55701         }
55702         if(this.collapsed){
55703             this.updateBody(box.width, null);
55704         }
55705         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55706     }
55707 });
55708
55709 Roo.SouthLayoutRegion = function(mgr, config){
55710     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
55711     if(this.split){
55712         this.split.placement = Roo.SplitBar.BOTTOM;
55713         this.split.orientation = Roo.SplitBar.VERTICAL;
55714         this.split.el.addClass("x-layout-split-v");
55715     }
55716     var size = config.initialSize || config.height;
55717     if(typeof size != "undefined"){
55718         this.el.setHeight(size);
55719     }
55720 };
55721 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
55722     orientation: Roo.SplitBar.VERTICAL,
55723     getBox : function(){
55724         if(this.collapsed){
55725             return this.collapsedEl.getBox();
55726         }
55727         var box = this.el.getBox();
55728         if(this.split){
55729             var sh = this.split.el.getHeight();
55730             box.height += sh;
55731             box.y -= sh;
55732         }
55733         return box;
55734     },
55735     
55736     updateBox : function(box){
55737         if(this.split && !this.collapsed){
55738             var sh = this.split.el.getHeight();
55739             box.height -= sh;
55740             box.y += sh;
55741             this.split.el.setLeft(box.x);
55742             this.split.el.setTop(box.y-sh);
55743             this.split.el.setWidth(box.width);
55744         }
55745         if(this.collapsed){
55746             this.updateBody(box.width, null);
55747         }
55748         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55749     }
55750 });
55751
55752 Roo.EastLayoutRegion = function(mgr, config){
55753     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
55754     if(this.split){
55755         this.split.placement = Roo.SplitBar.RIGHT;
55756         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55757         this.split.el.addClass("x-layout-split-h");
55758     }
55759     var size = config.initialSize || config.width;
55760     if(typeof size != "undefined"){
55761         this.el.setWidth(size);
55762     }
55763 };
55764 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
55765     orientation: Roo.SplitBar.HORIZONTAL,
55766     getBox : function(){
55767         if(this.collapsed){
55768             return this.collapsedEl.getBox();
55769         }
55770         var box = this.el.getBox();
55771         if(this.split){
55772             var sw = this.split.el.getWidth();
55773             box.width += sw;
55774             box.x -= sw;
55775         }
55776         return box;
55777     },
55778
55779     updateBox : function(box){
55780         if(this.split && !this.collapsed){
55781             var sw = this.split.el.getWidth();
55782             box.width -= sw;
55783             this.split.el.setLeft(box.x);
55784             this.split.el.setTop(box.y);
55785             this.split.el.setHeight(box.height);
55786             box.x += sw;
55787         }
55788         if(this.collapsed){
55789             this.updateBody(null, box.height);
55790         }
55791         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55792     }
55793 });
55794
55795 Roo.WestLayoutRegion = function(mgr, config){
55796     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
55797     if(this.split){
55798         this.split.placement = Roo.SplitBar.LEFT;
55799         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55800         this.split.el.addClass("x-layout-split-h");
55801     }
55802     var size = config.initialSize || config.width;
55803     if(typeof size != "undefined"){
55804         this.el.setWidth(size);
55805     }
55806 };
55807 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
55808     orientation: Roo.SplitBar.HORIZONTAL,
55809     getBox : function(){
55810         if(this.collapsed){
55811             return this.collapsedEl.getBox();
55812         }
55813         var box = this.el.getBox();
55814         if(this.split){
55815             box.width += this.split.el.getWidth();
55816         }
55817         return box;
55818     },
55819     
55820     updateBox : function(box){
55821         if(this.split && !this.collapsed){
55822             var sw = this.split.el.getWidth();
55823             box.width -= sw;
55824             this.split.el.setLeft(box.x+box.width);
55825             this.split.el.setTop(box.y);
55826             this.split.el.setHeight(box.height);
55827         }
55828         if(this.collapsed){
55829             this.updateBody(null, box.height);
55830         }
55831         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55832     }
55833 });
55834 /*
55835  * Based on:
55836  * Ext JS Library 1.1.1
55837  * Copyright(c) 2006-2007, Ext JS, LLC.
55838  *
55839  * Originally Released Under LGPL - original licence link has changed is not relivant.
55840  *
55841  * Fork - LGPL
55842  * <script type="text/javascript">
55843  */
55844  
55845  
55846 /*
55847  * Private internal class for reading and applying state
55848  */
55849 Roo.LayoutStateManager = function(layout){
55850      // default empty state
55851      this.state = {
55852         north: {},
55853         south: {},
55854         east: {},
55855         west: {}       
55856     };
55857 };
55858
55859 Roo.LayoutStateManager.prototype = {
55860     init : function(layout, provider){
55861         this.provider = provider;
55862         var state = provider.get(layout.id+"-layout-state");
55863         if(state){
55864             var wasUpdating = layout.isUpdating();
55865             if(!wasUpdating){
55866                 layout.beginUpdate();
55867             }
55868             for(var key in state){
55869                 if(typeof state[key] != "function"){
55870                     var rstate = state[key];
55871                     var r = layout.getRegion(key);
55872                     if(r && rstate){
55873                         if(rstate.size){
55874                             r.resizeTo(rstate.size);
55875                         }
55876                         if(rstate.collapsed == true){
55877                             r.collapse(true);
55878                         }else{
55879                             r.expand(null, true);
55880                         }
55881                     }
55882                 }
55883             }
55884             if(!wasUpdating){
55885                 layout.endUpdate();
55886             }
55887             this.state = state; 
55888         }
55889         this.layout = layout;
55890         layout.on("regionresized", this.onRegionResized, this);
55891         layout.on("regioncollapsed", this.onRegionCollapsed, this);
55892         layout.on("regionexpanded", this.onRegionExpanded, this);
55893     },
55894     
55895     storeState : function(){
55896         this.provider.set(this.layout.id+"-layout-state", this.state);
55897     },
55898     
55899     onRegionResized : function(region, newSize){
55900         this.state[region.getPosition()].size = newSize;
55901         this.storeState();
55902     },
55903     
55904     onRegionCollapsed : function(region){
55905         this.state[region.getPosition()].collapsed = true;
55906         this.storeState();
55907     },
55908     
55909     onRegionExpanded : function(region){
55910         this.state[region.getPosition()].collapsed = false;
55911         this.storeState();
55912     }
55913 };/*
55914  * Based on:
55915  * Ext JS Library 1.1.1
55916  * Copyright(c) 2006-2007, Ext JS, LLC.
55917  *
55918  * Originally Released Under LGPL - original licence link has changed is not relivant.
55919  *
55920  * Fork - LGPL
55921  * <script type="text/javascript">
55922  */
55923 /**
55924  * @class Roo.ContentPanel
55925  * @extends Roo.util.Observable
55926  * @children Roo.form.Form Roo.JsonView Roo.View
55927  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
55928  * A basic ContentPanel element.
55929  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
55930  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
55931  * @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
55932  * @cfg {Boolean}   closable      True if the panel can be closed/removed
55933  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
55934  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
55935  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
55936  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
55937  * @cfg {String} title          The title for this panel
55938  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
55939  * @cfg {String} url            Calls {@link #setUrl} with this value
55940  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
55941  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
55942  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
55943  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
55944  * @cfg {String}    style  Extra style to add to the content panel
55945  * @cfg {Roo.menu.Menu} menu  popup menu
55946
55947  * @constructor
55948  * Create a new ContentPanel.
55949  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
55950  * @param {String/Object} config A string to set only the title or a config object
55951  * @param {String} content (optional) Set the HTML content for this panel
55952  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
55953  */
55954 Roo.ContentPanel = function(el, config, content){
55955     
55956      
55957     /*
55958     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
55959         config = el;
55960         el = Roo.id();
55961     }
55962     if (config && config.parentLayout) { 
55963         el = config.parentLayout.el.createChild(); 
55964     }
55965     */
55966     if(el.autoCreate){ // xtype is available if this is called from factory
55967         config = el;
55968         el = Roo.id();
55969     }
55970     this.el = Roo.get(el);
55971     if(!this.el && config && config.autoCreate){
55972         if(typeof config.autoCreate == "object"){
55973             if(!config.autoCreate.id){
55974                 config.autoCreate.id = config.id||el;
55975             }
55976             this.el = Roo.DomHelper.append(document.body,
55977                         config.autoCreate, true);
55978         }else{
55979             this.el = Roo.DomHelper.append(document.body,
55980                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
55981         }
55982     }
55983     
55984     
55985     this.closable = false;
55986     this.loaded = false;
55987     this.active = false;
55988     if(typeof config == "string"){
55989         this.title = config;
55990     }else{
55991         Roo.apply(this, config);
55992     }
55993     
55994     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
55995         this.wrapEl = this.el.wrap();
55996         this.toolbar.container = this.el.insertSibling(false, 'before');
55997         this.toolbar = new Roo.Toolbar(this.toolbar);
55998     }
55999     
56000     // xtype created footer. - not sure if will work as we normally have to render first..
56001     if (this.footer && !this.footer.el && this.footer.xtype) {
56002         if (!this.wrapEl) {
56003             this.wrapEl = this.el.wrap();
56004         }
56005     
56006         this.footer.container = this.wrapEl.createChild();
56007          
56008         this.footer = Roo.factory(this.footer, Roo);
56009         
56010     }
56011     
56012     if(this.resizeEl){
56013         this.resizeEl = Roo.get(this.resizeEl, true);
56014     }else{
56015         this.resizeEl = this.el;
56016     }
56017     // handle view.xtype
56018     
56019  
56020     
56021     
56022     this.addEvents({
56023         /**
56024          * @event activate
56025          * Fires when this panel is activated. 
56026          * @param {Roo.ContentPanel} this
56027          */
56028         "activate" : true,
56029         /**
56030          * @event deactivate
56031          * Fires when this panel is activated. 
56032          * @param {Roo.ContentPanel} this
56033          */
56034         "deactivate" : true,
56035
56036         /**
56037          * @event resize
56038          * Fires when this panel is resized if fitToFrame is true.
56039          * @param {Roo.ContentPanel} this
56040          * @param {Number} width The width after any component adjustments
56041          * @param {Number} height The height after any component adjustments
56042          */
56043         "resize" : true,
56044         
56045          /**
56046          * @event render
56047          * Fires when this tab is created
56048          * @param {Roo.ContentPanel} this
56049          */
56050         "render" : true
56051          
56052         
56053     });
56054     
56055
56056     
56057     
56058     if(this.autoScroll){
56059         this.resizeEl.setStyle("overflow", "auto");
56060     } else {
56061         // fix randome scrolling
56062         this.el.on('scroll', function() {
56063             Roo.log('fix random scolling');
56064             this.scrollTo('top',0); 
56065         });
56066     }
56067     content = content || this.content;
56068     if(content){
56069         this.setContent(content);
56070     }
56071     if(config && config.url){
56072         this.setUrl(this.url, this.params, this.loadOnce);
56073     }
56074     
56075     
56076     
56077     Roo.ContentPanel.superclass.constructor.call(this);
56078     
56079     if (this.view && typeof(this.view.xtype) != 'undefined') {
56080         this.view.el = this.el.appendChild(document.createElement("div"));
56081         this.view = Roo.factory(this.view); 
56082         this.view.render  &&  this.view.render(false, '');  
56083     }
56084     
56085     
56086     this.fireEvent('render', this);
56087 };
56088
56089 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
56090     tabTip:'',
56091     setRegion : function(region){
56092         this.region = region;
56093         if(region){
56094            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
56095         }else{
56096            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
56097         } 
56098     },
56099     
56100     /**
56101      * Returns the toolbar for this Panel if one was configured. 
56102      * @return {Roo.Toolbar} 
56103      */
56104     getToolbar : function(){
56105         return this.toolbar;
56106     },
56107     
56108     setActiveState : function(active){
56109         this.active = active;
56110         if(!active){
56111             this.fireEvent("deactivate", this);
56112         }else{
56113             this.fireEvent("activate", this);
56114         }
56115     },
56116     /**
56117      * Updates this panel's element
56118      * @param {String} content The new content
56119      * @param {Boolean} loadScripts (optional) true to look for and process scripts
56120     */
56121     setContent : function(content, loadScripts){
56122         this.el.update(content, loadScripts);
56123     },
56124
56125     ignoreResize : function(w, h){
56126         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
56127             return true;
56128         }else{
56129             this.lastSize = {width: w, height: h};
56130             return false;
56131         }
56132     },
56133     /**
56134      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
56135      * @return {Roo.UpdateManager} The UpdateManager
56136      */
56137     getUpdateManager : function(){
56138         return this.el.getUpdateManager();
56139     },
56140      /**
56141      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
56142      * @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:
56143 <pre><code>
56144 panel.load({
56145     url: "your-url.php",
56146     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
56147     callback: yourFunction,
56148     scope: yourObject, //(optional scope)
56149     discardUrl: false,
56150     nocache: false,
56151     text: "Loading...",
56152     timeout: 30,
56153     scripts: false
56154 });
56155 </code></pre>
56156      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
56157      * 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.
56158      * @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}
56159      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
56160      * @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.
56161      * @return {Roo.ContentPanel} this
56162      */
56163     load : function(){
56164         var um = this.el.getUpdateManager();
56165         um.update.apply(um, arguments);
56166         return this;
56167     },
56168
56169
56170     /**
56171      * 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.
56172      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
56173      * @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)
56174      * @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)
56175      * @return {Roo.UpdateManager} The UpdateManager
56176      */
56177     setUrl : function(url, params, loadOnce){
56178         if(this.refreshDelegate){
56179             this.removeListener("activate", this.refreshDelegate);
56180         }
56181         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
56182         this.on("activate", this.refreshDelegate);
56183         return this.el.getUpdateManager();
56184     },
56185     
56186     _handleRefresh : function(url, params, loadOnce){
56187         if(!loadOnce || !this.loaded){
56188             var updater = this.el.getUpdateManager();
56189             updater.update(url, params, this._setLoaded.createDelegate(this));
56190         }
56191     },
56192     
56193     _setLoaded : function(){
56194         this.loaded = true;
56195     }, 
56196     
56197     /**
56198      * Returns this panel's id
56199      * @return {String} 
56200      */
56201     getId : function(){
56202         return this.el.id;
56203     },
56204     
56205     /** 
56206      * Returns this panel's element - used by regiosn to add.
56207      * @return {Roo.Element} 
56208      */
56209     getEl : function(){
56210         return this.wrapEl || this.el;
56211     },
56212     
56213     adjustForComponents : function(width, height)
56214     {
56215         //Roo.log('adjustForComponents ');
56216         if(this.resizeEl != this.el){
56217             width -= this.el.getFrameWidth('lr');
56218             height -= this.el.getFrameWidth('tb');
56219         }
56220         if(this.toolbar){
56221             var te = this.toolbar.getEl();
56222             height -= te.getHeight();
56223             te.setWidth(width);
56224         }
56225         if(this.footer){
56226             var te = this.footer.getEl();
56227             //Roo.log("footer:" + te.getHeight());
56228             
56229             height -= te.getHeight();
56230             te.setWidth(width);
56231         }
56232         
56233         
56234         if(this.adjustments){
56235             width += this.adjustments[0];
56236             height += this.adjustments[1];
56237         }
56238         return {"width": width, "height": height};
56239     },
56240     
56241     setSize : function(width, height){
56242         if(this.fitToFrame && !this.ignoreResize(width, height)){
56243             if(this.fitContainer && this.resizeEl != this.el){
56244                 this.el.setSize(width, height);
56245             }
56246             var size = this.adjustForComponents(width, height);
56247             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
56248             this.fireEvent('resize', this, size.width, size.height);
56249         }
56250     },
56251     
56252     /**
56253      * Returns this panel's title
56254      * @return {String} 
56255      */
56256     getTitle : function(){
56257         return this.title;
56258     },
56259     
56260     /**
56261      * Set this panel's title
56262      * @param {String} title
56263      */
56264     setTitle : function(title){
56265         this.title = title;
56266         if(this.region){
56267             this.region.updatePanelTitle(this, title);
56268         }
56269     },
56270     
56271     /**
56272      * Returns true is this panel was configured to be closable
56273      * @return {Boolean} 
56274      */
56275     isClosable : function(){
56276         return this.closable;
56277     },
56278     
56279     beforeSlide : function(){
56280         this.el.clip();
56281         this.resizeEl.clip();
56282     },
56283     
56284     afterSlide : function(){
56285         this.el.unclip();
56286         this.resizeEl.unclip();
56287     },
56288     
56289     /**
56290      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
56291      *   Will fail silently if the {@link #setUrl} method has not been called.
56292      *   This does not activate the panel, just updates its content.
56293      */
56294     refresh : function(){
56295         if(this.refreshDelegate){
56296            this.loaded = false;
56297            this.refreshDelegate();
56298         }
56299     },
56300     
56301     /**
56302      * Destroys this panel
56303      */
56304     destroy : function(){
56305         this.el.removeAllListeners();
56306         var tempEl = document.createElement("span");
56307         tempEl.appendChild(this.el.dom);
56308         tempEl.innerHTML = "";
56309         this.el.remove();
56310         this.el = null;
56311     },
56312     
56313     /**
56314      * form - if the content panel contains a form - this is a reference to it.
56315      * @type {Roo.form.Form}
56316      */
56317     form : false,
56318     /**
56319      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
56320      *    This contains a reference to it.
56321      * @type {Roo.View}
56322      */
56323     view : false,
56324     
56325       /**
56326      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
56327      * <pre><code>
56328
56329 layout.addxtype({
56330        xtype : 'Form',
56331        items: [ .... ]
56332    }
56333 );
56334
56335 </code></pre>
56336      * @param {Object} cfg Xtype definition of item to add.
56337      */
56338     
56339     addxtype : function(cfg) {
56340         // add form..
56341         if (cfg.xtype.match(/^Form$/)) {
56342             
56343             var el;
56344             //if (this.footer) {
56345             //    el = this.footer.container.insertSibling(false, 'before');
56346             //} else {
56347                 el = this.el.createChild();
56348             //}
56349
56350             this.form = new  Roo.form.Form(cfg);
56351             
56352             
56353             if ( this.form.allItems.length) {
56354                 this.form.render(el.dom);
56355             }
56356             return this.form;
56357         }
56358         // should only have one of theses..
56359         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
56360             // views.. should not be just added - used named prop 'view''
56361             
56362             cfg.el = this.el.appendChild(document.createElement("div"));
56363             // factory?
56364             
56365             var ret = new Roo.factory(cfg);
56366              
56367              ret.render && ret.render(false, ''); // render blank..
56368             this.view = ret;
56369             return ret;
56370         }
56371         return false;
56372     }
56373 });
56374
56375 /**
56376  * @class Roo.GridPanel
56377  * @extends Roo.ContentPanel
56378  * @constructor
56379  * Create a new GridPanel.
56380  * @param {Roo.grid.Grid} grid The grid for this panel
56381  * @param {String/Object} config A string to set only the panel's title, or a config object
56382  */
56383 Roo.GridPanel = function(grid, config){
56384     
56385   
56386     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
56387         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
56388         
56389     this.wrapper.dom.appendChild(grid.getGridEl().dom);
56390     
56391     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
56392     
56393     if(this.toolbar){
56394         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
56395     }
56396     // xtype created footer. - not sure if will work as we normally have to render first..
56397     if (this.footer && !this.footer.el && this.footer.xtype) {
56398         
56399         this.footer.container = this.grid.getView().getFooterPanel(true);
56400         this.footer.dataSource = this.grid.dataSource;
56401         this.footer = Roo.factory(this.footer, Roo);
56402         
56403     }
56404     
56405     grid.monitorWindowResize = false; // turn off autosizing
56406     grid.autoHeight = false;
56407     grid.autoWidth = false;
56408     this.grid = grid;
56409     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
56410 };
56411
56412 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
56413     getId : function(){
56414         return this.grid.id;
56415     },
56416     
56417     /**
56418      * Returns the grid for this panel
56419      * @return {Roo.grid.Grid} 
56420      */
56421     getGrid : function(){
56422         return this.grid;    
56423     },
56424     
56425     setSize : function(width, height){
56426         if(!this.ignoreResize(width, height)){
56427             var grid = this.grid;
56428             var size = this.adjustForComponents(width, height);
56429             grid.getGridEl().setSize(size.width, size.height);
56430             grid.autoSize();
56431         }
56432     },
56433     
56434     beforeSlide : function(){
56435         this.grid.getView().scroller.clip();
56436     },
56437     
56438     afterSlide : function(){
56439         this.grid.getView().scroller.unclip();
56440     },
56441     
56442     destroy : function(){
56443         this.grid.destroy();
56444         delete this.grid;
56445         Roo.GridPanel.superclass.destroy.call(this); 
56446     }
56447 });
56448
56449
56450 /**
56451  * @class Roo.NestedLayoutPanel
56452  * @extends Roo.ContentPanel
56453  * @constructor
56454  * Create a new NestedLayoutPanel.
56455  * 
56456  * 
56457  * @param {Roo.BorderLayout} layout [required] The layout for this panel
56458  * @param {String/Object} config A string to set only the title or a config object
56459  */
56460 Roo.NestedLayoutPanel = function(layout, config)
56461 {
56462     // construct with only one argument..
56463     /* FIXME - implement nicer consturctors
56464     if (layout.layout) {
56465         config = layout;
56466         layout = config.layout;
56467         delete config.layout;
56468     }
56469     if (layout.xtype && !layout.getEl) {
56470         // then layout needs constructing..
56471         layout = Roo.factory(layout, Roo);
56472     }
56473     */
56474     
56475     
56476     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
56477     
56478     layout.monitorWindowResize = false; // turn off autosizing
56479     this.layout = layout;
56480     this.layout.getEl().addClass("x-layout-nested-layout");
56481     
56482     
56483     
56484     
56485 };
56486
56487 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
56488
56489     setSize : function(width, height){
56490         if(!this.ignoreResize(width, height)){
56491             var size = this.adjustForComponents(width, height);
56492             var el = this.layout.getEl();
56493             el.setSize(size.width, size.height);
56494             var touch = el.dom.offsetWidth;
56495             this.layout.layout();
56496             // ie requires a double layout on the first pass
56497             if(Roo.isIE && !this.initialized){
56498                 this.initialized = true;
56499                 this.layout.layout();
56500             }
56501         }
56502     },
56503     
56504     // activate all subpanels if not currently active..
56505     
56506     setActiveState : function(active){
56507         this.active = active;
56508         if(!active){
56509             this.fireEvent("deactivate", this);
56510             return;
56511         }
56512         
56513         this.fireEvent("activate", this);
56514         // not sure if this should happen before or after..
56515         if (!this.layout) {
56516             return; // should not happen..
56517         }
56518         var reg = false;
56519         for (var r in this.layout.regions) {
56520             reg = this.layout.getRegion(r);
56521             if (reg.getActivePanel()) {
56522                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
56523                 reg.setActivePanel(reg.getActivePanel());
56524                 continue;
56525             }
56526             if (!reg.panels.length) {
56527                 continue;
56528             }
56529             reg.showPanel(reg.getPanel(0));
56530         }
56531         
56532         
56533         
56534         
56535     },
56536     
56537     /**
56538      * Returns the nested BorderLayout for this panel
56539      * @return {Roo.BorderLayout} 
56540      */
56541     getLayout : function(){
56542         return this.layout;
56543     },
56544     
56545      /**
56546      * Adds a xtype elements to the layout of the nested panel
56547      * <pre><code>
56548
56549 panel.addxtype({
56550        xtype : 'ContentPanel',
56551        region: 'west',
56552        items: [ .... ]
56553    }
56554 );
56555
56556 panel.addxtype({
56557         xtype : 'NestedLayoutPanel',
56558         region: 'west',
56559         layout: {
56560            center: { },
56561            west: { }   
56562         },
56563         items : [ ... list of content panels or nested layout panels.. ]
56564    }
56565 );
56566 </code></pre>
56567      * @param {Object} cfg Xtype definition of item to add.
56568      */
56569     addxtype : function(cfg) {
56570         return this.layout.addxtype(cfg);
56571     
56572     }
56573 });
56574
56575 Roo.ScrollPanel = function(el, config, content){
56576     config = config || {};
56577     config.fitToFrame = true;
56578     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
56579     
56580     this.el.dom.style.overflow = "hidden";
56581     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
56582     this.el.removeClass("x-layout-inactive-content");
56583     this.el.on("mousewheel", this.onWheel, this);
56584
56585     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
56586     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
56587     up.unselectable(); down.unselectable();
56588     up.on("click", this.scrollUp, this);
56589     down.on("click", this.scrollDown, this);
56590     up.addClassOnOver("x-scroller-btn-over");
56591     down.addClassOnOver("x-scroller-btn-over");
56592     up.addClassOnClick("x-scroller-btn-click");
56593     down.addClassOnClick("x-scroller-btn-click");
56594     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
56595
56596     this.resizeEl = this.el;
56597     this.el = wrap; this.up = up; this.down = down;
56598 };
56599
56600 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
56601     increment : 100,
56602     wheelIncrement : 5,
56603     scrollUp : function(){
56604         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
56605     },
56606
56607     scrollDown : function(){
56608         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
56609     },
56610
56611     afterScroll : function(){
56612         var el = this.resizeEl;
56613         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
56614         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56615         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56616     },
56617
56618     setSize : function(){
56619         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
56620         this.afterScroll();
56621     },
56622
56623     onWheel : function(e){
56624         var d = e.getWheelDelta();
56625         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
56626         this.afterScroll();
56627         e.stopEvent();
56628     },
56629
56630     setContent : function(content, loadScripts){
56631         this.resizeEl.update(content, loadScripts);
56632     }
56633
56634 });
56635
56636
56637
56638 /**
56639  * @class Roo.TreePanel
56640  * @extends Roo.ContentPanel
56641  * Treepanel component
56642  * 
56643  * @constructor
56644  * Create a new TreePanel. - defaults to fit/scoll contents.
56645  * @param {String/Object} config A string to set only the panel's title, or a config object
56646  */
56647 Roo.TreePanel = function(config){
56648     var el = config.el;
56649     var tree = config.tree;
56650     delete config.tree; 
56651     delete config.el; // hopefull!
56652     
56653     // wrapper for IE7 strict & safari scroll issue
56654     
56655     var treeEl = el.createChild();
56656     config.resizeEl = treeEl;
56657     
56658     
56659     
56660     Roo.TreePanel.superclass.constructor.call(this, el, config);
56661  
56662  
56663     this.tree = new Roo.tree.TreePanel(treeEl , tree);
56664     //console.log(tree);
56665     this.on('activate', function()
56666     {
56667         if (this.tree.rendered) {
56668             return;
56669         }
56670         //console.log('render tree');
56671         this.tree.render();
56672     });
56673     // this should not be needed.. - it's actually the 'el' that resizes?
56674     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
56675     
56676     //this.on('resize',  function (cp, w, h) {
56677     //        this.tree.innerCt.setWidth(w);
56678     //        this.tree.innerCt.setHeight(h);
56679     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
56680     //});
56681
56682         
56683     
56684 };
56685
56686 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
56687     fitToFrame : true,
56688     autoScroll : true,
56689     /*
56690      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
56691      */
56692     tree : false
56693
56694 });
56695
56696
56697
56698
56699
56700
56701
56702
56703
56704
56705
56706 /*
56707  * Based on:
56708  * Ext JS Library 1.1.1
56709  * Copyright(c) 2006-2007, Ext JS, LLC.
56710  *
56711  * Originally Released Under LGPL - original licence link has changed is not relivant.
56712  *
56713  * Fork - LGPL
56714  * <script type="text/javascript">
56715  */
56716  
56717
56718 /**
56719  * @class Roo.ReaderLayout
56720  * @extends Roo.BorderLayout
56721  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
56722  * center region containing two nested regions (a top one for a list view and one for item preview below),
56723  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
56724  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
56725  * expedites the setup of the overall layout and regions for this common application style.
56726  * Example:
56727  <pre><code>
56728 var reader = new Roo.ReaderLayout();
56729 var CP = Roo.ContentPanel;  // shortcut for adding
56730
56731 reader.beginUpdate();
56732 reader.add("north", new CP("north", "North"));
56733 reader.add("west", new CP("west", {title: "West"}));
56734 reader.add("east", new CP("east", {title: "East"}));
56735
56736 reader.regions.listView.add(new CP("listView", "List"));
56737 reader.regions.preview.add(new CP("preview", "Preview"));
56738 reader.endUpdate();
56739 </code></pre>
56740 * @constructor
56741 * Create a new ReaderLayout
56742 * @param {Object} config Configuration options
56743 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
56744 * document.body if omitted)
56745 */
56746 Roo.ReaderLayout = function(config, renderTo){
56747     var c = config || {size:{}};
56748     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
56749         north: c.north !== false ? Roo.apply({
56750             split:false,
56751             initialSize: 32,
56752             titlebar: false
56753         }, c.north) : false,
56754         west: c.west !== false ? Roo.apply({
56755             split:true,
56756             initialSize: 200,
56757             minSize: 175,
56758             maxSize: 400,
56759             titlebar: true,
56760             collapsible: true,
56761             animate: true,
56762             margins:{left:5,right:0,bottom:5,top:5},
56763             cmargins:{left:5,right:5,bottom:5,top:5}
56764         }, c.west) : false,
56765         east: c.east !== false ? Roo.apply({
56766             split:true,
56767             initialSize: 200,
56768             minSize: 175,
56769             maxSize: 400,
56770             titlebar: true,
56771             collapsible: true,
56772             animate: true,
56773             margins:{left:0,right:5,bottom:5,top:5},
56774             cmargins:{left:5,right:5,bottom:5,top:5}
56775         }, c.east) : false,
56776         center: Roo.apply({
56777             tabPosition: 'top',
56778             autoScroll:false,
56779             closeOnTab: true,
56780             titlebar:false,
56781             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
56782         }, c.center)
56783     });
56784
56785     this.el.addClass('x-reader');
56786
56787     this.beginUpdate();
56788
56789     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
56790         south: c.preview !== false ? Roo.apply({
56791             split:true,
56792             initialSize: 200,
56793             minSize: 100,
56794             autoScroll:true,
56795             collapsible:true,
56796             titlebar: true,
56797             cmargins:{top:5,left:0, right:0, bottom:0}
56798         }, c.preview) : false,
56799         center: Roo.apply({
56800             autoScroll:false,
56801             titlebar:false,
56802             minHeight:200
56803         }, c.listView)
56804     });
56805     this.add('center', new Roo.NestedLayoutPanel(inner,
56806             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
56807
56808     this.endUpdate();
56809
56810     this.regions.preview = inner.getRegion('south');
56811     this.regions.listView = inner.getRegion('center');
56812 };
56813
56814 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
56815  * Based on:
56816  * Ext JS Library 1.1.1
56817  * Copyright(c) 2006-2007, Ext JS, LLC.
56818  *
56819  * Originally Released Under LGPL - original licence link has changed is not relivant.
56820  *
56821  * Fork - LGPL
56822  * <script type="text/javascript">
56823  */
56824  
56825 /**
56826  * @class Roo.grid.Grid
56827  * @extends Roo.util.Observable
56828  * This class represents the primary interface of a component based grid control.
56829  * <br><br>Usage:<pre><code>
56830  var grid = new Roo.grid.Grid("my-container-id", {
56831      ds: myDataStore,
56832      cm: myColModel,
56833      selModel: mySelectionModel,
56834      autoSizeColumns: true,
56835      monitorWindowResize: false,
56836      trackMouseOver: true
56837  });
56838  // set any options
56839  grid.render();
56840  * </code></pre>
56841  * <b>Common Problems:</b><br/>
56842  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
56843  * element will correct this<br/>
56844  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
56845  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
56846  * are unpredictable.<br/>
56847  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
56848  * grid to calculate dimensions/offsets.<br/>
56849   * @constructor
56850  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56851  * The container MUST have some type of size defined for the grid to fill. The container will be
56852  * automatically set to position relative if it isn't already.
56853  * @param {Object} config A config object that sets properties on this grid.
56854  */
56855 Roo.grid.Grid = function(container, config){
56856         // initialize the container
56857         this.container = Roo.get(container);
56858         this.container.update("");
56859         this.container.setStyle("overflow", "hidden");
56860     this.container.addClass('x-grid-container');
56861
56862     this.id = this.container.id;
56863
56864     Roo.apply(this, config);
56865     // check and correct shorthanded configs
56866     if(this.ds){
56867         this.dataSource = this.ds;
56868         delete this.ds;
56869     }
56870     if(this.cm){
56871         this.colModel = this.cm;
56872         delete this.cm;
56873     }
56874     if(this.sm){
56875         this.selModel = this.sm;
56876         delete this.sm;
56877     }
56878
56879     if (this.selModel) {
56880         this.selModel = Roo.factory(this.selModel, Roo.grid);
56881         this.sm = this.selModel;
56882         this.sm.xmodule = this.xmodule || false;
56883     }
56884     if (typeof(this.colModel.config) == 'undefined') {
56885         this.colModel = new Roo.grid.ColumnModel(this.colModel);
56886         this.cm = this.colModel;
56887         this.cm.xmodule = this.xmodule || false;
56888     }
56889     if (this.dataSource) {
56890         this.dataSource= Roo.factory(this.dataSource, Roo.data);
56891         this.ds = this.dataSource;
56892         this.ds.xmodule = this.xmodule || false;
56893          
56894     }
56895     
56896     
56897     
56898     if(this.width){
56899         this.container.setWidth(this.width);
56900     }
56901
56902     if(this.height){
56903         this.container.setHeight(this.height);
56904     }
56905     /** @private */
56906         this.addEvents({
56907         // raw events
56908         /**
56909          * @event click
56910          * The raw click event for the entire grid.
56911          * @param {Roo.EventObject} e
56912          */
56913         "click" : true,
56914         /**
56915          * @event dblclick
56916          * The raw dblclick event for the entire grid.
56917          * @param {Roo.EventObject} e
56918          */
56919         "dblclick" : true,
56920         /**
56921          * @event contextmenu
56922          * The raw contextmenu event for the entire grid.
56923          * @param {Roo.EventObject} e
56924          */
56925         "contextmenu" : true,
56926         /**
56927          * @event mousedown
56928          * The raw mousedown event for the entire grid.
56929          * @param {Roo.EventObject} e
56930          */
56931         "mousedown" : true,
56932         /**
56933          * @event mouseup
56934          * The raw mouseup event for the entire grid.
56935          * @param {Roo.EventObject} e
56936          */
56937         "mouseup" : true,
56938         /**
56939          * @event mouseover
56940          * The raw mouseover event for the entire grid.
56941          * @param {Roo.EventObject} e
56942          */
56943         "mouseover" : true,
56944         /**
56945          * @event mouseout
56946          * The raw mouseout event for the entire grid.
56947          * @param {Roo.EventObject} e
56948          */
56949         "mouseout" : true,
56950         /**
56951          * @event keypress
56952          * The raw keypress event for the entire grid.
56953          * @param {Roo.EventObject} e
56954          */
56955         "keypress" : true,
56956         /**
56957          * @event keydown
56958          * The raw keydown event for the entire grid.
56959          * @param {Roo.EventObject} e
56960          */
56961         "keydown" : true,
56962
56963         // custom events
56964
56965         /**
56966          * @event cellclick
56967          * Fires when a cell is clicked
56968          * @param {Grid} this
56969          * @param {Number} rowIndex
56970          * @param {Number} columnIndex
56971          * @param {Roo.EventObject} e
56972          */
56973         "cellclick" : true,
56974         /**
56975          * @event celldblclick
56976          * Fires when a cell is double clicked
56977          * @param {Grid} this
56978          * @param {Number} rowIndex
56979          * @param {Number} columnIndex
56980          * @param {Roo.EventObject} e
56981          */
56982         "celldblclick" : true,
56983         /**
56984          * @event rowclick
56985          * Fires when a row is clicked
56986          * @param {Grid} this
56987          * @param {Number} rowIndex
56988          * @param {Roo.EventObject} e
56989          */
56990         "rowclick" : true,
56991         /**
56992          * @event rowdblclick
56993          * Fires when a row is double clicked
56994          * @param {Grid} this
56995          * @param {Number} rowIndex
56996          * @param {Roo.EventObject} e
56997          */
56998         "rowdblclick" : true,
56999         /**
57000          * @event headerclick
57001          * Fires when a header is clicked
57002          * @param {Grid} this
57003          * @param {Number} columnIndex
57004          * @param {Roo.EventObject} e
57005          */
57006         "headerclick" : true,
57007         /**
57008          * @event headerdblclick
57009          * Fires when a header cell is double clicked
57010          * @param {Grid} this
57011          * @param {Number} columnIndex
57012          * @param {Roo.EventObject} e
57013          */
57014         "headerdblclick" : true,
57015         /**
57016          * @event rowcontextmenu
57017          * Fires when a row is right clicked
57018          * @param {Grid} this
57019          * @param {Number} rowIndex
57020          * @param {Roo.EventObject} e
57021          */
57022         "rowcontextmenu" : true,
57023         /**
57024          * @event cellcontextmenu
57025          * Fires when a cell is right clicked
57026          * @param {Grid} this
57027          * @param {Number} rowIndex
57028          * @param {Number} cellIndex
57029          * @param {Roo.EventObject} e
57030          */
57031          "cellcontextmenu" : true,
57032         /**
57033          * @event headercontextmenu
57034          * Fires when a header is right clicked
57035          * @param {Grid} this
57036          * @param {Number} columnIndex
57037          * @param {Roo.EventObject} e
57038          */
57039         "headercontextmenu" : true,
57040         /**
57041          * @event bodyscroll
57042          * Fires when the body element is scrolled
57043          * @param {Number} scrollLeft
57044          * @param {Number} scrollTop
57045          */
57046         "bodyscroll" : true,
57047         /**
57048          * @event columnresize
57049          * Fires when the user resizes a column
57050          * @param {Number} columnIndex
57051          * @param {Number} newSize
57052          */
57053         "columnresize" : true,
57054         /**
57055          * @event columnmove
57056          * Fires when the user moves a column
57057          * @param {Number} oldIndex
57058          * @param {Number} newIndex
57059          */
57060         "columnmove" : true,
57061         /**
57062          * @event startdrag
57063          * Fires when row(s) start being dragged
57064          * @param {Grid} this
57065          * @param {Roo.GridDD} dd The drag drop object
57066          * @param {event} e The raw browser event
57067          */
57068         "startdrag" : true,
57069         /**
57070          * @event enddrag
57071          * Fires when a drag operation is complete
57072          * @param {Grid} this
57073          * @param {Roo.GridDD} dd The drag drop object
57074          * @param {event} e The raw browser event
57075          */
57076         "enddrag" : true,
57077         /**
57078          * @event dragdrop
57079          * Fires when dragged row(s) are dropped on a valid DD target
57080          * @param {Grid} this
57081          * @param {Roo.GridDD} dd The drag drop object
57082          * @param {String} targetId The target drag drop object
57083          * @param {event} e The raw browser event
57084          */
57085         "dragdrop" : true,
57086         /**
57087          * @event dragover
57088          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57089          * @param {Grid} this
57090          * @param {Roo.GridDD} dd The drag drop object
57091          * @param {String} targetId The target drag drop object
57092          * @param {event} e The raw browser event
57093          */
57094         "dragover" : true,
57095         /**
57096          * @event dragenter
57097          *  Fires when the dragged row(s) first cross another DD target while being dragged
57098          * @param {Grid} this
57099          * @param {Roo.GridDD} dd The drag drop object
57100          * @param {String} targetId The target drag drop object
57101          * @param {event} e The raw browser event
57102          */
57103         "dragenter" : true,
57104         /**
57105          * @event dragout
57106          * Fires when the dragged row(s) leave another DD target while being dragged
57107          * @param {Grid} this
57108          * @param {Roo.GridDD} dd The drag drop object
57109          * @param {String} targetId The target drag drop object
57110          * @param {event} e The raw browser event
57111          */
57112         "dragout" : true,
57113         /**
57114          * @event rowclass
57115          * Fires when a row is rendered, so you can change add a style to it.
57116          * @param {GridView} gridview   The grid view
57117          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57118          */
57119         'rowclass' : true,
57120
57121         /**
57122          * @event render
57123          * Fires when the grid is rendered
57124          * @param {Grid} grid
57125          */
57126         'render' : true
57127     });
57128
57129     Roo.grid.Grid.superclass.constructor.call(this);
57130 };
57131 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
57132     
57133     /**
57134          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
57135          */
57136         /**
57137          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
57138          */
57139         /**
57140          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
57141          */
57142         /**
57143          * @cfg {Roo.grid.Store} ds The data store for the grid
57144          */
57145         /**
57146          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
57147          */
57148         /**
57149      * @cfg {String} ddGroup - drag drop group.
57150      */
57151       /**
57152      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
57153      */
57154
57155     /**
57156      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
57157      */
57158     minColumnWidth : 25,
57159
57160     /**
57161      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
57162      * <b>on initial render.</b> It is more efficient to explicitly size the columns
57163      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
57164      */
57165     autoSizeColumns : false,
57166
57167     /**
57168      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
57169      */
57170     autoSizeHeaders : true,
57171
57172     /**
57173      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
57174      */
57175     monitorWindowResize : true,
57176
57177     /**
57178      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
57179      * rows measured to get a columns size. Default is 0 (all rows).
57180      */
57181     maxRowsToMeasure : 0,
57182
57183     /**
57184      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
57185      */
57186     trackMouseOver : true,
57187
57188     /**
57189     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
57190     */
57191       /**
57192     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
57193     */
57194     
57195     /**
57196     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
57197     */
57198     enableDragDrop : false,
57199     
57200     /**
57201     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
57202     */
57203     enableColumnMove : true,
57204     
57205     /**
57206     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
57207     */
57208     enableColumnHide : true,
57209     
57210     /**
57211     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
57212     */
57213     enableRowHeightSync : false,
57214     
57215     /**
57216     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
57217     */
57218     stripeRows : true,
57219     
57220     /**
57221     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
57222     */
57223     autoHeight : false,
57224
57225     /**
57226      * @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.
57227      */
57228     autoExpandColumn : false,
57229
57230     /**
57231     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
57232     * Default is 50.
57233     */
57234     autoExpandMin : 50,
57235
57236     /**
57237     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
57238     */
57239     autoExpandMax : 1000,
57240
57241     /**
57242     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
57243     */
57244     view : null,
57245
57246     /**
57247     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
57248     */
57249     loadMask : false,
57250     /**
57251     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
57252     */
57253     dropTarget: false,
57254     
57255    
57256     
57257     // private
57258     rendered : false,
57259
57260     /**
57261     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
57262     * of a fixed width. Default is false.
57263     */
57264     /**
57265     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
57266     */
57267     
57268     
57269     /**
57270     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
57271     * %0 is replaced with the number of selected rows.
57272     */
57273     ddText : "{0} selected row{1}",
57274     
57275     
57276     /**
57277      * Called once after all setup has been completed and the grid is ready to be rendered.
57278      * @return {Roo.grid.Grid} this
57279      */
57280     render : function()
57281     {
57282         var c = this.container;
57283         // try to detect autoHeight/width mode
57284         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
57285             this.autoHeight = true;
57286         }
57287         var view = this.getView();
57288         view.init(this);
57289
57290         c.on("click", this.onClick, this);
57291         c.on("dblclick", this.onDblClick, this);
57292         c.on("contextmenu", this.onContextMenu, this);
57293         c.on("keydown", this.onKeyDown, this);
57294         if (Roo.isTouch) {
57295             c.on("touchstart", this.onTouchStart, this);
57296         }
57297
57298         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
57299
57300         this.getSelectionModel().init(this);
57301
57302         view.render();
57303
57304         if(this.loadMask){
57305             this.loadMask = new Roo.LoadMask(this.container,
57306                     Roo.apply({store:this.dataSource}, this.loadMask));
57307         }
57308         
57309         
57310         if (this.toolbar && this.toolbar.xtype) {
57311             this.toolbar.container = this.getView().getHeaderPanel(true);
57312             this.toolbar = new Roo.Toolbar(this.toolbar);
57313         }
57314         if (this.footer && this.footer.xtype) {
57315             this.footer.dataSource = this.getDataSource();
57316             this.footer.container = this.getView().getFooterPanel(true);
57317             this.footer = Roo.factory(this.footer, Roo);
57318         }
57319         if (this.dropTarget && this.dropTarget.xtype) {
57320             delete this.dropTarget.xtype;
57321             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
57322         }
57323         
57324         
57325         this.rendered = true;
57326         this.fireEvent('render', this);
57327         return this;
57328     },
57329
57330     /**
57331      * Reconfigures the grid to use a different Store and Column Model.
57332      * The View will be bound to the new objects and refreshed.
57333      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
57334      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
57335      */
57336     reconfigure : function(dataSource, colModel){
57337         if(this.loadMask){
57338             this.loadMask.destroy();
57339             this.loadMask = new Roo.LoadMask(this.container,
57340                     Roo.apply({store:dataSource}, this.loadMask));
57341         }
57342         this.view.bind(dataSource, colModel);
57343         this.dataSource = dataSource;
57344         this.colModel = colModel;
57345         this.view.refresh(true);
57346     },
57347     /**
57348      * addColumns
57349      * Add's a column, default at the end..
57350      
57351      * @param {int} position to add (default end)
57352      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
57353      */
57354     addColumns : function(pos, ar)
57355     {
57356         
57357         for (var i =0;i< ar.length;i++) {
57358             var cfg = ar[i];
57359             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
57360             this.cm.lookup[cfg.id] = cfg;
57361         }
57362         
57363         
57364         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
57365             pos = this.cm.config.length; //this.cm.config.push(cfg);
57366         } 
57367         pos = Math.max(0,pos);
57368         ar.unshift(0);
57369         ar.unshift(pos);
57370         this.cm.config.splice.apply(this.cm.config, ar);
57371         
57372         
57373         
57374         this.view.generateRules(this.cm);
57375         this.view.refresh(true);
57376         
57377     },
57378     
57379     
57380     
57381     
57382     // private
57383     onKeyDown : function(e){
57384         this.fireEvent("keydown", e);
57385     },
57386
57387     /**
57388      * Destroy this grid.
57389      * @param {Boolean} removeEl True to remove the element
57390      */
57391     destroy : function(removeEl, keepListeners){
57392         if(this.loadMask){
57393             this.loadMask.destroy();
57394         }
57395         var c = this.container;
57396         c.removeAllListeners();
57397         this.view.destroy();
57398         this.colModel.purgeListeners();
57399         if(!keepListeners){
57400             this.purgeListeners();
57401         }
57402         c.update("");
57403         if(removeEl === true){
57404             c.remove();
57405         }
57406     },
57407
57408     // private
57409     processEvent : function(name, e){
57410         // does this fire select???
57411         //Roo.log('grid:processEvent '  + name);
57412         
57413         if (name != 'touchstart' ) {
57414             this.fireEvent(name, e);    
57415         }
57416         
57417         var t = e.getTarget();
57418         var v = this.view;
57419         var header = v.findHeaderIndex(t);
57420         if(header !== false){
57421             var ename = name == 'touchstart' ? 'click' : name;
57422              
57423             this.fireEvent("header" + ename, this, header, e);
57424         }else{
57425             var row = v.findRowIndex(t);
57426             var cell = v.findCellIndex(t);
57427             if (name == 'touchstart') {
57428                 // first touch is always a click.
57429                 // hopefull this happens after selection is updated.?
57430                 name = false;
57431                 
57432                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
57433                     var cs = this.selModel.getSelectedCell();
57434                     if (row == cs[0] && cell == cs[1]){
57435                         name = 'dblclick';
57436                     }
57437                 }
57438                 if (typeof(this.selModel.getSelections) != 'undefined') {
57439                     var cs = this.selModel.getSelections();
57440                     var ds = this.dataSource;
57441                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
57442                         name = 'dblclick';
57443                     }
57444                 }
57445                 if (!name) {
57446                     return;
57447                 }
57448             }
57449             
57450             
57451             if(row !== false){
57452                 this.fireEvent("row" + name, this, row, e);
57453                 if(cell !== false){
57454                     this.fireEvent("cell" + name, this, row, cell, e);
57455                 }
57456             }
57457         }
57458     },
57459
57460     // private
57461     onClick : function(e){
57462         this.processEvent("click", e);
57463     },
57464    // private
57465     onTouchStart : function(e){
57466         this.processEvent("touchstart", e);
57467     },
57468
57469     // private
57470     onContextMenu : function(e, t){
57471         this.processEvent("contextmenu", e);
57472     },
57473
57474     // private
57475     onDblClick : function(e){
57476         this.processEvent("dblclick", e);
57477     },
57478
57479     // private
57480     walkCells : function(row, col, step, fn, scope){
57481         var cm = this.colModel, clen = cm.getColumnCount();
57482         var ds = this.dataSource, rlen = ds.getCount(), first = true;
57483         if(step < 0){
57484             if(col < 0){
57485                 row--;
57486                 first = false;
57487             }
57488             while(row >= 0){
57489                 if(!first){
57490                     col = clen-1;
57491                 }
57492                 first = false;
57493                 while(col >= 0){
57494                     if(fn.call(scope || this, row, col, cm) === true){
57495                         return [row, col];
57496                     }
57497                     col--;
57498                 }
57499                 row--;
57500             }
57501         } else {
57502             if(col >= clen){
57503                 row++;
57504                 first = false;
57505             }
57506             while(row < rlen){
57507                 if(!first){
57508                     col = 0;
57509                 }
57510                 first = false;
57511                 while(col < clen){
57512                     if(fn.call(scope || this, row, col, cm) === true){
57513                         return [row, col];
57514                     }
57515                     col++;
57516                 }
57517                 row++;
57518             }
57519         }
57520         return null;
57521     },
57522
57523     // private
57524     getSelections : function(){
57525         return this.selModel.getSelections();
57526     },
57527
57528     /**
57529      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
57530      * but if manual update is required this method will initiate it.
57531      */
57532     autoSize : function(){
57533         if(this.rendered){
57534             this.view.layout();
57535             if(this.view.adjustForScroll){
57536                 this.view.adjustForScroll();
57537             }
57538         }
57539     },
57540
57541     /**
57542      * Returns the grid's underlying element.
57543      * @return {Element} The element
57544      */
57545     getGridEl : function(){
57546         return this.container;
57547     },
57548
57549     // private for compatibility, overridden by editor grid
57550     stopEditing : function(){},
57551
57552     /**
57553      * Returns the grid's SelectionModel.
57554      * @return {SelectionModel}
57555      */
57556     getSelectionModel : function(){
57557         if(!this.selModel){
57558             this.selModel = new Roo.grid.RowSelectionModel();
57559         }
57560         return this.selModel;
57561     },
57562
57563     /**
57564      * Returns the grid's DataSource.
57565      * @return {DataSource}
57566      */
57567     getDataSource : function(){
57568         return this.dataSource;
57569     },
57570
57571     /**
57572      * Returns the grid's ColumnModel.
57573      * @return {ColumnModel}
57574      */
57575     getColumnModel : function(){
57576         return this.colModel;
57577     },
57578
57579     /**
57580      * Returns the grid's GridView object.
57581      * @return {GridView}
57582      */
57583     getView : function(){
57584         if(!this.view){
57585             this.view = new Roo.grid.GridView(this.viewConfig);
57586             this.relayEvents(this.view, [
57587                 "beforerowremoved", "beforerowsinserted",
57588                 "beforerefresh", "rowremoved",
57589                 "rowsinserted", "rowupdated" ,"refresh"
57590             ]);
57591         }
57592         return this.view;
57593     },
57594     /**
57595      * Called to get grid's drag proxy text, by default returns this.ddText.
57596      * Override this to put something different in the dragged text.
57597      * @return {String}
57598      */
57599     getDragDropText : function(){
57600         var count = this.selModel.getCount();
57601         return String.format(this.ddText, count, count == 1 ? '' : 's');
57602     }
57603 });
57604 /*
57605  * Based on:
57606  * Ext JS Library 1.1.1
57607  * Copyright(c) 2006-2007, Ext JS, LLC.
57608  *
57609  * Originally Released Under LGPL - original licence link has changed is not relivant.
57610  *
57611  * Fork - LGPL
57612  * <script type="text/javascript">
57613  */
57614  /**
57615  * @class Roo.grid.AbstractGridView
57616  * @extends Roo.util.Observable
57617  * @abstract
57618  * Abstract base class for grid Views
57619  * @constructor
57620  */
57621 Roo.grid.AbstractGridView = function(){
57622         this.grid = null;
57623         
57624         this.events = {
57625             "beforerowremoved" : true,
57626             "beforerowsinserted" : true,
57627             "beforerefresh" : true,
57628             "rowremoved" : true,
57629             "rowsinserted" : true,
57630             "rowupdated" : true,
57631             "refresh" : true
57632         };
57633     Roo.grid.AbstractGridView.superclass.constructor.call(this);
57634 };
57635
57636 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
57637     rowClass : "x-grid-row",
57638     cellClass : "x-grid-cell",
57639     tdClass : "x-grid-td",
57640     hdClass : "x-grid-hd",
57641     splitClass : "x-grid-hd-split",
57642     
57643     init: function(grid){
57644         this.grid = grid;
57645                 var cid = this.grid.getGridEl().id;
57646         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
57647         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
57648         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
57649         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
57650         },
57651         
57652     getColumnRenderers : function(){
57653         var renderers = [];
57654         var cm = this.grid.colModel;
57655         var colCount = cm.getColumnCount();
57656         for(var i = 0; i < colCount; i++){
57657             renderers[i] = cm.getRenderer(i);
57658         }
57659         return renderers;
57660     },
57661     
57662     getColumnIds : function(){
57663         var ids = [];
57664         var cm = this.grid.colModel;
57665         var colCount = cm.getColumnCount();
57666         for(var i = 0; i < colCount; i++){
57667             ids[i] = cm.getColumnId(i);
57668         }
57669         return ids;
57670     },
57671     
57672     getDataIndexes : function(){
57673         if(!this.indexMap){
57674             this.indexMap = this.buildIndexMap();
57675         }
57676         return this.indexMap.colToData;
57677     },
57678     
57679     getColumnIndexByDataIndex : function(dataIndex){
57680         if(!this.indexMap){
57681             this.indexMap = this.buildIndexMap();
57682         }
57683         return this.indexMap.dataToCol[dataIndex];
57684     },
57685     
57686     /**
57687      * Set a css style for a column dynamically. 
57688      * @param {Number} colIndex The index of the column
57689      * @param {String} name The css property name
57690      * @param {String} value The css value
57691      */
57692     setCSSStyle : function(colIndex, name, value){
57693         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
57694         Roo.util.CSS.updateRule(selector, name, value);
57695     },
57696     
57697     generateRules : function(cm){
57698         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
57699         Roo.util.CSS.removeStyleSheet(rulesId);
57700         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57701             var cid = cm.getColumnId(i);
57702             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
57703                          this.tdSelector, cid, " {\n}\n",
57704                          this.hdSelector, cid, " {\n}\n",
57705                          this.splitSelector, cid, " {\n}\n");
57706         }
57707         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57708     }
57709 });/*
57710  * Based on:
57711  * Ext JS Library 1.1.1
57712  * Copyright(c) 2006-2007, Ext JS, LLC.
57713  *
57714  * Originally Released Under LGPL - original licence link has changed is not relivant.
57715  *
57716  * Fork - LGPL
57717  * <script type="text/javascript">
57718  */
57719
57720 // private
57721 // This is a support class used internally by the Grid components
57722 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
57723     this.grid = grid;
57724     this.view = grid.getView();
57725     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57726     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
57727     if(hd2){
57728         this.setHandleElId(Roo.id(hd));
57729         this.setOuterHandleElId(Roo.id(hd2));
57730     }
57731     this.scroll = false;
57732 };
57733 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
57734     maxDragWidth: 120,
57735     getDragData : function(e){
57736         var t = Roo.lib.Event.getTarget(e);
57737         var h = this.view.findHeaderCell(t);
57738         if(h){
57739             return {ddel: h.firstChild, header:h};
57740         }
57741         return false;
57742     },
57743
57744     onInitDrag : function(e){
57745         this.view.headersDisabled = true;
57746         var clone = this.dragData.ddel.cloneNode(true);
57747         clone.id = Roo.id();
57748         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
57749         this.proxy.update(clone);
57750         return true;
57751     },
57752
57753     afterValidDrop : function(){
57754         var v = this.view;
57755         setTimeout(function(){
57756             v.headersDisabled = false;
57757         }, 50);
57758     },
57759
57760     afterInvalidDrop : function(){
57761         var v = this.view;
57762         setTimeout(function(){
57763             v.headersDisabled = false;
57764         }, 50);
57765     }
57766 });
57767 /*
57768  * Based on:
57769  * Ext JS Library 1.1.1
57770  * Copyright(c) 2006-2007, Ext JS, LLC.
57771  *
57772  * Originally Released Under LGPL - original licence link has changed is not relivant.
57773  *
57774  * Fork - LGPL
57775  * <script type="text/javascript">
57776  */
57777 // private
57778 // This is a support class used internally by the Grid components
57779 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
57780     this.grid = grid;
57781     this.view = grid.getView();
57782     // split the proxies so they don't interfere with mouse events
57783     this.proxyTop = Roo.DomHelper.append(document.body, {
57784         cls:"col-move-top", html:"&#160;"
57785     }, true);
57786     this.proxyBottom = Roo.DomHelper.append(document.body, {
57787         cls:"col-move-bottom", html:"&#160;"
57788     }, true);
57789     this.proxyTop.hide = this.proxyBottom.hide = function(){
57790         this.setLeftTop(-100,-100);
57791         this.setStyle("visibility", "hidden");
57792     };
57793     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57794     // temporarily disabled
57795     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
57796     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
57797 };
57798 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
57799     proxyOffsets : [-4, -9],
57800     fly: Roo.Element.fly,
57801
57802     getTargetFromEvent : function(e){
57803         var t = Roo.lib.Event.getTarget(e);
57804         var cindex = this.view.findCellIndex(t);
57805         if(cindex !== false){
57806             return this.view.getHeaderCell(cindex);
57807         }
57808         return null;
57809     },
57810
57811     nextVisible : function(h){
57812         var v = this.view, cm = this.grid.colModel;
57813         h = h.nextSibling;
57814         while(h){
57815             if(!cm.isHidden(v.getCellIndex(h))){
57816                 return h;
57817             }
57818             h = h.nextSibling;
57819         }
57820         return null;
57821     },
57822
57823     prevVisible : function(h){
57824         var v = this.view, cm = this.grid.colModel;
57825         h = h.prevSibling;
57826         while(h){
57827             if(!cm.isHidden(v.getCellIndex(h))){
57828                 return h;
57829             }
57830             h = h.prevSibling;
57831         }
57832         return null;
57833     },
57834
57835     positionIndicator : function(h, n, e){
57836         var x = Roo.lib.Event.getPageX(e);
57837         var r = Roo.lib.Dom.getRegion(n.firstChild);
57838         var px, pt, py = r.top + this.proxyOffsets[1];
57839         if((r.right - x) <= (r.right-r.left)/2){
57840             px = r.right+this.view.borderWidth;
57841             pt = "after";
57842         }else{
57843             px = r.left;
57844             pt = "before";
57845         }
57846         var oldIndex = this.view.getCellIndex(h);
57847         var newIndex = this.view.getCellIndex(n);
57848
57849         if(this.grid.colModel.isFixed(newIndex)){
57850             return false;
57851         }
57852
57853         var locked = this.grid.colModel.isLocked(newIndex);
57854
57855         if(pt == "after"){
57856             newIndex++;
57857         }
57858         if(oldIndex < newIndex){
57859             newIndex--;
57860         }
57861         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
57862             return false;
57863         }
57864         px +=  this.proxyOffsets[0];
57865         this.proxyTop.setLeftTop(px, py);
57866         this.proxyTop.show();
57867         if(!this.bottomOffset){
57868             this.bottomOffset = this.view.mainHd.getHeight();
57869         }
57870         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
57871         this.proxyBottom.show();
57872         return pt;
57873     },
57874
57875     onNodeEnter : function(n, dd, e, data){
57876         if(data.header != n){
57877             this.positionIndicator(data.header, n, e);
57878         }
57879     },
57880
57881     onNodeOver : function(n, dd, e, data){
57882         var result = false;
57883         if(data.header != n){
57884             result = this.positionIndicator(data.header, n, e);
57885         }
57886         if(!result){
57887             this.proxyTop.hide();
57888             this.proxyBottom.hide();
57889         }
57890         return result ? this.dropAllowed : this.dropNotAllowed;
57891     },
57892
57893     onNodeOut : function(n, dd, e, data){
57894         this.proxyTop.hide();
57895         this.proxyBottom.hide();
57896     },
57897
57898     onNodeDrop : function(n, dd, e, data){
57899         var h = data.header;
57900         if(h != n){
57901             var cm = this.grid.colModel;
57902             var x = Roo.lib.Event.getPageX(e);
57903             var r = Roo.lib.Dom.getRegion(n.firstChild);
57904             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
57905             var oldIndex = this.view.getCellIndex(h);
57906             var newIndex = this.view.getCellIndex(n);
57907             var locked = cm.isLocked(newIndex);
57908             if(pt == "after"){
57909                 newIndex++;
57910             }
57911             if(oldIndex < newIndex){
57912                 newIndex--;
57913             }
57914             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
57915                 return false;
57916             }
57917             cm.setLocked(oldIndex, locked, true);
57918             cm.moveColumn(oldIndex, newIndex);
57919             this.grid.fireEvent("columnmove", oldIndex, newIndex);
57920             return true;
57921         }
57922         return false;
57923     }
57924 });
57925 /*
57926  * Based on:
57927  * Ext JS Library 1.1.1
57928  * Copyright(c) 2006-2007, Ext JS, LLC.
57929  *
57930  * Originally Released Under LGPL - original licence link has changed is not relivant.
57931  *
57932  * Fork - LGPL
57933  * <script type="text/javascript">
57934  */
57935   
57936 /**
57937  * @class Roo.grid.GridView
57938  * @extends Roo.util.Observable
57939  *
57940  * @constructor
57941  * @param {Object} config
57942  */
57943 Roo.grid.GridView = function(config){
57944     Roo.grid.GridView.superclass.constructor.call(this);
57945     this.el = null;
57946
57947     Roo.apply(this, config);
57948 };
57949
57950 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
57951
57952     unselectable :  'unselectable="on"',
57953     unselectableCls :  'x-unselectable',
57954     
57955     
57956     rowClass : "x-grid-row",
57957
57958     cellClass : "x-grid-col",
57959
57960     tdClass : "x-grid-td",
57961
57962     hdClass : "x-grid-hd",
57963
57964     splitClass : "x-grid-split",
57965
57966     sortClasses : ["sort-asc", "sort-desc"],
57967
57968     enableMoveAnim : false,
57969
57970     hlColor: "C3DAF9",
57971
57972     dh : Roo.DomHelper,
57973
57974     fly : Roo.Element.fly,
57975
57976     css : Roo.util.CSS,
57977
57978     borderWidth: 1,
57979
57980     splitOffset: 3,
57981
57982     scrollIncrement : 22,
57983
57984     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
57985
57986     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
57987
57988     bind : function(ds, cm){
57989         if(this.ds){
57990             this.ds.un("load", this.onLoad, this);
57991             this.ds.un("datachanged", this.onDataChange, this);
57992             this.ds.un("add", this.onAdd, this);
57993             this.ds.un("remove", this.onRemove, this);
57994             this.ds.un("update", this.onUpdate, this);
57995             this.ds.un("clear", this.onClear, this);
57996         }
57997         if(ds){
57998             ds.on("load", this.onLoad, this);
57999             ds.on("datachanged", this.onDataChange, this);
58000             ds.on("add", this.onAdd, this);
58001             ds.on("remove", this.onRemove, this);
58002             ds.on("update", this.onUpdate, this);
58003             ds.on("clear", this.onClear, this);
58004         }
58005         this.ds = ds;
58006
58007         if(this.cm){
58008             this.cm.un("widthchange", this.onColWidthChange, this);
58009             this.cm.un("headerchange", this.onHeaderChange, this);
58010             this.cm.un("hiddenchange", this.onHiddenChange, this);
58011             this.cm.un("columnmoved", this.onColumnMove, this);
58012             this.cm.un("columnlockchange", this.onColumnLock, this);
58013         }
58014         if(cm){
58015             this.generateRules(cm);
58016             cm.on("widthchange", this.onColWidthChange, this);
58017             cm.on("headerchange", this.onHeaderChange, this);
58018             cm.on("hiddenchange", this.onHiddenChange, this);
58019             cm.on("columnmoved", this.onColumnMove, this);
58020             cm.on("columnlockchange", this.onColumnLock, this);
58021         }
58022         this.cm = cm;
58023     },
58024
58025     init: function(grid){
58026         Roo.grid.GridView.superclass.init.call(this, grid);
58027
58028         this.bind(grid.dataSource, grid.colModel);
58029
58030         grid.on("headerclick", this.handleHeaderClick, this);
58031
58032         if(grid.trackMouseOver){
58033             grid.on("mouseover", this.onRowOver, this);
58034             grid.on("mouseout", this.onRowOut, this);
58035         }
58036         grid.cancelTextSelection = function(){};
58037         this.gridId = grid.id;
58038
58039         var tpls = this.templates || {};
58040
58041         if(!tpls.master){
58042             tpls.master = new Roo.Template(
58043                '<div class="x-grid" hidefocus="true">',
58044                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
58045                   '<div class="x-grid-topbar"></div>',
58046                   '<div class="x-grid-scroller"><div></div></div>',
58047                   '<div class="x-grid-locked">',
58048                       '<div class="x-grid-header">{lockedHeader}</div>',
58049                       '<div class="x-grid-body">{lockedBody}</div>',
58050                   "</div>",
58051                   '<div class="x-grid-viewport">',
58052                       '<div class="x-grid-header">{header}</div>',
58053                       '<div class="x-grid-body">{body}</div>',
58054                   "</div>",
58055                   '<div class="x-grid-bottombar"></div>',
58056                  
58057                   '<div class="x-grid-resize-proxy">&#160;</div>',
58058                "</div>"
58059             );
58060             tpls.master.disableformats = true;
58061         }
58062
58063         if(!tpls.header){
58064             tpls.header = new Roo.Template(
58065                '<table border="0" cellspacing="0" cellpadding="0">',
58066                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
58067                "</table>{splits}"
58068             );
58069             tpls.header.disableformats = true;
58070         }
58071         tpls.header.compile();
58072
58073         if(!tpls.hcell){
58074             tpls.hcell = new Roo.Template(
58075                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
58076                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
58077                 "</div></td>"
58078              );
58079              tpls.hcell.disableFormats = true;
58080         }
58081         tpls.hcell.compile();
58082
58083         if(!tpls.hsplit){
58084             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
58085                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
58086             tpls.hsplit.disableFormats = true;
58087         }
58088         tpls.hsplit.compile();
58089
58090         if(!tpls.body){
58091             tpls.body = new Roo.Template(
58092                '<table border="0" cellspacing="0" cellpadding="0">',
58093                "<tbody>{rows}</tbody>",
58094                "</table>"
58095             );
58096             tpls.body.disableFormats = true;
58097         }
58098         tpls.body.compile();
58099
58100         if(!tpls.row){
58101             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
58102             tpls.row.disableFormats = true;
58103         }
58104         tpls.row.compile();
58105
58106         if(!tpls.cell){
58107             tpls.cell = new Roo.Template(
58108                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
58109                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
58110                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
58111                 "</td>"
58112             );
58113             tpls.cell.disableFormats = true;
58114         }
58115         tpls.cell.compile();
58116
58117         this.templates = tpls;
58118     },
58119
58120     // remap these for backwards compat
58121     onColWidthChange : function(){
58122         this.updateColumns.apply(this, arguments);
58123     },
58124     onHeaderChange : function(){
58125         this.updateHeaders.apply(this, arguments);
58126     }, 
58127     onHiddenChange : function(){
58128         this.handleHiddenChange.apply(this, arguments);
58129     },
58130     onColumnMove : function(){
58131         this.handleColumnMove.apply(this, arguments);
58132     },
58133     onColumnLock : function(){
58134         this.handleLockChange.apply(this, arguments);
58135     },
58136
58137     onDataChange : function(){
58138         this.refresh();
58139         this.updateHeaderSortState();
58140     },
58141
58142     onClear : function(){
58143         this.refresh();
58144     },
58145
58146     onUpdate : function(ds, record){
58147         this.refreshRow(record);
58148     },
58149
58150     refreshRow : function(record){
58151         var ds = this.ds, index;
58152         if(typeof record == 'number'){
58153             index = record;
58154             record = ds.getAt(index);
58155         }else{
58156             index = ds.indexOf(record);
58157         }
58158         this.insertRows(ds, index, index, true);
58159         this.onRemove(ds, record, index+1, true);
58160         this.syncRowHeights(index, index);
58161         this.layout();
58162         this.fireEvent("rowupdated", this, index, record);
58163     },
58164
58165     onAdd : function(ds, records, index){
58166         this.insertRows(ds, index, index + (records.length-1));
58167     },
58168
58169     onRemove : function(ds, record, index, isUpdate){
58170         if(isUpdate !== true){
58171             this.fireEvent("beforerowremoved", this, index, record);
58172         }
58173         var bt = this.getBodyTable(), lt = this.getLockedTable();
58174         if(bt.rows[index]){
58175             bt.firstChild.removeChild(bt.rows[index]);
58176         }
58177         if(lt.rows[index]){
58178             lt.firstChild.removeChild(lt.rows[index]);
58179         }
58180         if(isUpdate !== true){
58181             this.stripeRows(index);
58182             this.syncRowHeights(index, index);
58183             this.layout();
58184             this.fireEvent("rowremoved", this, index, record);
58185         }
58186     },
58187
58188     onLoad : function(){
58189         this.scrollToTop();
58190     },
58191
58192     /**
58193      * Scrolls the grid to the top
58194      */
58195     scrollToTop : function(){
58196         if(this.scroller){
58197             this.scroller.dom.scrollTop = 0;
58198             this.syncScroll();
58199         }
58200     },
58201
58202     /**
58203      * Gets a panel in the header of the grid that can be used for toolbars etc.
58204      * After modifying the contents of this panel a call to grid.autoSize() may be
58205      * required to register any changes in size.
58206      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
58207      * @return Roo.Element
58208      */
58209     getHeaderPanel : function(doShow){
58210         if(doShow){
58211             this.headerPanel.show();
58212         }
58213         return this.headerPanel;
58214     },
58215
58216     /**
58217      * Gets a panel in the footer of the grid that can be used for toolbars etc.
58218      * After modifying the contents of this panel a call to grid.autoSize() may be
58219      * required to register any changes in size.
58220      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
58221      * @return Roo.Element
58222      */
58223     getFooterPanel : function(doShow){
58224         if(doShow){
58225             this.footerPanel.show();
58226         }
58227         return this.footerPanel;
58228     },
58229
58230     initElements : function(){
58231         var E = Roo.Element;
58232         var el = this.grid.getGridEl().dom.firstChild;
58233         var cs = el.childNodes;
58234
58235         this.el = new E(el);
58236         
58237          this.focusEl = new E(el.firstChild);
58238         this.focusEl.swallowEvent("click", true);
58239         
58240         this.headerPanel = new E(cs[1]);
58241         this.headerPanel.enableDisplayMode("block");
58242
58243         this.scroller = new E(cs[2]);
58244         this.scrollSizer = new E(this.scroller.dom.firstChild);
58245
58246         this.lockedWrap = new E(cs[3]);
58247         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
58248         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
58249
58250         this.mainWrap = new E(cs[4]);
58251         this.mainHd = new E(this.mainWrap.dom.firstChild);
58252         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
58253
58254         this.footerPanel = new E(cs[5]);
58255         this.footerPanel.enableDisplayMode("block");
58256
58257         this.resizeProxy = new E(cs[6]);
58258
58259         this.headerSelector = String.format(
58260            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
58261            this.lockedHd.id, this.mainHd.id
58262         );
58263
58264         this.splitterSelector = String.format(
58265            '#{0} div.x-grid-split, #{1} div.x-grid-split',
58266            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
58267         );
58268     },
58269     idToCssName : function(s)
58270     {
58271         return s.replace(/[^a-z0-9]+/ig, '-');
58272     },
58273
58274     getHeaderCell : function(index){
58275         return Roo.DomQuery.select(this.headerSelector)[index];
58276     },
58277
58278     getHeaderCellMeasure : function(index){
58279         return this.getHeaderCell(index).firstChild;
58280     },
58281
58282     getHeaderCellText : function(index){
58283         return this.getHeaderCell(index).firstChild.firstChild;
58284     },
58285
58286     getLockedTable : function(){
58287         return this.lockedBody.dom.firstChild;
58288     },
58289
58290     getBodyTable : function(){
58291         return this.mainBody.dom.firstChild;
58292     },
58293
58294     getLockedRow : function(index){
58295         return this.getLockedTable().rows[index];
58296     },
58297
58298     getRow : function(index){
58299         return this.getBodyTable().rows[index];
58300     },
58301
58302     getRowComposite : function(index){
58303         if(!this.rowEl){
58304             this.rowEl = new Roo.CompositeElementLite();
58305         }
58306         var els = [], lrow, mrow;
58307         if(lrow = this.getLockedRow(index)){
58308             els.push(lrow);
58309         }
58310         if(mrow = this.getRow(index)){
58311             els.push(mrow);
58312         }
58313         this.rowEl.elements = els;
58314         return this.rowEl;
58315     },
58316     /**
58317      * Gets the 'td' of the cell
58318      * 
58319      * @param {Integer} rowIndex row to select
58320      * @param {Integer} colIndex column to select
58321      * 
58322      * @return {Object} 
58323      */
58324     getCell : function(rowIndex, colIndex){
58325         var locked = this.cm.getLockedCount();
58326         var source;
58327         if(colIndex < locked){
58328             source = this.lockedBody.dom.firstChild;
58329         }else{
58330             source = this.mainBody.dom.firstChild;
58331             colIndex -= locked;
58332         }
58333         return source.rows[rowIndex].childNodes[colIndex];
58334     },
58335
58336     getCellText : function(rowIndex, colIndex){
58337         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
58338     },
58339
58340     getCellBox : function(cell){
58341         var b = this.fly(cell).getBox();
58342         if(Roo.isOpera){ // opera fails to report the Y
58343             b.y = cell.offsetTop + this.mainBody.getY();
58344         }
58345         return b;
58346     },
58347
58348     getCellIndex : function(cell){
58349         var id = String(cell.className).match(this.cellRE);
58350         if(id){
58351             return parseInt(id[1], 10);
58352         }
58353         return 0;
58354     },
58355
58356     findHeaderIndex : function(n){
58357         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
58358         return r ? this.getCellIndex(r) : false;
58359     },
58360
58361     findHeaderCell : function(n){
58362         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
58363         return r ? r : false;
58364     },
58365
58366     findRowIndex : function(n){
58367         if(!n){
58368             return false;
58369         }
58370         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
58371         return r ? r.rowIndex : false;
58372     },
58373
58374     findCellIndex : function(node){
58375         var stop = this.el.dom;
58376         while(node && node != stop){
58377             if(this.findRE.test(node.className)){
58378                 return this.getCellIndex(node);
58379             }
58380             node = node.parentNode;
58381         }
58382         return false;
58383     },
58384
58385     getColumnId : function(index){
58386         return this.cm.getColumnId(index);
58387     },
58388
58389     getSplitters : function()
58390     {
58391         if(this.splitterSelector){
58392            return Roo.DomQuery.select(this.splitterSelector);
58393         }else{
58394             return null;
58395       }
58396     },
58397
58398     getSplitter : function(index){
58399         return this.getSplitters()[index];
58400     },
58401
58402     onRowOver : function(e, t){
58403         var row;
58404         if((row = this.findRowIndex(t)) !== false){
58405             this.getRowComposite(row).addClass("x-grid-row-over");
58406         }
58407     },
58408
58409     onRowOut : function(e, t){
58410         var row;
58411         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
58412             this.getRowComposite(row).removeClass("x-grid-row-over");
58413         }
58414     },
58415
58416     renderHeaders : function(){
58417         var cm = this.cm;
58418         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
58419         var cb = [], lb = [], sb = [], lsb = [], p = {};
58420         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58421             p.cellId = "x-grid-hd-0-" + i;
58422             p.splitId = "x-grid-csplit-0-" + i;
58423             p.id = cm.getColumnId(i);
58424             p.value = cm.getColumnHeader(i) || "";
58425             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
58426             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
58427             if(!cm.isLocked(i)){
58428                 cb[cb.length] = ct.apply(p);
58429                 sb[sb.length] = st.apply(p);
58430             }else{
58431                 lb[lb.length] = ct.apply(p);
58432                 lsb[lsb.length] = st.apply(p);
58433             }
58434         }
58435         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
58436                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
58437     },
58438
58439     updateHeaders : function(){
58440         var html = this.renderHeaders();
58441         this.lockedHd.update(html[0]);
58442         this.mainHd.update(html[1]);
58443     },
58444
58445     /**
58446      * Focuses the specified row.
58447      * @param {Number} row The row index
58448      */
58449     focusRow : function(row)
58450     {
58451         //Roo.log('GridView.focusRow');
58452         var x = this.scroller.dom.scrollLeft;
58453         this.focusCell(row, 0, false);
58454         this.scroller.dom.scrollLeft = x;
58455     },
58456
58457     /**
58458      * Focuses the specified cell.
58459      * @param {Number} row The row index
58460      * @param {Number} col The column index
58461      * @param {Boolean} hscroll false to disable horizontal scrolling
58462      */
58463     focusCell : function(row, col, hscroll)
58464     {
58465         //Roo.log('GridView.focusCell');
58466         var el = this.ensureVisible(row, col, hscroll);
58467         this.focusEl.alignTo(el, "tl-tl");
58468         if(Roo.isGecko){
58469             this.focusEl.focus();
58470         }else{
58471             this.focusEl.focus.defer(1, this.focusEl);
58472         }
58473     },
58474
58475     /**
58476      * Scrolls the specified cell into view
58477      * @param {Number} row The row index
58478      * @param {Number} col The column index
58479      * @param {Boolean} hscroll false to disable horizontal scrolling
58480      */
58481     ensureVisible : function(row, col, hscroll)
58482     {
58483         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
58484         //return null; //disable for testing.
58485         if(typeof row != "number"){
58486             row = row.rowIndex;
58487         }
58488         if(row < 0 && row >= this.ds.getCount()){
58489             return  null;
58490         }
58491         col = (col !== undefined ? col : 0);
58492         var cm = this.grid.colModel;
58493         while(cm.isHidden(col)){
58494             col++;
58495         }
58496
58497         var el = this.getCell(row, col);
58498         if(!el){
58499             return null;
58500         }
58501         var c = this.scroller.dom;
58502
58503         var ctop = parseInt(el.offsetTop, 10);
58504         var cleft = parseInt(el.offsetLeft, 10);
58505         var cbot = ctop + el.offsetHeight;
58506         var cright = cleft + el.offsetWidth;
58507         
58508         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
58509         var stop = parseInt(c.scrollTop, 10);
58510         var sleft = parseInt(c.scrollLeft, 10);
58511         var sbot = stop + ch;
58512         var sright = sleft + c.clientWidth;
58513         /*
58514         Roo.log('GridView.ensureVisible:' +
58515                 ' ctop:' + ctop +
58516                 ' c.clientHeight:' + c.clientHeight +
58517                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
58518                 ' stop:' + stop +
58519                 ' cbot:' + cbot +
58520                 ' sbot:' + sbot +
58521                 ' ch:' + ch  
58522                 );
58523         */
58524         if(ctop < stop){
58525             c.scrollTop = ctop;
58526             //Roo.log("set scrolltop to ctop DISABLE?");
58527         }else if(cbot > sbot){
58528             //Roo.log("set scrolltop to cbot-ch");
58529             c.scrollTop = cbot-ch;
58530         }
58531         
58532         if(hscroll !== false){
58533             if(cleft < sleft){
58534                 c.scrollLeft = cleft;
58535             }else if(cright > sright){
58536                 c.scrollLeft = cright-c.clientWidth;
58537             }
58538         }
58539          
58540         return el;
58541     },
58542
58543     updateColumns : function(){
58544         this.grid.stopEditing();
58545         var cm = this.grid.colModel, colIds = this.getColumnIds();
58546         //var totalWidth = cm.getTotalWidth();
58547         var pos = 0;
58548         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58549             //if(cm.isHidden(i)) continue;
58550             var w = cm.getColumnWidth(i);
58551             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58552             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58553         }
58554         this.updateSplitters();
58555     },
58556
58557     generateRules : function(cm){
58558         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
58559         Roo.util.CSS.removeStyleSheet(rulesId);
58560         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58561             var cid = cm.getColumnId(i);
58562             var align = '';
58563             if(cm.config[i].align){
58564                 align = 'text-align:'+cm.config[i].align+';';
58565             }
58566             var hidden = '';
58567             if(cm.isHidden(i)){
58568                 hidden = 'display:none;';
58569             }
58570             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
58571             ruleBuf.push(
58572                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
58573                     this.hdSelector, cid, " {\n", align, width, "}\n",
58574                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
58575                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
58576         }
58577         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58578     },
58579
58580     updateSplitters : function(){
58581         var cm = this.cm, s = this.getSplitters();
58582         if(s){ // splitters not created yet
58583             var pos = 0, locked = true;
58584             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58585                 if(cm.isHidden(i)) {
58586                     continue;
58587                 }
58588                 var w = cm.getColumnWidth(i); // make sure it's a number
58589                 if(!cm.isLocked(i) && locked){
58590                     pos = 0;
58591                     locked = false;
58592                 }
58593                 pos += w;
58594                 s[i].style.left = (pos-this.splitOffset) + "px";
58595             }
58596         }
58597     },
58598
58599     handleHiddenChange : function(colModel, colIndex, hidden){
58600         if(hidden){
58601             this.hideColumn(colIndex);
58602         }else{
58603             this.unhideColumn(colIndex);
58604         }
58605     },
58606
58607     hideColumn : function(colIndex){
58608         var cid = this.getColumnId(colIndex);
58609         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
58610         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
58611         if(Roo.isSafari){
58612             this.updateHeaders();
58613         }
58614         this.updateSplitters();
58615         this.layout();
58616     },
58617
58618     unhideColumn : function(colIndex){
58619         var cid = this.getColumnId(colIndex);
58620         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
58621         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
58622
58623         if(Roo.isSafari){
58624             this.updateHeaders();
58625         }
58626         this.updateSplitters();
58627         this.layout();
58628     },
58629
58630     insertRows : function(dm, firstRow, lastRow, isUpdate){
58631         if(firstRow == 0 && lastRow == dm.getCount()-1){
58632             this.refresh();
58633         }else{
58634             if(!isUpdate){
58635                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
58636             }
58637             var s = this.getScrollState();
58638             var markup = this.renderRows(firstRow, lastRow);
58639             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
58640             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
58641             this.restoreScroll(s);
58642             if(!isUpdate){
58643                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
58644                 this.syncRowHeights(firstRow, lastRow);
58645                 this.stripeRows(firstRow);
58646                 this.layout();
58647             }
58648         }
58649     },
58650
58651     bufferRows : function(markup, target, index){
58652         var before = null, trows = target.rows, tbody = target.tBodies[0];
58653         if(index < trows.length){
58654             before = trows[index];
58655         }
58656         var b = document.createElement("div");
58657         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
58658         var rows = b.firstChild.rows;
58659         for(var i = 0, len = rows.length; i < len; i++){
58660             if(before){
58661                 tbody.insertBefore(rows[0], before);
58662             }else{
58663                 tbody.appendChild(rows[0]);
58664             }
58665         }
58666         b.innerHTML = "";
58667         b = null;
58668     },
58669
58670     deleteRows : function(dm, firstRow, lastRow){
58671         if(dm.getRowCount()<1){
58672             this.fireEvent("beforerefresh", this);
58673             this.mainBody.update("");
58674             this.lockedBody.update("");
58675             this.fireEvent("refresh", this);
58676         }else{
58677             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
58678             var bt = this.getBodyTable();
58679             var tbody = bt.firstChild;
58680             var rows = bt.rows;
58681             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
58682                 tbody.removeChild(rows[firstRow]);
58683             }
58684             this.stripeRows(firstRow);
58685             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
58686         }
58687     },
58688
58689     updateRows : function(dataSource, firstRow, lastRow){
58690         var s = this.getScrollState();
58691         this.refresh();
58692         this.restoreScroll(s);
58693     },
58694
58695     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
58696         if(!noRefresh){
58697            this.refresh();
58698         }
58699         this.updateHeaderSortState();
58700     },
58701
58702     getScrollState : function(){
58703         
58704         var sb = this.scroller.dom;
58705         return {left: sb.scrollLeft, top: sb.scrollTop};
58706     },
58707
58708     stripeRows : function(startRow){
58709         if(!this.grid.stripeRows || this.ds.getCount() < 1){
58710             return;
58711         }
58712         startRow = startRow || 0;
58713         var rows = this.getBodyTable().rows;
58714         var lrows = this.getLockedTable().rows;
58715         var cls = ' x-grid-row-alt ';
58716         for(var i = startRow, len = rows.length; i < len; i++){
58717             var row = rows[i], lrow = lrows[i];
58718             var isAlt = ((i+1) % 2 == 0);
58719             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
58720             if(isAlt == hasAlt){
58721                 continue;
58722             }
58723             if(isAlt){
58724                 row.className += " x-grid-row-alt";
58725             }else{
58726                 row.className = row.className.replace("x-grid-row-alt", "");
58727             }
58728             if(lrow){
58729                 lrow.className = row.className;
58730             }
58731         }
58732     },
58733
58734     restoreScroll : function(state){
58735         //Roo.log('GridView.restoreScroll');
58736         var sb = this.scroller.dom;
58737         sb.scrollLeft = state.left;
58738         sb.scrollTop = state.top;
58739         this.syncScroll();
58740     },
58741
58742     syncScroll : function(){
58743         //Roo.log('GridView.syncScroll');
58744         var sb = this.scroller.dom;
58745         var sh = this.mainHd.dom;
58746         var bs = this.mainBody.dom;
58747         var lv = this.lockedBody.dom;
58748         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
58749         lv.scrollTop = bs.scrollTop = sb.scrollTop;
58750     },
58751
58752     handleScroll : function(e){
58753         this.syncScroll();
58754         var sb = this.scroller.dom;
58755         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
58756         e.stopEvent();
58757     },
58758
58759     handleWheel : function(e){
58760         var d = e.getWheelDelta();
58761         this.scroller.dom.scrollTop -= d*22;
58762         // set this here to prevent jumpy scrolling on large tables
58763         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
58764         e.stopEvent();
58765     },
58766
58767     renderRows : function(startRow, endRow){
58768         // pull in all the crap needed to render rows
58769         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
58770         var colCount = cm.getColumnCount();
58771
58772         if(ds.getCount() < 1){
58773             return ["", ""];
58774         }
58775
58776         // build a map for all the columns
58777         var cs = [];
58778         for(var i = 0; i < colCount; i++){
58779             var name = cm.getDataIndex(i);
58780             cs[i] = {
58781                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
58782                 renderer : cm.getRenderer(i),
58783                 id : cm.getColumnId(i),
58784                 locked : cm.isLocked(i),
58785                 has_editor : cm.isCellEditable(i)
58786             };
58787         }
58788
58789         startRow = startRow || 0;
58790         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
58791
58792         // records to render
58793         var rs = ds.getRange(startRow, endRow);
58794
58795         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
58796     },
58797
58798     // As much as I hate to duplicate code, this was branched because FireFox really hates
58799     // [].join("") on strings. The performance difference was substantial enough to
58800     // branch this function
58801     doRender : Roo.isGecko ?
58802             function(cs, rs, ds, startRow, colCount, stripe){
58803                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58804                 // buffers
58805                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58806                 
58807                 var hasListener = this.grid.hasListener('rowclass');
58808                 var rowcfg = {};
58809                 for(var j = 0, len = rs.length; j < len; j++){
58810                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
58811                     for(var i = 0; i < colCount; i++){
58812                         c = cs[i];
58813                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58814                         p.id = c.id;
58815                         p.css = p.attr = "";
58816                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58817                         if(p.value == undefined || p.value === "") {
58818                             p.value = "&#160;";
58819                         }
58820                         if(c.has_editor){
58821                             p.css += ' x-grid-editable-cell';
58822                         }
58823                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
58824                             p.css +=  ' x-grid-dirty-cell';
58825                         }
58826                         var markup = ct.apply(p);
58827                         if(!c.locked){
58828                             cb+= markup;
58829                         }else{
58830                             lcb+= markup;
58831                         }
58832                     }
58833                     var alt = [];
58834                     if(stripe && ((rowIndex+1) % 2 == 0)){
58835                         alt.push("x-grid-row-alt")
58836                     }
58837                     if(r.dirty){
58838                         alt.push(  " x-grid-dirty-row");
58839                     }
58840                     rp.cells = lcb;
58841                     if(this.getRowClass){
58842                         alt.push(this.getRowClass(r, rowIndex));
58843                     }
58844                     if (hasListener) {
58845                         rowcfg = {
58846                              
58847                             record: r,
58848                             rowIndex : rowIndex,
58849                             rowClass : ''
58850                         };
58851                         this.grid.fireEvent('rowclass', this, rowcfg);
58852                         alt.push(rowcfg.rowClass);
58853                     }
58854                     rp.alt = alt.join(" ");
58855                     lbuf+= rt.apply(rp);
58856                     rp.cells = cb;
58857                     buf+=  rt.apply(rp);
58858                 }
58859                 return [lbuf, buf];
58860             } :
58861             function(cs, rs, ds, startRow, colCount, stripe){
58862                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58863                 // buffers
58864                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58865                 var hasListener = this.grid.hasListener('rowclass');
58866  
58867                 var rowcfg = {};
58868                 for(var j = 0, len = rs.length; j < len; j++){
58869                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
58870                     for(var i = 0; i < colCount; i++){
58871                         c = cs[i];
58872                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58873                         p.id = c.id;
58874                         p.css = p.attr = "";
58875                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58876                         if(p.value == undefined || p.value === "") {
58877                             p.value = "&#160;";
58878                         }
58879                         //Roo.log(c);
58880                          if(c.has_editor){
58881                             p.css += ' x-grid-editable-cell';
58882                         }
58883                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
58884                             p.css += ' x-grid-dirty-cell' 
58885                         }
58886                         
58887                         var markup = ct.apply(p);
58888                         if(!c.locked){
58889                             cb[cb.length] = markup;
58890                         }else{
58891                             lcb[lcb.length] = markup;
58892                         }
58893                     }
58894                     var alt = [];
58895                     if(stripe && ((rowIndex+1) % 2 == 0)){
58896                         alt.push( "x-grid-row-alt");
58897                     }
58898                     if(r.dirty){
58899                         alt.push(" x-grid-dirty-row");
58900                     }
58901                     rp.cells = lcb;
58902                     if(this.getRowClass){
58903                         alt.push( this.getRowClass(r, rowIndex));
58904                     }
58905                     if (hasListener) {
58906                         rowcfg = {
58907                              
58908                             record: r,
58909                             rowIndex : rowIndex,
58910                             rowClass : ''
58911                         };
58912                         this.grid.fireEvent('rowclass', this, rowcfg);
58913                         alt.push(rowcfg.rowClass);
58914                     }
58915                     
58916                     rp.alt = alt.join(" ");
58917                     rp.cells = lcb.join("");
58918                     lbuf[lbuf.length] = rt.apply(rp);
58919                     rp.cells = cb.join("");
58920                     buf[buf.length] =  rt.apply(rp);
58921                 }
58922                 return [lbuf.join(""), buf.join("")];
58923             },
58924
58925     renderBody : function(){
58926         var markup = this.renderRows();
58927         var bt = this.templates.body;
58928         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
58929     },
58930
58931     /**
58932      * Refreshes the grid
58933      * @param {Boolean} headersToo
58934      */
58935     refresh : function(headersToo){
58936         this.fireEvent("beforerefresh", this);
58937         this.grid.stopEditing();
58938         var result = this.renderBody();
58939         this.lockedBody.update(result[0]);
58940         this.mainBody.update(result[1]);
58941         if(headersToo === true){
58942             this.updateHeaders();
58943             this.updateColumns();
58944             this.updateSplitters();
58945             this.updateHeaderSortState();
58946         }
58947         this.syncRowHeights();
58948         this.layout();
58949         this.fireEvent("refresh", this);
58950     },
58951
58952     handleColumnMove : function(cm, oldIndex, newIndex){
58953         this.indexMap = null;
58954         var s = this.getScrollState();
58955         this.refresh(true);
58956         this.restoreScroll(s);
58957         this.afterMove(newIndex);
58958     },
58959
58960     afterMove : function(colIndex){
58961         if(this.enableMoveAnim && Roo.enableFx){
58962             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
58963         }
58964         // if multisort - fix sortOrder, and reload..
58965         if (this.grid.dataSource.multiSort) {
58966             // the we can call sort again..
58967             var dm = this.grid.dataSource;
58968             var cm = this.grid.colModel;
58969             var so = [];
58970             for(var i = 0; i < cm.config.length; i++ ) {
58971                 
58972                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
58973                     continue; // dont' bother, it's not in sort list or being set.
58974                 }
58975                 
58976                 so.push(cm.config[i].dataIndex);
58977             };
58978             dm.sortOrder = so;
58979             dm.load(dm.lastOptions);
58980             
58981             
58982         }
58983         
58984     },
58985
58986     updateCell : function(dm, rowIndex, dataIndex){
58987         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
58988         if(typeof colIndex == "undefined"){ // not present in grid
58989             return;
58990         }
58991         var cm = this.grid.colModel;
58992         var cell = this.getCell(rowIndex, colIndex);
58993         var cellText = this.getCellText(rowIndex, colIndex);
58994
58995         var p = {
58996             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
58997             id : cm.getColumnId(colIndex),
58998             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
58999         };
59000         var renderer = cm.getRenderer(colIndex);
59001         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
59002         if(typeof val == "undefined" || val === "") {
59003             val = "&#160;";
59004         }
59005         cellText.innerHTML = val;
59006         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
59007         this.syncRowHeights(rowIndex, rowIndex);
59008     },
59009
59010     calcColumnWidth : function(colIndex, maxRowsToMeasure){
59011         var maxWidth = 0;
59012         if(this.grid.autoSizeHeaders){
59013             var h = this.getHeaderCellMeasure(colIndex);
59014             maxWidth = Math.max(maxWidth, h.scrollWidth);
59015         }
59016         var tb, index;
59017         if(this.cm.isLocked(colIndex)){
59018             tb = this.getLockedTable();
59019             index = colIndex;
59020         }else{
59021             tb = this.getBodyTable();
59022             index = colIndex - this.cm.getLockedCount();
59023         }
59024         if(tb && tb.rows){
59025             var rows = tb.rows;
59026             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
59027             for(var i = 0; i < stopIndex; i++){
59028                 var cell = rows[i].childNodes[index].firstChild;
59029                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
59030             }
59031         }
59032         return maxWidth + /*margin for error in IE*/ 5;
59033     },
59034     /**
59035      * Autofit a column to its content.
59036      * @param {Number} colIndex
59037      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
59038      */
59039      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
59040          if(this.cm.isHidden(colIndex)){
59041              return; // can't calc a hidden column
59042          }
59043         if(forceMinSize){
59044             var cid = this.cm.getColumnId(colIndex);
59045             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
59046            if(this.grid.autoSizeHeaders){
59047                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
59048            }
59049         }
59050         var newWidth = this.calcColumnWidth(colIndex);
59051         this.cm.setColumnWidth(colIndex,
59052             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
59053         if(!suppressEvent){
59054             this.grid.fireEvent("columnresize", colIndex, newWidth);
59055         }
59056     },
59057
59058     /**
59059      * Autofits all columns to their content and then expands to fit any extra space in the grid
59060      */
59061      autoSizeColumns : function(){
59062         var cm = this.grid.colModel;
59063         var colCount = cm.getColumnCount();
59064         for(var i = 0; i < colCount; i++){
59065             this.autoSizeColumn(i, true, true);
59066         }
59067         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
59068             this.fitColumns();
59069         }else{
59070             this.updateColumns();
59071             this.layout();
59072         }
59073     },
59074
59075     /**
59076      * Autofits all columns to the grid's width proportionate with their current size
59077      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
59078      */
59079     fitColumns : function(reserveScrollSpace){
59080         var cm = this.grid.colModel;
59081         var colCount = cm.getColumnCount();
59082         var cols = [];
59083         var width = 0;
59084         var i, w;
59085         for (i = 0; i < colCount; i++){
59086             if(!cm.isHidden(i) && !cm.isFixed(i)){
59087                 w = cm.getColumnWidth(i);
59088                 cols.push(i);
59089                 cols.push(w);
59090                 width += w;
59091             }
59092         }
59093         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
59094         if(reserveScrollSpace){
59095             avail -= 17;
59096         }
59097         var frac = (avail - cm.getTotalWidth())/width;
59098         while (cols.length){
59099             w = cols.pop();
59100             i = cols.pop();
59101             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
59102         }
59103         this.updateColumns();
59104         this.layout();
59105     },
59106
59107     onRowSelect : function(rowIndex){
59108         var row = this.getRowComposite(rowIndex);
59109         row.addClass("x-grid-row-selected");
59110     },
59111
59112     onRowDeselect : function(rowIndex){
59113         var row = this.getRowComposite(rowIndex);
59114         row.removeClass("x-grid-row-selected");
59115     },
59116
59117     onCellSelect : function(row, col){
59118         var cell = this.getCell(row, col);
59119         if(cell){
59120             Roo.fly(cell).addClass("x-grid-cell-selected");
59121         }
59122     },
59123
59124     onCellDeselect : function(row, col){
59125         var cell = this.getCell(row, col);
59126         if(cell){
59127             Roo.fly(cell).removeClass("x-grid-cell-selected");
59128         }
59129     },
59130
59131     updateHeaderSortState : function(){
59132         
59133         // sort state can be single { field: xxx, direction : yyy}
59134         // or   { xxx=>ASC , yyy : DESC ..... }
59135         
59136         var mstate = {};
59137         if (!this.ds.multiSort) { 
59138             var state = this.ds.getSortState();
59139             if(!state){
59140                 return;
59141             }
59142             mstate[state.field] = state.direction;
59143             // FIXME... - this is not used here.. but might be elsewhere..
59144             this.sortState = state;
59145             
59146         } else {
59147             mstate = this.ds.sortToggle;
59148         }
59149         //remove existing sort classes..
59150         
59151         var sc = this.sortClasses;
59152         var hds = this.el.select(this.headerSelector).removeClass(sc);
59153         
59154         for(var f in mstate) {
59155         
59156             var sortColumn = this.cm.findColumnIndex(f);
59157             
59158             if(sortColumn != -1){
59159                 var sortDir = mstate[f];        
59160                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
59161             }
59162         }
59163         
59164          
59165         
59166     },
59167
59168
59169     handleHeaderClick : function(g, index,e){
59170         
59171         Roo.log("header click");
59172         
59173         if (Roo.isTouch) {
59174             // touch events on header are handled by context
59175             this.handleHdCtx(g,index,e);
59176             return;
59177         }
59178         
59179         
59180         if(this.headersDisabled){
59181             return;
59182         }
59183         var dm = g.dataSource, cm = g.colModel;
59184         if(!cm.isSortable(index)){
59185             return;
59186         }
59187         g.stopEditing();
59188         
59189         if (dm.multiSort) {
59190             // update the sortOrder
59191             var so = [];
59192             for(var i = 0; i < cm.config.length; i++ ) {
59193                 
59194                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
59195                     continue; // dont' bother, it's not in sort list or being set.
59196                 }
59197                 
59198                 so.push(cm.config[i].dataIndex);
59199             };
59200             dm.sortOrder = so;
59201         }
59202         
59203         
59204         dm.sort(cm.getDataIndex(index));
59205     },
59206
59207
59208     destroy : function(){
59209         if(this.colMenu){
59210             this.colMenu.removeAll();
59211             Roo.menu.MenuMgr.unregister(this.colMenu);
59212             this.colMenu.getEl().remove();
59213             delete this.colMenu;
59214         }
59215         if(this.hmenu){
59216             this.hmenu.removeAll();
59217             Roo.menu.MenuMgr.unregister(this.hmenu);
59218             this.hmenu.getEl().remove();
59219             delete this.hmenu;
59220         }
59221         if(this.grid.enableColumnMove){
59222             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
59223             if(dds){
59224                 for(var dd in dds){
59225                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
59226                         var elid = dds[dd].dragElId;
59227                         dds[dd].unreg();
59228                         Roo.get(elid).remove();
59229                     } else if(dds[dd].config.isTarget){
59230                         dds[dd].proxyTop.remove();
59231                         dds[dd].proxyBottom.remove();
59232                         dds[dd].unreg();
59233                     }
59234                     if(Roo.dd.DDM.locationCache[dd]){
59235                         delete Roo.dd.DDM.locationCache[dd];
59236                     }
59237                 }
59238                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
59239             }
59240         }
59241         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
59242         this.bind(null, null);
59243         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
59244     },
59245
59246     handleLockChange : function(){
59247         this.refresh(true);
59248     },
59249
59250     onDenyColumnLock : function(){
59251
59252     },
59253
59254     onDenyColumnHide : function(){
59255
59256     },
59257
59258     handleHdMenuClick : function(item){
59259         var index = this.hdCtxIndex;
59260         var cm = this.cm, ds = this.ds;
59261         switch(item.id){
59262             case "asc":
59263                 ds.sort(cm.getDataIndex(index), "ASC");
59264                 break;
59265             case "desc":
59266                 ds.sort(cm.getDataIndex(index), "DESC");
59267                 break;
59268             case "lock":
59269                 var lc = cm.getLockedCount();
59270                 if(cm.getColumnCount(true) <= lc+1){
59271                     this.onDenyColumnLock();
59272                     return;
59273                 }
59274                 if(lc != index){
59275                     cm.setLocked(index, true, true);
59276                     cm.moveColumn(index, lc);
59277                     this.grid.fireEvent("columnmove", index, lc);
59278                 }else{
59279                     cm.setLocked(index, true);
59280                 }
59281             break;
59282             case "unlock":
59283                 var lc = cm.getLockedCount();
59284                 if((lc-1) != index){
59285                     cm.setLocked(index, false, true);
59286                     cm.moveColumn(index, lc-1);
59287                     this.grid.fireEvent("columnmove", index, lc-1);
59288                 }else{
59289                     cm.setLocked(index, false);
59290                 }
59291             break;
59292             case 'wider': // used to expand cols on touch..
59293             case 'narrow':
59294                 var cw = cm.getColumnWidth(index);
59295                 cw += (item.id == 'wider' ? 1 : -1) * 50;
59296                 cw = Math.max(0, cw);
59297                 cw = Math.min(cw,4000);
59298                 cm.setColumnWidth(index, cw);
59299                 break;
59300                 
59301             default:
59302                 index = cm.getIndexById(item.id.substr(4));
59303                 if(index != -1){
59304                     if(item.checked && cm.getColumnCount(true) <= 1){
59305                         this.onDenyColumnHide();
59306                         return false;
59307                     }
59308                     cm.setHidden(index, item.checked);
59309                 }
59310         }
59311         return true;
59312     },
59313
59314     beforeColMenuShow : function(){
59315         var cm = this.cm,  colCount = cm.getColumnCount();
59316         this.colMenu.removeAll();
59317         for(var i = 0; i < colCount; i++){
59318             this.colMenu.add(new Roo.menu.CheckItem({
59319                 id: "col-"+cm.getColumnId(i),
59320                 text: cm.getColumnHeader(i),
59321                 checked: !cm.isHidden(i),
59322                 hideOnClick:false
59323             }));
59324         }
59325     },
59326
59327     handleHdCtx : function(g, index, e){
59328         e.stopEvent();
59329         var hd = this.getHeaderCell(index);
59330         this.hdCtxIndex = index;
59331         var ms = this.hmenu.items, cm = this.cm;
59332         ms.get("asc").setDisabled(!cm.isSortable(index));
59333         ms.get("desc").setDisabled(!cm.isSortable(index));
59334         if(this.grid.enableColLock !== false){
59335             ms.get("lock").setDisabled(cm.isLocked(index));
59336             ms.get("unlock").setDisabled(!cm.isLocked(index));
59337         }
59338         this.hmenu.show(hd, "tl-bl");
59339     },
59340
59341     handleHdOver : function(e){
59342         var hd = this.findHeaderCell(e.getTarget());
59343         if(hd && !this.headersDisabled){
59344             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
59345                this.fly(hd).addClass("x-grid-hd-over");
59346             }
59347         }
59348     },
59349
59350     handleHdOut : function(e){
59351         var hd = this.findHeaderCell(e.getTarget());
59352         if(hd){
59353             this.fly(hd).removeClass("x-grid-hd-over");
59354         }
59355     },
59356
59357     handleSplitDblClick : function(e, t){
59358         var i = this.getCellIndex(t);
59359         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
59360             this.autoSizeColumn(i, true);
59361             this.layout();
59362         }
59363     },
59364
59365     render : function(){
59366
59367         var cm = this.cm;
59368         var colCount = cm.getColumnCount();
59369
59370         if(this.grid.monitorWindowResize === true){
59371             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
59372         }
59373         var header = this.renderHeaders();
59374         var body = this.templates.body.apply({rows:""});
59375         var html = this.templates.master.apply({
59376             lockedBody: body,
59377             body: body,
59378             lockedHeader: header[0],
59379             header: header[1]
59380         });
59381
59382         //this.updateColumns();
59383
59384         this.grid.getGridEl().dom.innerHTML = html;
59385
59386         this.initElements();
59387         
59388         // a kludge to fix the random scolling effect in webkit
59389         this.el.on("scroll", function() {
59390             this.el.dom.scrollTop=0; // hopefully not recursive..
59391         },this);
59392
59393         this.scroller.on("scroll", this.handleScroll, this);
59394         this.lockedBody.on("mousewheel", this.handleWheel, this);
59395         this.mainBody.on("mousewheel", this.handleWheel, this);
59396
59397         this.mainHd.on("mouseover", this.handleHdOver, this);
59398         this.mainHd.on("mouseout", this.handleHdOut, this);
59399         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
59400                 {delegate: "."+this.splitClass});
59401
59402         this.lockedHd.on("mouseover", this.handleHdOver, this);
59403         this.lockedHd.on("mouseout", this.handleHdOut, this);
59404         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
59405                 {delegate: "."+this.splitClass});
59406
59407         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
59408             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59409         }
59410
59411         this.updateSplitters();
59412
59413         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
59414             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59415             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59416         }
59417
59418         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
59419             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
59420             this.hmenu.add(
59421                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
59422                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
59423             );
59424             if(this.grid.enableColLock !== false){
59425                 this.hmenu.add('-',
59426                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
59427                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
59428                 );
59429             }
59430             if (Roo.isTouch) {
59431                  this.hmenu.add('-',
59432                     {id:"wider", text: this.columnsWiderText},
59433                     {id:"narrow", text: this.columnsNarrowText }
59434                 );
59435                 
59436                  
59437             }
59438             
59439             if(this.grid.enableColumnHide !== false){
59440
59441                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
59442                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
59443                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
59444
59445                 this.hmenu.add('-',
59446                     {id:"columns", text: this.columnsText, menu: this.colMenu}
59447                 );
59448             }
59449             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
59450
59451             this.grid.on("headercontextmenu", this.handleHdCtx, this);
59452         }
59453
59454         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
59455             this.dd = new Roo.grid.GridDragZone(this.grid, {
59456                 ddGroup : this.grid.ddGroup || 'GridDD'
59457             });
59458             
59459         }
59460
59461         /*
59462         for(var i = 0; i < colCount; i++){
59463             if(cm.isHidden(i)){
59464                 this.hideColumn(i);
59465             }
59466             if(cm.config[i].align){
59467                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
59468                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
59469             }
59470         }*/
59471         
59472         this.updateHeaderSortState();
59473
59474         this.beforeInitialResize();
59475         this.layout(true);
59476
59477         // two part rendering gives faster view to the user
59478         this.renderPhase2.defer(1, this);
59479     },
59480
59481     renderPhase2 : function(){
59482         // render the rows now
59483         this.refresh();
59484         if(this.grid.autoSizeColumns){
59485             this.autoSizeColumns();
59486         }
59487     },
59488
59489     beforeInitialResize : function(){
59490
59491     },
59492
59493     onColumnSplitterMoved : function(i, w){
59494         this.userResized = true;
59495         var cm = this.grid.colModel;
59496         cm.setColumnWidth(i, w, true);
59497         var cid = cm.getColumnId(i);
59498         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
59499         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
59500         this.updateSplitters();
59501         this.layout();
59502         this.grid.fireEvent("columnresize", i, w);
59503     },
59504
59505     syncRowHeights : function(startIndex, endIndex){
59506         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
59507             startIndex = startIndex || 0;
59508             var mrows = this.getBodyTable().rows;
59509             var lrows = this.getLockedTable().rows;
59510             var len = mrows.length-1;
59511             endIndex = Math.min(endIndex || len, len);
59512             for(var i = startIndex; i <= endIndex; i++){
59513                 var m = mrows[i], l = lrows[i];
59514                 var h = Math.max(m.offsetHeight, l.offsetHeight);
59515                 m.style.height = l.style.height = h + "px";
59516             }
59517         }
59518     },
59519
59520     layout : function(initialRender, is2ndPass)
59521     {
59522         var g = this.grid;
59523         var auto = g.autoHeight;
59524         var scrollOffset = 16;
59525         var c = g.getGridEl(), cm = this.cm,
59526                 expandCol = g.autoExpandColumn,
59527                 gv = this;
59528         //c.beginMeasure();
59529
59530         if(!c.dom.offsetWidth){ // display:none?
59531             if(initialRender){
59532                 this.lockedWrap.show();
59533                 this.mainWrap.show();
59534             }
59535             return;
59536         }
59537
59538         var hasLock = this.cm.isLocked(0);
59539
59540         var tbh = this.headerPanel.getHeight();
59541         var bbh = this.footerPanel.getHeight();
59542
59543         if(auto){
59544             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
59545             var newHeight = ch + c.getBorderWidth("tb");
59546             if(g.maxHeight){
59547                 newHeight = Math.min(g.maxHeight, newHeight);
59548             }
59549             c.setHeight(newHeight);
59550         }
59551
59552         if(g.autoWidth){
59553             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
59554         }
59555
59556         var s = this.scroller;
59557
59558         var csize = c.getSize(true);
59559
59560         this.el.setSize(csize.width, csize.height);
59561
59562         this.headerPanel.setWidth(csize.width);
59563         this.footerPanel.setWidth(csize.width);
59564
59565         var hdHeight = this.mainHd.getHeight();
59566         var vw = csize.width;
59567         var vh = csize.height - (tbh + bbh);
59568
59569         s.setSize(vw, vh);
59570
59571         var bt = this.getBodyTable();
59572         
59573         if(cm.getLockedCount() == cm.config.length){
59574             bt = this.getLockedTable();
59575         }
59576         
59577         var ltWidth = hasLock ?
59578                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
59579
59580         var scrollHeight = bt.offsetHeight;
59581         var scrollWidth = ltWidth + bt.offsetWidth;
59582         var vscroll = false, hscroll = false;
59583
59584         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
59585
59586         var lw = this.lockedWrap, mw = this.mainWrap;
59587         var lb = this.lockedBody, mb = this.mainBody;
59588
59589         setTimeout(function(){
59590             var t = s.dom.offsetTop;
59591             var w = s.dom.clientWidth,
59592                 h = s.dom.clientHeight;
59593
59594             lw.setTop(t);
59595             lw.setSize(ltWidth, h);
59596
59597             mw.setLeftTop(ltWidth, t);
59598             mw.setSize(w-ltWidth, h);
59599
59600             lb.setHeight(h-hdHeight);
59601             mb.setHeight(h-hdHeight);
59602
59603             if(is2ndPass !== true && !gv.userResized && expandCol){
59604                 // high speed resize without full column calculation
59605                 
59606                 var ci = cm.getIndexById(expandCol);
59607                 if (ci < 0) {
59608                     ci = cm.findColumnIndex(expandCol);
59609                 }
59610                 ci = Math.max(0, ci); // make sure it's got at least the first col.
59611                 var expandId = cm.getColumnId(ci);
59612                 var  tw = cm.getTotalWidth(false);
59613                 var currentWidth = cm.getColumnWidth(ci);
59614                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
59615                 if(currentWidth != cw){
59616                     cm.setColumnWidth(ci, cw, true);
59617                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59618                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59619                     gv.updateSplitters();
59620                     gv.layout(false, true);
59621                 }
59622             }
59623
59624             if(initialRender){
59625                 lw.show();
59626                 mw.show();
59627             }
59628             //c.endMeasure();
59629         }, 10);
59630     },
59631
59632     onWindowResize : function(){
59633         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
59634             return;
59635         }
59636         this.layout();
59637     },
59638
59639     appendFooter : function(parentEl){
59640         return null;
59641     },
59642
59643     sortAscText : "Sort Ascending",
59644     sortDescText : "Sort Descending",
59645     lockText : "Lock Column",
59646     unlockText : "Unlock Column",
59647     columnsText : "Columns",
59648  
59649     columnsWiderText : "Wider",
59650     columnsNarrowText : "Thinner"
59651 });
59652
59653
59654 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
59655     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
59656     this.proxy.el.addClass('x-grid3-col-dd');
59657 };
59658
59659 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
59660     handleMouseDown : function(e){
59661
59662     },
59663
59664     callHandleMouseDown : function(e){
59665         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
59666     }
59667 });
59668 /*
59669  * Based on:
59670  * Ext JS Library 1.1.1
59671  * Copyright(c) 2006-2007, Ext JS, LLC.
59672  *
59673  * Originally Released Under LGPL - original licence link has changed is not relivant.
59674  *
59675  * Fork - LGPL
59676  * <script type="text/javascript">
59677  */
59678  /**
59679  * @extends Roo.dd.DDProxy
59680  * @class Roo.grid.SplitDragZone
59681  * Support for Column Header resizing
59682  * @constructor
59683  * @param {Object} config
59684  */
59685 // private
59686 // This is a support class used internally by the Grid components
59687 Roo.grid.SplitDragZone = function(grid, hd, hd2){
59688     this.grid = grid;
59689     this.view = grid.getView();
59690     this.proxy = this.view.resizeProxy;
59691     Roo.grid.SplitDragZone.superclass.constructor.call(
59692         this,
59693         hd, // ID
59694         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
59695         {  // CONFIG
59696             dragElId : Roo.id(this.proxy.dom),
59697             resizeFrame:false
59698         }
59699     );
59700     
59701     this.setHandleElId(Roo.id(hd));
59702     if (hd2 !== false) {
59703         this.setOuterHandleElId(Roo.id(hd2));
59704     }
59705     
59706     this.scroll = false;
59707 };
59708 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
59709     fly: Roo.Element.fly,
59710
59711     b4StartDrag : function(x, y){
59712         this.view.headersDisabled = true;
59713         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
59714                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
59715         );
59716         this.proxy.setHeight(h);
59717         
59718         // for old system colWidth really stored the actual width?
59719         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
59720         // which in reality did not work.. - it worked only for fixed sizes
59721         // for resizable we need to use actual sizes.
59722         var w = this.cm.getColumnWidth(this.cellIndex);
59723         if (!this.view.mainWrap) {
59724             // bootstrap.
59725             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
59726         }
59727         
59728         
59729         
59730         // this was w-this.grid.minColumnWidth;
59731         // doesnt really make sense? - w = thie curren width or the rendered one?
59732         var minw = Math.max(w-this.grid.minColumnWidth, 0);
59733         this.resetConstraints();
59734         this.setXConstraint(minw, 1000);
59735         this.setYConstraint(0, 0);
59736         this.minX = x - minw;
59737         this.maxX = x + 1000;
59738         this.startPos = x;
59739         if (!this.view.mainWrap) { // this is Bootstrap code..
59740             this.getDragEl().style.display='block';
59741         }
59742         
59743         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
59744     },
59745
59746
59747     handleMouseDown : function(e){
59748         ev = Roo.EventObject.setEvent(e);
59749         var t = this.fly(ev.getTarget());
59750         if(t.hasClass("x-grid-split")){
59751             this.cellIndex = this.view.getCellIndex(t.dom);
59752             this.split = t.dom;
59753             this.cm = this.grid.colModel;
59754             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
59755                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
59756             }
59757         }
59758     },
59759
59760     endDrag : function(e){
59761         this.view.headersDisabled = false;
59762         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
59763         var diff = endX - this.startPos;
59764         // 
59765         var w = this.cm.getColumnWidth(this.cellIndex);
59766         if (!this.view.mainWrap) {
59767             w = 0;
59768         }
59769         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
59770     },
59771
59772     autoOffset : function(){
59773         this.setDelta(0,0);
59774     }
59775 });/*
59776  * Based on:
59777  * Ext JS Library 1.1.1
59778  * Copyright(c) 2006-2007, Ext JS, LLC.
59779  *
59780  * Originally Released Under LGPL - original licence link has changed is not relivant.
59781  *
59782  * Fork - LGPL
59783  * <script type="text/javascript">
59784  */
59785  
59786 // private
59787 // This is a support class used internally by the Grid components
59788 Roo.grid.GridDragZone = function(grid, config){
59789     this.view = grid.getView();
59790     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
59791     if(this.view.lockedBody){
59792         this.setHandleElId(Roo.id(this.view.mainBody.dom));
59793         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
59794     }
59795     this.scroll = false;
59796     this.grid = grid;
59797     this.ddel = document.createElement('div');
59798     this.ddel.className = 'x-grid-dd-wrap';
59799 };
59800
59801 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
59802     ddGroup : "GridDD",
59803
59804     getDragData : function(e){
59805         var t = Roo.lib.Event.getTarget(e);
59806         var rowIndex = this.view.findRowIndex(t);
59807         var sm = this.grid.selModel;
59808             
59809         //Roo.log(rowIndex);
59810         
59811         if (sm.getSelectedCell) {
59812             // cell selection..
59813             if (!sm.getSelectedCell()) {
59814                 return false;
59815             }
59816             if (rowIndex != sm.getSelectedCell()[0]) {
59817                 return false;
59818             }
59819         
59820         }
59821         if (sm.getSelections && sm.getSelections().length < 1) {
59822             return false;
59823         }
59824         
59825         
59826         // before it used to all dragging of unseleted... - now we dont do that.
59827         if(rowIndex !== false){
59828             
59829             // if editorgrid.. 
59830             
59831             
59832             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
59833                
59834             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
59835               //  
59836             //}
59837             if (e.hasModifier()){
59838                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
59839             }
59840             
59841             Roo.log("getDragData");
59842             
59843             return {
59844                 grid: this.grid,
59845                 ddel: this.ddel,
59846                 rowIndex: rowIndex,
59847                 selections: sm.getSelections ? sm.getSelections() : (
59848                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
59849             };
59850         }
59851         return false;
59852     },
59853     
59854     
59855     onInitDrag : function(e){
59856         var data = this.dragData;
59857         this.ddel.innerHTML = this.grid.getDragDropText();
59858         this.proxy.update(this.ddel);
59859         // fire start drag?
59860     },
59861
59862     afterRepair : function(){
59863         this.dragging = false;
59864     },
59865
59866     getRepairXY : function(e, data){
59867         return false;
59868     },
59869
59870     onEndDrag : function(data, e){
59871         // fire end drag?
59872     },
59873
59874     onValidDrop : function(dd, e, id){
59875         // fire drag drop?
59876         this.hideProxy();
59877     },
59878
59879     beforeInvalidDrop : function(e, id){
59880
59881     }
59882 });/*
59883  * Based on:
59884  * Ext JS Library 1.1.1
59885  * Copyright(c) 2006-2007, Ext JS, LLC.
59886  *
59887  * Originally Released Under LGPL - original licence link has changed is not relivant.
59888  *
59889  * Fork - LGPL
59890  * <script type="text/javascript">
59891  */
59892  
59893
59894 /**
59895  * @class Roo.grid.ColumnModel
59896  * @extends Roo.util.Observable
59897  * This is the default implementation of a ColumnModel used by the Grid. It defines
59898  * the columns in the grid.
59899  * <br>Usage:<br>
59900  <pre><code>
59901  var colModel = new Roo.grid.ColumnModel([
59902         {header: "Ticker", width: 60, sortable: true, locked: true},
59903         {header: "Company Name", width: 150, sortable: true},
59904         {header: "Market Cap.", width: 100, sortable: true},
59905         {header: "$ Sales", width: 100, sortable: true, renderer: money},
59906         {header: "Employees", width: 100, sortable: true, resizable: false}
59907  ]);
59908  </code></pre>
59909  * <p>
59910  
59911  * The config options listed for this class are options which may appear in each
59912  * individual column definition.
59913  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
59914  * @constructor
59915  * @param {Object} config An Array of column config objects. See this class's
59916  * config objects for details.
59917 */
59918 Roo.grid.ColumnModel = function(config){
59919         /**
59920      * The config passed into the constructor
59921      */
59922     this.config = []; //config;
59923     this.lookup = {};
59924
59925     // if no id, create one
59926     // if the column does not have a dataIndex mapping,
59927     // map it to the order it is in the config
59928     for(var i = 0, len = config.length; i < len; i++){
59929         this.addColumn(config[i]);
59930         
59931     }
59932
59933     /**
59934      * The width of columns which have no width specified (defaults to 100)
59935      * @type Number
59936      */
59937     this.defaultWidth = 100;
59938
59939     /**
59940      * Default sortable of columns which have no sortable specified (defaults to false)
59941      * @type Boolean
59942      */
59943     this.defaultSortable = false;
59944
59945     this.addEvents({
59946         /**
59947              * @event widthchange
59948              * Fires when the width of a column changes.
59949              * @param {ColumnModel} this
59950              * @param {Number} columnIndex The column index
59951              * @param {Number} newWidth The new width
59952              */
59953             "widthchange": true,
59954         /**
59955              * @event headerchange
59956              * Fires when the text of a header changes.
59957              * @param {ColumnModel} this
59958              * @param {Number} columnIndex The column index
59959              * @param {Number} newText The new header text
59960              */
59961             "headerchange": true,
59962         /**
59963              * @event hiddenchange
59964              * Fires when a column is hidden or "unhidden".
59965              * @param {ColumnModel} this
59966              * @param {Number} columnIndex The column index
59967              * @param {Boolean} hidden true if hidden, false otherwise
59968              */
59969             "hiddenchange": true,
59970             /**
59971          * @event columnmoved
59972          * Fires when a column is moved.
59973          * @param {ColumnModel} this
59974          * @param {Number} oldIndex
59975          * @param {Number} newIndex
59976          */
59977         "columnmoved" : true,
59978         /**
59979          * @event columlockchange
59980          * Fires when a column's locked state is changed
59981          * @param {ColumnModel} this
59982          * @param {Number} colIndex
59983          * @param {Boolean} locked true if locked
59984          */
59985         "columnlockchange" : true
59986     });
59987     Roo.grid.ColumnModel.superclass.constructor.call(this);
59988 };
59989 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
59990     /**
59991      * @cfg {String} header The header text to display in the Grid view.
59992      */
59993         /**
59994      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
59995      */
59996         /**
59997      * @cfg {String} smHeader Header at Bootsrap Small width
59998      */
59999         /**
60000      * @cfg {String} mdHeader Header at Bootsrap Medium width
60001      */
60002         /**
60003      * @cfg {String} lgHeader Header at Bootsrap Large width
60004      */
60005         /**
60006      * @cfg {String} xlHeader Header at Bootsrap extra Large width
60007      */
60008     /**
60009      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
60010      * {@link Roo.data.Record} definition from which to draw the column's value. If not
60011      * specified, the column's index is used as an index into the Record's data Array.
60012      */
60013     /**
60014      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
60015      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
60016      */
60017     /**
60018      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
60019      * Defaults to the value of the {@link #defaultSortable} property.
60020      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
60021      */
60022     /**
60023      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
60024      */
60025     /**
60026      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
60027      */
60028     /**
60029      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
60030      */
60031     /**
60032      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
60033      */
60034     /**
60035      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
60036      * given the cell's data value. See {@link #setRenderer}. If not specified, the
60037      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
60038      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
60039      */
60040        /**
60041      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
60042      */
60043     /**
60044      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
60045      */
60046     /**
60047      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
60048      */
60049     /**
60050      * @cfg {String} cursor (Optional)
60051      */
60052     /**
60053      * @cfg {String} tooltip (Optional)
60054      */
60055     /**
60056      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
60057      */
60058     /**
60059      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
60060      */
60061     /**
60062      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
60063      */
60064     /**
60065      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
60066      */
60067         /**
60068      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
60069      */
60070     /**
60071      * Returns the id of the column at the specified index.
60072      * @param {Number} index The column index
60073      * @return {String} the id
60074      */
60075     getColumnId : function(index){
60076         return this.config[index].id;
60077     },
60078
60079     /**
60080      * Returns the column for a specified id.
60081      * @param {String} id The column id
60082      * @return {Object} the column
60083      */
60084     getColumnById : function(id){
60085         return this.lookup[id];
60086     },
60087
60088     
60089     /**
60090      * Returns the column Object for a specified dataIndex.
60091      * @param {String} dataIndex The column dataIndex
60092      * @return {Object|Boolean} the column or false if not found
60093      */
60094     getColumnByDataIndex: function(dataIndex){
60095         var index = this.findColumnIndex(dataIndex);
60096         return index > -1 ? this.config[index] : false;
60097     },
60098     
60099     /**
60100      * Returns the index for a specified column id.
60101      * @param {String} id The column id
60102      * @return {Number} the index, or -1 if not found
60103      */
60104     getIndexById : function(id){
60105         for(var i = 0, len = this.config.length; i < len; i++){
60106             if(this.config[i].id == id){
60107                 return i;
60108             }
60109         }
60110         return -1;
60111     },
60112     
60113     /**
60114      * Returns the index for a specified column dataIndex.
60115      * @param {String} dataIndex The column dataIndex
60116      * @return {Number} the index, or -1 if not found
60117      */
60118     
60119     findColumnIndex : function(dataIndex){
60120         for(var i = 0, len = this.config.length; i < len; i++){
60121             if(this.config[i].dataIndex == dataIndex){
60122                 return i;
60123             }
60124         }
60125         return -1;
60126     },
60127     
60128     
60129     moveColumn : function(oldIndex, newIndex){
60130         var c = this.config[oldIndex];
60131         this.config.splice(oldIndex, 1);
60132         this.config.splice(newIndex, 0, c);
60133         this.dataMap = null;
60134         this.fireEvent("columnmoved", this, oldIndex, newIndex);
60135     },
60136
60137     isLocked : function(colIndex){
60138         return this.config[colIndex].locked === true;
60139     },
60140
60141     setLocked : function(colIndex, value, suppressEvent){
60142         if(this.isLocked(colIndex) == value){
60143             return;
60144         }
60145         this.config[colIndex].locked = value;
60146         if(!suppressEvent){
60147             this.fireEvent("columnlockchange", this, colIndex, value);
60148         }
60149     },
60150
60151     getTotalLockedWidth : function(){
60152         var totalWidth = 0;
60153         for(var i = 0; i < this.config.length; i++){
60154             if(this.isLocked(i) && !this.isHidden(i)){
60155                 this.totalWidth += this.getColumnWidth(i);
60156             }
60157         }
60158         return totalWidth;
60159     },
60160
60161     getLockedCount : function(){
60162         for(var i = 0, len = this.config.length; i < len; i++){
60163             if(!this.isLocked(i)){
60164                 return i;
60165             }
60166         }
60167         
60168         return this.config.length;
60169     },
60170
60171     /**
60172      * Returns the number of columns.
60173      * @return {Number}
60174      */
60175     getColumnCount : function(visibleOnly){
60176         if(visibleOnly === true){
60177             var c = 0;
60178             for(var i = 0, len = this.config.length; i < len; i++){
60179                 if(!this.isHidden(i)){
60180                     c++;
60181                 }
60182             }
60183             return c;
60184         }
60185         return this.config.length;
60186     },
60187
60188     /**
60189      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
60190      * @param {Function} fn
60191      * @param {Object} scope (optional)
60192      * @return {Array} result
60193      */
60194     getColumnsBy : function(fn, scope){
60195         var r = [];
60196         for(var i = 0, len = this.config.length; i < len; i++){
60197             var c = this.config[i];
60198             if(fn.call(scope||this, c, i) === true){
60199                 r[r.length] = c;
60200             }
60201         }
60202         return r;
60203     },
60204
60205     /**
60206      * Returns true if the specified column is sortable.
60207      * @param {Number} col The column index
60208      * @return {Boolean}
60209      */
60210     isSortable : function(col){
60211         if(typeof this.config[col].sortable == "undefined"){
60212             return this.defaultSortable;
60213         }
60214         return this.config[col].sortable;
60215     },
60216
60217     /**
60218      * Returns the rendering (formatting) function defined for the column.
60219      * @param {Number} col The column index.
60220      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
60221      */
60222     getRenderer : function(col){
60223         if(!this.config[col].renderer){
60224             return Roo.grid.ColumnModel.defaultRenderer;
60225         }
60226         return this.config[col].renderer;
60227     },
60228
60229     /**
60230      * Sets the rendering (formatting) function for a column.
60231      * @param {Number} col The column index
60232      * @param {Function} fn The function to use to process the cell's raw data
60233      * to return HTML markup for the grid view. The render function is called with
60234      * the following parameters:<ul>
60235      * <li>Data value.</li>
60236      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
60237      * <li>css A CSS style string to apply to the table cell.</li>
60238      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
60239      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
60240      * <li>Row index</li>
60241      * <li>Column index</li>
60242      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
60243      */
60244     setRenderer : function(col, fn){
60245         this.config[col].renderer = fn;
60246     },
60247
60248     /**
60249      * Returns the width for the specified column.
60250      * @param {Number} col The column index
60251      * @param (optional) {String} gridSize bootstrap width size.
60252      * @return {Number}
60253      */
60254     getColumnWidth : function(col, gridSize)
60255         {
60256                 var cfg = this.config[col];
60257                 
60258                 if (typeof(gridSize) == 'undefined') {
60259                         return cfg.width * 1 || this.defaultWidth;
60260                 }
60261                 if (gridSize === false) { // if we set it..
60262                         return cfg.width || false;
60263                 }
60264                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
60265                 
60266                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
60267                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
60268                                 continue;
60269                         }
60270                         return cfg[ sizes[i] ];
60271                 }
60272                 return 1;
60273                 
60274     },
60275
60276     /**
60277      * Sets the width for a column.
60278      * @param {Number} col The column index
60279      * @param {Number} width The new width
60280      */
60281     setColumnWidth : function(col, width, suppressEvent){
60282         this.config[col].width = width;
60283         this.totalWidth = null;
60284         if(!suppressEvent){
60285              this.fireEvent("widthchange", this, col, width);
60286         }
60287     },
60288
60289     /**
60290      * Returns the total width of all columns.
60291      * @param {Boolean} includeHidden True to include hidden column widths
60292      * @return {Number}
60293      */
60294     getTotalWidth : function(includeHidden){
60295         if(!this.totalWidth){
60296             this.totalWidth = 0;
60297             for(var i = 0, len = this.config.length; i < len; i++){
60298                 if(includeHidden || !this.isHidden(i)){
60299                     this.totalWidth += this.getColumnWidth(i);
60300                 }
60301             }
60302         }
60303         return this.totalWidth;
60304     },
60305
60306     /**
60307      * Returns the header for the specified column.
60308      * @param {Number} col The column index
60309      * @return {String}
60310      */
60311     getColumnHeader : function(col){
60312         return this.config[col].header;
60313     },
60314
60315     /**
60316      * Sets the header for a column.
60317      * @param {Number} col The column index
60318      * @param {String} header The new header
60319      */
60320     setColumnHeader : function(col, header){
60321         this.config[col].header = header;
60322         this.fireEvent("headerchange", this, col, header);
60323     },
60324
60325     /**
60326      * Returns the tooltip for the specified column.
60327      * @param {Number} col The column index
60328      * @return {String}
60329      */
60330     getColumnTooltip : function(col){
60331             return this.config[col].tooltip;
60332     },
60333     /**
60334      * Sets the tooltip for a column.
60335      * @param {Number} col The column index
60336      * @param {String} tooltip The new tooltip
60337      */
60338     setColumnTooltip : function(col, tooltip){
60339             this.config[col].tooltip = tooltip;
60340     },
60341
60342     /**
60343      * Returns the dataIndex for the specified column.
60344      * @param {Number} col The column index
60345      * @return {Number}
60346      */
60347     getDataIndex : function(col){
60348         return this.config[col].dataIndex;
60349     },
60350
60351     /**
60352      * Sets the dataIndex for a column.
60353      * @param {Number} col The column index
60354      * @param {Number} dataIndex The new dataIndex
60355      */
60356     setDataIndex : function(col, dataIndex){
60357         this.config[col].dataIndex = dataIndex;
60358     },
60359
60360     
60361     
60362     /**
60363      * Returns true if the cell is editable.
60364      * @param {Number} colIndex The column index
60365      * @param {Number} rowIndex The row index - this is nto actually used..?
60366      * @return {Boolean}
60367      */
60368     isCellEditable : function(colIndex, rowIndex){
60369         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
60370     },
60371
60372     /**
60373      * Returns the editor defined for the cell/column.
60374      * return false or null to disable editing.
60375      * @param {Number} colIndex The column index
60376      * @param {Number} rowIndex The row index
60377      * @return {Object}
60378      */
60379     getCellEditor : function(colIndex, rowIndex){
60380         return this.config[colIndex].editor;
60381     },
60382
60383     /**
60384      * Sets if a column is editable.
60385      * @param {Number} col The column index
60386      * @param {Boolean} editable True if the column is editable
60387      */
60388     setEditable : function(col, editable){
60389         this.config[col].editable = editable;
60390     },
60391
60392
60393     /**
60394      * Returns true if the column is hidden.
60395      * @param {Number} colIndex The column index
60396      * @return {Boolean}
60397      */
60398     isHidden : function(colIndex){
60399         return this.config[colIndex].hidden;
60400     },
60401
60402
60403     /**
60404      * Returns true if the column width cannot be changed
60405      */
60406     isFixed : function(colIndex){
60407         return this.config[colIndex].fixed;
60408     },
60409
60410     /**
60411      * Returns true if the column can be resized
60412      * @return {Boolean}
60413      */
60414     isResizable : function(colIndex){
60415         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
60416     },
60417     /**
60418      * Sets if a column is hidden.
60419      * @param {Number} colIndex The column index
60420      * @param {Boolean} hidden True if the column is hidden
60421      */
60422     setHidden : function(colIndex, hidden){
60423         this.config[colIndex].hidden = hidden;
60424         this.totalWidth = null;
60425         this.fireEvent("hiddenchange", this, colIndex, hidden);
60426     },
60427
60428     /**
60429      * Sets the editor for a column.
60430      * @param {Number} col The column index
60431      * @param {Object} editor The editor object
60432      */
60433     setEditor : function(col, editor){
60434         this.config[col].editor = editor;
60435     },
60436     /**
60437      * Add a column (experimental...) - defaults to adding to the end..
60438      * @param {Object} config 
60439     */
60440     addColumn : function(c)
60441     {
60442     
60443         var i = this.config.length;
60444         this.config[i] = c;
60445         
60446         if(typeof c.dataIndex == "undefined"){
60447             c.dataIndex = i;
60448         }
60449         if(typeof c.renderer == "string"){
60450             c.renderer = Roo.util.Format[c.renderer];
60451         }
60452         if(typeof c.id == "undefined"){
60453             c.id = Roo.id();
60454         }
60455         if(c.editor && c.editor.xtype){
60456             c.editor  = Roo.factory(c.editor, Roo.grid);
60457         }
60458         if(c.editor && c.editor.isFormField){
60459             c.editor = new Roo.grid.GridEditor(c.editor);
60460         }
60461         this.lookup[c.id] = c;
60462     }
60463     
60464 });
60465
60466 Roo.grid.ColumnModel.defaultRenderer = function(value)
60467 {
60468     if(typeof value == "object") {
60469         return value;
60470     }
60471         if(typeof value == "string" && value.length < 1){
60472             return "&#160;";
60473         }
60474     
60475         return String.format("{0}", value);
60476 };
60477
60478 // Alias for backwards compatibility
60479 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
60480 /*
60481  * Based on:
60482  * Ext JS Library 1.1.1
60483  * Copyright(c) 2006-2007, Ext JS, LLC.
60484  *
60485  * Originally Released Under LGPL - original licence link has changed is not relivant.
60486  *
60487  * Fork - LGPL
60488  * <script type="text/javascript">
60489  */
60490
60491 /**
60492  * @class Roo.grid.AbstractSelectionModel
60493  * @extends Roo.util.Observable
60494  * @abstract
60495  * Abstract base class for grid SelectionModels.  It provides the interface that should be
60496  * implemented by descendant classes.  This class should not be directly instantiated.
60497  * @constructor
60498  */
60499 Roo.grid.AbstractSelectionModel = function(){
60500     this.locked = false;
60501     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
60502 };
60503
60504 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
60505     /** @ignore Called by the grid automatically. Do not call directly. */
60506     init : function(grid){
60507         this.grid = grid;
60508         this.initEvents();
60509     },
60510
60511     /**
60512      * Locks the selections.
60513      */
60514     lock : function(){
60515         this.locked = true;
60516     },
60517
60518     /**
60519      * Unlocks the selections.
60520      */
60521     unlock : function(){
60522         this.locked = false;
60523     },
60524
60525     /**
60526      * Returns true if the selections are locked.
60527      * @return {Boolean}
60528      */
60529     isLocked : function(){
60530         return this.locked;
60531     }
60532 });/*
60533  * Based on:
60534  * Ext JS Library 1.1.1
60535  * Copyright(c) 2006-2007, Ext JS, LLC.
60536  *
60537  * Originally Released Under LGPL - original licence link has changed is not relivant.
60538  *
60539  * Fork - LGPL
60540  * <script type="text/javascript">
60541  */
60542 /**
60543  * @extends Roo.grid.AbstractSelectionModel
60544  * @class Roo.grid.RowSelectionModel
60545  * The default SelectionModel used by {@link Roo.grid.Grid}.
60546  * It supports multiple selections and keyboard selection/navigation. 
60547  * @constructor
60548  * @param {Object} config
60549  */
60550 Roo.grid.RowSelectionModel = function(config){
60551     Roo.apply(this, config);
60552     this.selections = new Roo.util.MixedCollection(false, function(o){
60553         return o.id;
60554     });
60555
60556     this.last = false;
60557     this.lastActive = false;
60558
60559     this.addEvents({
60560         /**
60561         * @event selectionchange
60562         * Fires when the selection changes
60563         * @param {SelectionModel} this
60564         */
60565        "selectionchange" : true,
60566        /**
60567         * @event afterselectionchange
60568         * Fires after the selection changes (eg. by key press or clicking)
60569         * @param {SelectionModel} this
60570         */
60571        "afterselectionchange" : true,
60572        /**
60573         * @event beforerowselect
60574         * Fires when a row is selected being selected, return false to cancel.
60575         * @param {SelectionModel} this
60576         * @param {Number} rowIndex The selected index
60577         * @param {Boolean} keepExisting False if other selections will be cleared
60578         */
60579        "beforerowselect" : true,
60580        /**
60581         * @event rowselect
60582         * Fires when a row is selected.
60583         * @param {SelectionModel} this
60584         * @param {Number} rowIndex The selected index
60585         * @param {Roo.data.Record} r The record
60586         */
60587        "rowselect" : true,
60588        /**
60589         * @event rowdeselect
60590         * Fires when a row is deselected.
60591         * @param {SelectionModel} this
60592         * @param {Number} rowIndex The selected index
60593         */
60594         "rowdeselect" : true
60595     });
60596     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
60597     this.locked = false;
60598 };
60599
60600 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
60601     /**
60602      * @cfg {Boolean} singleSelect
60603      * True to allow selection of only one row at a time (defaults to false)
60604      */
60605     singleSelect : false,
60606
60607     // private
60608     initEvents : function(){
60609
60610         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
60611             this.grid.on("mousedown", this.handleMouseDown, this);
60612         }else{ // allow click to work like normal
60613             this.grid.on("rowclick", this.handleDragableRowClick, this);
60614         }
60615         // bootstrap does not have a view..
60616         var view = this.grid.view ? this.grid.view : this.grid;
60617         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
60618             "up" : function(e){
60619                 if(!e.shiftKey){
60620                     this.selectPrevious(e.shiftKey);
60621                 }else if(this.last !== false && this.lastActive !== false){
60622                     var last = this.last;
60623                     this.selectRange(this.last,  this.lastActive-1);
60624                     view.focusRow(this.lastActive);
60625                     if(last !== false){
60626                         this.last = last;
60627                     }
60628                 }else{
60629                     this.selectFirstRow();
60630                 }
60631                 this.fireEvent("afterselectionchange", this);
60632             },
60633             "down" : function(e){
60634                 if(!e.shiftKey){
60635                     this.selectNext(e.shiftKey);
60636                 }else if(this.last !== false && this.lastActive !== false){
60637                     var last = this.last;
60638                     this.selectRange(this.last,  this.lastActive+1);
60639                     view.focusRow(this.lastActive);
60640                     if(last !== false){
60641                         this.last = last;
60642                     }
60643                 }else{
60644                     this.selectFirstRow();
60645                 }
60646                 this.fireEvent("afterselectionchange", this);
60647             },
60648             scope: this
60649         });
60650
60651          
60652         view.on("refresh", this.onRefresh, this);
60653         view.on("rowupdated", this.onRowUpdated, this);
60654         view.on("rowremoved", this.onRemove, this);
60655     },
60656
60657     // private
60658     onRefresh : function(){
60659         var ds = this.grid.ds, i, v = this.grid.view;
60660         var s = this.selections;
60661         s.each(function(r){
60662             if((i = ds.indexOfId(r.id)) != -1){
60663                 v.onRowSelect(i);
60664                 s.add(ds.getAt(i)); // updating the selection relate data
60665             }else{
60666                 s.remove(r);
60667             }
60668         });
60669     },
60670
60671     // private
60672     onRemove : function(v, index, r){
60673         this.selections.remove(r);
60674     },
60675
60676     // private
60677     onRowUpdated : function(v, index, r){
60678         if(this.isSelected(r)){
60679             v.onRowSelect(index);
60680         }
60681     },
60682
60683     /**
60684      * Select records.
60685      * @param {Array} records The records to select
60686      * @param {Boolean} keepExisting (optional) True to keep existing selections
60687      */
60688     selectRecords : function(records, keepExisting){
60689         if(!keepExisting){
60690             this.clearSelections();
60691         }
60692         var ds = this.grid.ds;
60693         for(var i = 0, len = records.length; i < len; i++){
60694             this.selectRow(ds.indexOf(records[i]), true);
60695         }
60696     },
60697
60698     /**
60699      * Gets the number of selected rows.
60700      * @return {Number}
60701      */
60702     getCount : function(){
60703         return this.selections.length;
60704     },
60705
60706     /**
60707      * Selects the first row in the grid.
60708      */
60709     selectFirstRow : function(){
60710         this.selectRow(0);
60711     },
60712
60713     /**
60714      * Select the last row.
60715      * @param {Boolean} keepExisting (optional) True to keep existing selections
60716      */
60717     selectLastRow : function(keepExisting){
60718         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
60719     },
60720
60721     /**
60722      * Selects the row immediately following the last selected row.
60723      * @param {Boolean} keepExisting (optional) True to keep existing selections
60724      */
60725     selectNext : function(keepExisting){
60726         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
60727             this.selectRow(this.last+1, keepExisting);
60728             var view = this.grid.view ? this.grid.view : this.grid;
60729             view.focusRow(this.last);
60730         }
60731     },
60732
60733     /**
60734      * Selects the row that precedes the last selected row.
60735      * @param {Boolean} keepExisting (optional) True to keep existing selections
60736      */
60737     selectPrevious : function(keepExisting){
60738         if(this.last){
60739             this.selectRow(this.last-1, keepExisting);
60740             var view = this.grid.view ? this.grid.view : this.grid;
60741             view.focusRow(this.last);
60742         }
60743     },
60744
60745     /**
60746      * Returns the selected records
60747      * @return {Array} Array of selected records
60748      */
60749     getSelections : function(){
60750         return [].concat(this.selections.items);
60751     },
60752
60753     /**
60754      * Returns the first selected record.
60755      * @return {Record}
60756      */
60757     getSelected : function(){
60758         return this.selections.itemAt(0);
60759     },
60760
60761
60762     /**
60763      * Clears all selections.
60764      */
60765     clearSelections : function(fast){
60766         if(this.locked) {
60767             return;
60768         }
60769         if(fast !== true){
60770             var ds = this.grid.ds;
60771             var s = this.selections;
60772             s.each(function(r){
60773                 this.deselectRow(ds.indexOfId(r.id));
60774             }, this);
60775             s.clear();
60776         }else{
60777             this.selections.clear();
60778         }
60779         this.last = false;
60780     },
60781
60782
60783     /**
60784      * Selects all rows.
60785      */
60786     selectAll : function(){
60787         if(this.locked) {
60788             return;
60789         }
60790         this.selections.clear();
60791         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
60792             this.selectRow(i, true);
60793         }
60794     },
60795
60796     /**
60797      * Returns True if there is a selection.
60798      * @return {Boolean}
60799      */
60800     hasSelection : function(){
60801         return this.selections.length > 0;
60802     },
60803
60804     /**
60805      * Returns True if the specified row is selected.
60806      * @param {Number/Record} record The record or index of the record to check
60807      * @return {Boolean}
60808      */
60809     isSelected : function(index){
60810         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
60811         return (r && this.selections.key(r.id) ? true : false);
60812     },
60813
60814     /**
60815      * Returns True if the specified record id is selected.
60816      * @param {String} id The id of record to check
60817      * @return {Boolean}
60818      */
60819     isIdSelected : function(id){
60820         return (this.selections.key(id) ? true : false);
60821     },
60822
60823     // private
60824     handleMouseDown : function(e, t)
60825     {
60826         var view = this.grid.view ? this.grid.view : this.grid;
60827         var rowIndex;
60828         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
60829             return;
60830         };
60831         if(e.shiftKey && this.last !== false){
60832             var last = this.last;
60833             this.selectRange(last, rowIndex, e.ctrlKey);
60834             this.last = last; // reset the last
60835             view.focusRow(rowIndex);
60836         }else{
60837             var isSelected = this.isSelected(rowIndex);
60838             if(e.button !== 0 && isSelected){
60839                 view.focusRow(rowIndex);
60840             }else if(e.ctrlKey && isSelected){
60841                 this.deselectRow(rowIndex);
60842             }else if(!isSelected){
60843                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
60844                 view.focusRow(rowIndex);
60845             }
60846         }
60847         this.fireEvent("afterselectionchange", this);
60848     },
60849     // private
60850     handleDragableRowClick :  function(grid, rowIndex, e) 
60851     {
60852         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
60853             this.selectRow(rowIndex, false);
60854             var view = this.grid.view ? this.grid.view : this.grid;
60855             view.focusRow(rowIndex);
60856              this.fireEvent("afterselectionchange", this);
60857         }
60858     },
60859     
60860     /**
60861      * Selects multiple rows.
60862      * @param {Array} rows Array of the indexes of the row to select
60863      * @param {Boolean} keepExisting (optional) True to keep existing selections
60864      */
60865     selectRows : function(rows, keepExisting){
60866         if(!keepExisting){
60867             this.clearSelections();
60868         }
60869         for(var i = 0, len = rows.length; i < len; i++){
60870             this.selectRow(rows[i], true);
60871         }
60872     },
60873
60874     /**
60875      * Selects a range of rows. All rows in between startRow and endRow are also selected.
60876      * @param {Number} startRow The index of the first row in the range
60877      * @param {Number} endRow The index of the last row in the range
60878      * @param {Boolean} keepExisting (optional) True to retain existing selections
60879      */
60880     selectRange : function(startRow, endRow, keepExisting){
60881         if(this.locked) {
60882             return;
60883         }
60884         if(!keepExisting){
60885             this.clearSelections();
60886         }
60887         if(startRow <= endRow){
60888             for(var i = startRow; i <= endRow; i++){
60889                 this.selectRow(i, true);
60890             }
60891         }else{
60892             for(var i = startRow; i >= endRow; i--){
60893                 this.selectRow(i, true);
60894             }
60895         }
60896     },
60897
60898     /**
60899      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
60900      * @param {Number} startRow The index of the first row in the range
60901      * @param {Number} endRow The index of the last row in the range
60902      */
60903     deselectRange : function(startRow, endRow, preventViewNotify){
60904         if(this.locked) {
60905             return;
60906         }
60907         for(var i = startRow; i <= endRow; i++){
60908             this.deselectRow(i, preventViewNotify);
60909         }
60910     },
60911
60912     /**
60913      * Selects a row.
60914      * @param {Number} row The index of the row to select
60915      * @param {Boolean} keepExisting (optional) True to keep existing selections
60916      */
60917     selectRow : function(index, keepExisting, preventViewNotify){
60918         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
60919             return;
60920         }
60921         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
60922             if(!keepExisting || this.singleSelect){
60923                 this.clearSelections();
60924             }
60925             var r = this.grid.ds.getAt(index);
60926             this.selections.add(r);
60927             this.last = this.lastActive = index;
60928             if(!preventViewNotify){
60929                 var view = this.grid.view ? this.grid.view : this.grid;
60930                 view.onRowSelect(index);
60931             }
60932             this.fireEvent("rowselect", this, index, r);
60933             this.fireEvent("selectionchange", this);
60934         }
60935     },
60936
60937     /**
60938      * Deselects a row.
60939      * @param {Number} row The index of the row to deselect
60940      */
60941     deselectRow : function(index, preventViewNotify){
60942         if(this.locked) {
60943             return;
60944         }
60945         if(this.last == index){
60946             this.last = false;
60947         }
60948         if(this.lastActive == index){
60949             this.lastActive = false;
60950         }
60951         var r = this.grid.ds.getAt(index);
60952         this.selections.remove(r);
60953         if(!preventViewNotify){
60954             var view = this.grid.view ? this.grid.view : this.grid;
60955             view.onRowDeselect(index);
60956         }
60957         this.fireEvent("rowdeselect", this, index);
60958         this.fireEvent("selectionchange", this);
60959     },
60960
60961     // private
60962     restoreLast : function(){
60963         if(this._last){
60964             this.last = this._last;
60965         }
60966     },
60967
60968     // private
60969     acceptsNav : function(row, col, cm){
60970         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60971     },
60972
60973     // private
60974     onEditorKey : function(field, e){
60975         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
60976         if(k == e.TAB){
60977             e.stopEvent();
60978             ed.completeEdit();
60979             if(e.shiftKey){
60980                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60981             }else{
60982                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60983             }
60984         }else if(k == e.ENTER && !e.ctrlKey){
60985             e.stopEvent();
60986             ed.completeEdit();
60987             if(e.shiftKey){
60988                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
60989             }else{
60990                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
60991             }
60992         }else if(k == e.ESC){
60993             ed.cancelEdit();
60994         }
60995         if(newCell){
60996             g.startEditing(newCell[0], newCell[1]);
60997         }
60998     }
60999 });/*
61000  * Based on:
61001  * Ext JS Library 1.1.1
61002  * Copyright(c) 2006-2007, Ext JS, LLC.
61003  *
61004  * Originally Released Under LGPL - original licence link has changed is not relivant.
61005  *
61006  * Fork - LGPL
61007  * <script type="text/javascript">
61008  */
61009 /**
61010  * @class Roo.grid.CellSelectionModel
61011  * @extends Roo.grid.AbstractSelectionModel
61012  * This class provides the basic implementation for cell selection in a grid.
61013  * @constructor
61014  * @param {Object} config The object containing the configuration of this model.
61015  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
61016  */
61017 Roo.grid.CellSelectionModel = function(config){
61018     Roo.apply(this, config);
61019
61020     this.selection = null;
61021
61022     this.addEvents({
61023         /**
61024              * @event beforerowselect
61025              * Fires before a cell is selected.
61026              * @param {SelectionModel} this
61027              * @param {Number} rowIndex The selected row index
61028              * @param {Number} colIndex The selected cell index
61029              */
61030             "beforecellselect" : true,
61031         /**
61032              * @event cellselect
61033              * Fires when a cell is selected.
61034              * @param {SelectionModel} this
61035              * @param {Number} rowIndex The selected row index
61036              * @param {Number} colIndex The selected cell index
61037              */
61038             "cellselect" : true,
61039         /**
61040              * @event selectionchange
61041              * Fires when the active selection changes.
61042              * @param {SelectionModel} this
61043              * @param {Object} selection null for no selection or an object (o) with two properties
61044                 <ul>
61045                 <li>o.record: the record object for the row the selection is in</li>
61046                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
61047                 </ul>
61048              */
61049             "selectionchange" : true,
61050         /**
61051              * @event tabend
61052              * Fires when the tab (or enter) was pressed on the last editable cell
61053              * You can use this to trigger add new row.
61054              * @param {SelectionModel} this
61055              */
61056             "tabend" : true,
61057          /**
61058              * @event beforeeditnext
61059              * Fires before the next editable sell is made active
61060              * You can use this to skip to another cell or fire the tabend
61061              *    if you set cell to false
61062              * @param {Object} eventdata object : { cell : [ row, col ] } 
61063              */
61064             "beforeeditnext" : true
61065     });
61066     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
61067 };
61068
61069 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
61070     
61071     enter_is_tab: false,
61072
61073     /** @ignore */
61074     initEvents : function(){
61075         this.grid.on("mousedown", this.handleMouseDown, this);
61076         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
61077         var view = this.grid.view;
61078         view.on("refresh", this.onViewChange, this);
61079         view.on("rowupdated", this.onRowUpdated, this);
61080         view.on("beforerowremoved", this.clearSelections, this);
61081         view.on("beforerowsinserted", this.clearSelections, this);
61082         if(this.grid.isEditor){
61083             this.grid.on("beforeedit", this.beforeEdit,  this);
61084         }
61085     },
61086
61087         //private
61088     beforeEdit : function(e){
61089         this.select(e.row, e.column, false, true, e.record);
61090     },
61091
61092         //private
61093     onRowUpdated : function(v, index, r){
61094         if(this.selection && this.selection.record == r){
61095             v.onCellSelect(index, this.selection.cell[1]);
61096         }
61097     },
61098
61099         //private
61100     onViewChange : function(){
61101         this.clearSelections(true);
61102     },
61103
61104         /**
61105          * Returns the currently selected cell,.
61106          * @return {Array} The selected cell (row, column) or null if none selected.
61107          */
61108     getSelectedCell : function(){
61109         return this.selection ? this.selection.cell : null;
61110     },
61111
61112     /**
61113      * Clears all selections.
61114      * @param {Boolean} true to prevent the gridview from being notified about the change.
61115      */
61116     clearSelections : function(preventNotify){
61117         var s = this.selection;
61118         if(s){
61119             if(preventNotify !== true){
61120                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
61121             }
61122             this.selection = null;
61123             this.fireEvent("selectionchange", this, null);
61124         }
61125     },
61126
61127     /**
61128      * Returns true if there is a selection.
61129      * @return {Boolean}
61130      */
61131     hasSelection : function(){
61132         return this.selection ? true : false;
61133     },
61134
61135     /** @ignore */
61136     handleMouseDown : function(e, t){
61137         var v = this.grid.getView();
61138         if(this.isLocked()){
61139             return;
61140         };
61141         var row = v.findRowIndex(t);
61142         var cell = v.findCellIndex(t);
61143         if(row !== false && cell !== false){
61144             this.select(row, cell);
61145         }
61146     },
61147
61148     /**
61149      * Selects a cell.
61150      * @param {Number} rowIndex
61151      * @param {Number} collIndex
61152      */
61153     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
61154         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
61155             this.clearSelections();
61156             r = r || this.grid.dataSource.getAt(rowIndex);
61157             this.selection = {
61158                 record : r,
61159                 cell : [rowIndex, colIndex]
61160             };
61161             if(!preventViewNotify){
61162                 var v = this.grid.getView();
61163                 v.onCellSelect(rowIndex, colIndex);
61164                 if(preventFocus !== true){
61165                     v.focusCell(rowIndex, colIndex);
61166                 }
61167             }
61168             this.fireEvent("cellselect", this, rowIndex, colIndex);
61169             this.fireEvent("selectionchange", this, this.selection);
61170         }
61171     },
61172
61173         //private
61174     isSelectable : function(rowIndex, colIndex, cm){
61175         return !cm.isHidden(colIndex);
61176     },
61177
61178     /** @ignore */
61179     handleKeyDown : function(e){
61180         //Roo.log('Cell Sel Model handleKeyDown');
61181         if(!e.isNavKeyPress()){
61182             return;
61183         }
61184         var g = this.grid, s = this.selection;
61185         if(!s){
61186             e.stopEvent();
61187             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
61188             if(cell){
61189                 this.select(cell[0], cell[1]);
61190             }
61191             return;
61192         }
61193         var sm = this;
61194         var walk = function(row, col, step){
61195             return g.walkCells(row, col, step, sm.isSelectable,  sm);
61196         };
61197         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
61198         var newCell;
61199
61200       
61201
61202         switch(k){
61203             case e.TAB:
61204                 // handled by onEditorKey
61205                 if (g.isEditor && g.editing) {
61206                     return;
61207                 }
61208                 if(e.shiftKey) {
61209                     newCell = walk(r, c-1, -1);
61210                 } else {
61211                     newCell = walk(r, c+1, 1);
61212                 }
61213                 break;
61214             
61215             case e.DOWN:
61216                newCell = walk(r+1, c, 1);
61217                 break;
61218             
61219             case e.UP:
61220                 newCell = walk(r-1, c, -1);
61221                 break;
61222             
61223             case e.RIGHT:
61224                 newCell = walk(r, c+1, 1);
61225                 break;
61226             
61227             case e.LEFT:
61228                 newCell = walk(r, c-1, -1);
61229                 break;
61230             
61231             case e.ENTER:
61232                 
61233                 if(g.isEditor && !g.editing){
61234                    g.startEditing(r, c);
61235                    e.stopEvent();
61236                    return;
61237                 }
61238                 
61239                 
61240              break;
61241         };
61242         if(newCell){
61243             this.select(newCell[0], newCell[1]);
61244             e.stopEvent();
61245             
61246         }
61247     },
61248
61249     acceptsNav : function(row, col, cm){
61250         return !cm.isHidden(col) && cm.isCellEditable(col, row);
61251     },
61252     /**
61253      * Selects a cell.
61254      * @param {Number} field (not used) - as it's normally used as a listener
61255      * @param {Number} e - event - fake it by using
61256      *
61257      * var e = Roo.EventObjectImpl.prototype;
61258      * e.keyCode = e.TAB
61259      *
61260      * 
61261      */
61262     onEditorKey : function(field, e){
61263         
61264         var k = e.getKey(),
61265             newCell,
61266             g = this.grid,
61267             ed = g.activeEditor,
61268             forward = false;
61269         ///Roo.log('onEditorKey' + k);
61270         
61271         
61272         if (this.enter_is_tab && k == e.ENTER) {
61273             k = e.TAB;
61274         }
61275         
61276         if(k == e.TAB){
61277             if(e.shiftKey){
61278                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
61279             }else{
61280                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61281                 forward = true;
61282             }
61283             
61284             e.stopEvent();
61285             
61286         } else if(k == e.ENTER &&  !e.ctrlKey){
61287             ed.completeEdit();
61288             e.stopEvent();
61289             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61290         
61291                 } else if(k == e.ESC){
61292             ed.cancelEdit();
61293         }
61294                 
61295         if (newCell) {
61296             var ecall = { cell : newCell, forward : forward };
61297             this.fireEvent('beforeeditnext', ecall );
61298             newCell = ecall.cell;
61299                         forward = ecall.forward;
61300         }
61301                 
61302         if(newCell){
61303             //Roo.log('next cell after edit');
61304             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
61305         } else if (forward) {
61306             // tabbed past last
61307             this.fireEvent.defer(100, this, ['tabend',this]);
61308         }
61309     }
61310 });/*
61311  * Based on:
61312  * Ext JS Library 1.1.1
61313  * Copyright(c) 2006-2007, Ext JS, LLC.
61314  *
61315  * Originally Released Under LGPL - original licence link has changed is not relivant.
61316  *
61317  * Fork - LGPL
61318  * <script type="text/javascript">
61319  */
61320  
61321 /**
61322  * @class Roo.grid.EditorGrid
61323  * @extends Roo.grid.Grid
61324  * Class for creating and editable grid.
61325  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
61326  * The container MUST have some type of size defined for the grid to fill. The container will be 
61327  * automatically set to position relative if it isn't already.
61328  * @param {Object} dataSource The data model to bind to
61329  * @param {Object} colModel The column model with info about this grid's columns
61330  */
61331 Roo.grid.EditorGrid = function(container, config){
61332     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
61333     this.getGridEl().addClass("xedit-grid");
61334
61335     if(!this.selModel){
61336         this.selModel = new Roo.grid.CellSelectionModel();
61337     }
61338
61339     this.activeEditor = null;
61340
61341         this.addEvents({
61342             /**
61343              * @event beforeedit
61344              * Fires before cell editing is triggered. The edit event object has the following properties <br />
61345              * <ul style="padding:5px;padding-left:16px;">
61346              * <li>grid - This grid</li>
61347              * <li>record - The record being edited</li>
61348              * <li>field - The field name being edited</li>
61349              * <li>value - The value for the field being edited.</li>
61350              * <li>row - The grid row index</li>
61351              * <li>column - The grid column index</li>
61352              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
61353              * </ul>
61354              * @param {Object} e An edit event (see above for description)
61355              */
61356             "beforeedit" : true,
61357             /**
61358              * @event afteredit
61359              * Fires after a cell is edited. <br />
61360              * <ul style="padding:5px;padding-left:16px;">
61361              * <li>grid - This grid</li>
61362              * <li>record - The record being edited</li>
61363              * <li>field - The field name being edited</li>
61364              * <li>value - The value being set</li>
61365              * <li>originalValue - The original value for the field, before the edit.</li>
61366              * <li>row - The grid row index</li>
61367              * <li>column - The grid column index</li>
61368              * </ul>
61369              * @param {Object} e An edit event (see above for description)
61370              */
61371             "afteredit" : true,
61372             /**
61373              * @event validateedit
61374              * Fires after a cell is edited, but before the value is set in the record. 
61375          * You can use this to modify the value being set in the field, Return false
61376              * to cancel the change. The edit event object has the following properties <br />
61377              * <ul style="padding:5px;padding-left:16px;">
61378          * <li>editor - This editor</li>
61379              * <li>grid - This grid</li>
61380              * <li>record - The record being edited</li>
61381              * <li>field - The field name being edited</li>
61382              * <li>value - The value being set</li>
61383              * <li>originalValue - The original value for the field, before the edit.</li>
61384              * <li>row - The grid row index</li>
61385              * <li>column - The grid column index</li>
61386              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
61387              * </ul>
61388              * @param {Object} e An edit event (see above for description)
61389              */
61390             "validateedit" : true
61391         });
61392     this.on("bodyscroll", this.stopEditing,  this);
61393     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
61394 };
61395
61396 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
61397     /**
61398      * @cfg {Number} clicksToEdit
61399      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
61400      */
61401     clicksToEdit: 2,
61402
61403     // private
61404     isEditor : true,
61405     // private
61406     trackMouseOver: false, // causes very odd FF errors
61407
61408     onCellDblClick : function(g, row, col){
61409         this.startEditing(row, col);
61410     },
61411
61412     onEditComplete : function(ed, value, startValue){
61413         this.editing = false;
61414         this.activeEditor = null;
61415         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
61416         var r = ed.record;
61417         var field = this.colModel.getDataIndex(ed.col);
61418         var e = {
61419             grid: this,
61420             record: r,
61421             field: field,
61422             originalValue: startValue,
61423             value: value,
61424             row: ed.row,
61425             column: ed.col,
61426             cancel:false,
61427             editor: ed
61428         };
61429         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
61430         cell.show();
61431           
61432         if(String(value) !== String(startValue)){
61433             
61434             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
61435                 r.set(field, e.value);
61436                 // if we are dealing with a combo box..
61437                 // then we also set the 'name' colum to be the displayField
61438                 if (ed.field.displayField && ed.field.name) {
61439                     r.set(ed.field.name, ed.field.el.dom.value);
61440                 }
61441                 
61442                 delete e.cancel; //?? why!!!
61443                 this.fireEvent("afteredit", e);
61444             }
61445         } else {
61446             this.fireEvent("afteredit", e); // always fire it!
61447         }
61448         this.view.focusCell(ed.row, ed.col);
61449     },
61450
61451     /**
61452      * Starts editing the specified for the specified row/column
61453      * @param {Number} rowIndex
61454      * @param {Number} colIndex
61455      */
61456     startEditing : function(row, col){
61457         this.stopEditing();
61458         if(this.colModel.isCellEditable(col, row)){
61459             this.view.ensureVisible(row, col, true);
61460           
61461             var r = this.dataSource.getAt(row);
61462             var field = this.colModel.getDataIndex(col);
61463             var cell = Roo.get(this.view.getCell(row,col));
61464             var e = {
61465                 grid: this,
61466                 record: r,
61467                 field: field,
61468                 value: r.data[field],
61469                 row: row,
61470                 column: col,
61471                 cancel:false 
61472             };
61473             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
61474                 this.editing = true;
61475                 var ed = this.colModel.getCellEditor(col, row);
61476                 
61477                 if (!ed) {
61478                     return;
61479                 }
61480                 if(!ed.rendered){
61481                     ed.render(ed.parentEl || document.body);
61482                 }
61483                 ed.field.reset();
61484                
61485                 cell.hide();
61486                 
61487                 (function(){ // complex but required for focus issues in safari, ie and opera
61488                     ed.row = row;
61489                     ed.col = col;
61490                     ed.record = r;
61491                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
61492                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
61493                     this.activeEditor = ed;
61494                     var v = r.data[field];
61495                     ed.startEdit(this.view.getCell(row, col), v);
61496                     // combo's with 'displayField and name set
61497                     if (ed.field.displayField && ed.field.name) {
61498                         ed.field.el.dom.value = r.data[ed.field.name];
61499                     }
61500                     
61501                     
61502                 }).defer(50, this);
61503             }
61504         }
61505     },
61506         
61507     /**
61508      * Stops any active editing
61509      */
61510     stopEditing : function(){
61511         if(this.activeEditor){
61512             this.activeEditor.completeEdit();
61513         }
61514         this.activeEditor = null;
61515     },
61516         
61517          /**
61518      * Called to get grid's drag proxy text, by default returns this.ddText.
61519      * @return {String}
61520      */
61521     getDragDropText : function(){
61522         var count = this.selModel.getSelectedCell() ? 1 : 0;
61523         return String.format(this.ddText, count, count == 1 ? '' : 's');
61524     }
61525         
61526 });/*
61527  * Based on:
61528  * Ext JS Library 1.1.1
61529  * Copyright(c) 2006-2007, Ext JS, LLC.
61530  *
61531  * Originally Released Under LGPL - original licence link has changed is not relivant.
61532  *
61533  * Fork - LGPL
61534  * <script type="text/javascript">
61535  */
61536
61537 // private - not really -- you end up using it !
61538 // This is a support class used internally by the Grid components
61539
61540 /**
61541  * @class Roo.grid.GridEditor
61542  * @extends Roo.Editor
61543  * Class for creating and editable grid elements.
61544  * @param {Object} config any settings (must include field)
61545  */
61546 Roo.grid.GridEditor = function(field, config){
61547     if (!config && field.field) {
61548         config = field;
61549         field = Roo.factory(config.field, Roo.form);
61550     }
61551     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
61552     field.monitorTab = false;
61553 };
61554
61555 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
61556     
61557     /**
61558      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
61559      */
61560     
61561     alignment: "tl-tl",
61562     autoSize: "width",
61563     hideEl : false,
61564     cls: "x-small-editor x-grid-editor",
61565     shim:false,
61566     shadow:"frame"
61567 });/*
61568  * Based on:
61569  * Ext JS Library 1.1.1
61570  * Copyright(c) 2006-2007, Ext JS, LLC.
61571  *
61572  * Originally Released Under LGPL - original licence link has changed is not relivant.
61573  *
61574  * Fork - LGPL
61575  * <script type="text/javascript">
61576  */
61577   
61578
61579   
61580 Roo.grid.PropertyRecord = Roo.data.Record.create([
61581     {name:'name',type:'string'},  'value'
61582 ]);
61583
61584
61585 Roo.grid.PropertyStore = function(grid, source){
61586     this.grid = grid;
61587     this.store = new Roo.data.Store({
61588         recordType : Roo.grid.PropertyRecord
61589     });
61590     this.store.on('update', this.onUpdate,  this);
61591     if(source){
61592         this.setSource(source);
61593     }
61594     Roo.grid.PropertyStore.superclass.constructor.call(this);
61595 };
61596
61597
61598
61599 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
61600     setSource : function(o){
61601         this.source = o;
61602         this.store.removeAll();
61603         var data = [];
61604         for(var k in o){
61605             if(this.isEditableValue(o[k])){
61606                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
61607             }
61608         }
61609         this.store.loadRecords({records: data}, {}, true);
61610     },
61611
61612     onUpdate : function(ds, record, type){
61613         if(type == Roo.data.Record.EDIT){
61614             var v = record.data['value'];
61615             var oldValue = record.modified['value'];
61616             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
61617                 this.source[record.id] = v;
61618                 record.commit();
61619                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
61620             }else{
61621                 record.reject();
61622             }
61623         }
61624     },
61625
61626     getProperty : function(row){
61627        return this.store.getAt(row);
61628     },
61629
61630     isEditableValue: function(val){
61631         if(val && val instanceof Date){
61632             return true;
61633         }else if(typeof val == 'object' || typeof val == 'function'){
61634             return false;
61635         }
61636         return true;
61637     },
61638
61639     setValue : function(prop, value){
61640         this.source[prop] = value;
61641         this.store.getById(prop).set('value', value);
61642     },
61643
61644     getSource : function(){
61645         return this.source;
61646     }
61647 });
61648
61649 Roo.grid.PropertyColumnModel = function(grid, store){
61650     this.grid = grid;
61651     var g = Roo.grid;
61652     g.PropertyColumnModel.superclass.constructor.call(this, [
61653         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
61654         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
61655     ]);
61656     this.store = store;
61657     this.bselect = Roo.DomHelper.append(document.body, {
61658         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
61659             {tag: 'option', value: 'true', html: 'true'},
61660             {tag: 'option', value: 'false', html: 'false'}
61661         ]
61662     });
61663     Roo.id(this.bselect);
61664     var f = Roo.form;
61665     this.editors = {
61666         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
61667         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
61668         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
61669         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
61670         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
61671     };
61672     this.renderCellDelegate = this.renderCell.createDelegate(this);
61673     this.renderPropDelegate = this.renderProp.createDelegate(this);
61674 };
61675
61676 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
61677     
61678     
61679     nameText : 'Name',
61680     valueText : 'Value',
61681     
61682     dateFormat : 'm/j/Y',
61683     
61684     
61685     renderDate : function(dateVal){
61686         return dateVal.dateFormat(this.dateFormat);
61687     },
61688
61689     renderBool : function(bVal){
61690         return bVal ? 'true' : 'false';
61691     },
61692
61693     isCellEditable : function(colIndex, rowIndex){
61694         return colIndex == 1;
61695     },
61696
61697     getRenderer : function(col){
61698         return col == 1 ?
61699             this.renderCellDelegate : this.renderPropDelegate;
61700     },
61701
61702     renderProp : function(v){
61703         return this.getPropertyName(v);
61704     },
61705
61706     renderCell : function(val){
61707         var rv = val;
61708         if(val instanceof Date){
61709             rv = this.renderDate(val);
61710         }else if(typeof val == 'boolean'){
61711             rv = this.renderBool(val);
61712         }
61713         return Roo.util.Format.htmlEncode(rv);
61714     },
61715
61716     getPropertyName : function(name){
61717         var pn = this.grid.propertyNames;
61718         return pn && pn[name] ? pn[name] : name;
61719     },
61720
61721     getCellEditor : function(colIndex, rowIndex){
61722         var p = this.store.getProperty(rowIndex);
61723         var n = p.data['name'], val = p.data['value'];
61724         
61725         if(typeof(this.grid.customEditors[n]) == 'string'){
61726             return this.editors[this.grid.customEditors[n]];
61727         }
61728         if(typeof(this.grid.customEditors[n]) != 'undefined'){
61729             return this.grid.customEditors[n];
61730         }
61731         if(val instanceof Date){
61732             return this.editors['date'];
61733         }else if(typeof val == 'number'){
61734             return this.editors['number'];
61735         }else if(typeof val == 'boolean'){
61736             return this.editors['boolean'];
61737         }else{
61738             return this.editors['string'];
61739         }
61740     }
61741 });
61742
61743 /**
61744  * @class Roo.grid.PropertyGrid
61745  * @extends Roo.grid.EditorGrid
61746  * This class represents the  interface of a component based property grid control.
61747  * <br><br>Usage:<pre><code>
61748  var grid = new Roo.grid.PropertyGrid("my-container-id", {
61749       
61750  });
61751  // set any options
61752  grid.render();
61753  * </code></pre>
61754   
61755  * @constructor
61756  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61757  * The container MUST have some type of size defined for the grid to fill. The container will be
61758  * automatically set to position relative if it isn't already.
61759  * @param {Object} config A config object that sets properties on this grid.
61760  */
61761 Roo.grid.PropertyGrid = function(container, config){
61762     config = config || {};
61763     var store = new Roo.grid.PropertyStore(this);
61764     this.store = store;
61765     var cm = new Roo.grid.PropertyColumnModel(this, store);
61766     store.store.sort('name', 'ASC');
61767     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
61768         ds: store.store,
61769         cm: cm,
61770         enableColLock:false,
61771         enableColumnMove:false,
61772         stripeRows:false,
61773         trackMouseOver: false,
61774         clicksToEdit:1
61775     }, config));
61776     this.getGridEl().addClass('x-props-grid');
61777     this.lastEditRow = null;
61778     this.on('columnresize', this.onColumnResize, this);
61779     this.addEvents({
61780          /**
61781              * @event beforepropertychange
61782              * Fires before a property changes (return false to stop?)
61783              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61784              * @param {String} id Record Id
61785              * @param {String} newval New Value
61786          * @param {String} oldval Old Value
61787              */
61788         "beforepropertychange": true,
61789         /**
61790              * @event propertychange
61791              * Fires after a property changes
61792              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61793              * @param {String} id Record Id
61794              * @param {String} newval New Value
61795          * @param {String} oldval Old Value
61796              */
61797         "propertychange": true
61798     });
61799     this.customEditors = this.customEditors || {};
61800 };
61801 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
61802     
61803      /**
61804      * @cfg {Object} customEditors map of colnames=> custom editors.
61805      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
61806      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
61807      * false disables editing of the field.
61808          */
61809     
61810       /**
61811      * @cfg {Object} propertyNames map of property Names to their displayed value
61812          */
61813     
61814     render : function(){
61815         Roo.grid.PropertyGrid.superclass.render.call(this);
61816         this.autoSize.defer(100, this);
61817     },
61818
61819     autoSize : function(){
61820         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
61821         if(this.view){
61822             this.view.fitColumns();
61823         }
61824     },
61825
61826     onColumnResize : function(){
61827         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
61828         this.autoSize();
61829     },
61830     /**
61831      * Sets the data for the Grid
61832      * accepts a Key => Value object of all the elements avaiable.
61833      * @param {Object} data  to appear in grid.
61834      */
61835     setSource : function(source){
61836         this.store.setSource(source);
61837         //this.autoSize();
61838     },
61839     /**
61840      * Gets all the data from the grid.
61841      * @return {Object} data  data stored in grid
61842      */
61843     getSource : function(){
61844         return this.store.getSource();
61845     }
61846 });/*
61847   
61848  * Licence LGPL
61849  
61850  */
61851  
61852 /**
61853  * @class Roo.grid.Calendar
61854  * @extends Roo.grid.Grid
61855  * This class extends the Grid to provide a calendar widget
61856  * <br><br>Usage:<pre><code>
61857  var grid = new Roo.grid.Calendar("my-container-id", {
61858      ds: myDataStore,
61859      cm: myColModel,
61860      selModel: mySelectionModel,
61861      autoSizeColumns: true,
61862      monitorWindowResize: false,
61863      trackMouseOver: true
61864      eventstore : real data store..
61865  });
61866  // set any options
61867  grid.render();
61868   
61869   * @constructor
61870  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61871  * The container MUST have some type of size defined for the grid to fill. The container will be
61872  * automatically set to position relative if it isn't already.
61873  * @param {Object} config A config object that sets properties on this grid.
61874  */
61875 Roo.grid.Calendar = function(container, config){
61876         // initialize the container
61877         this.container = Roo.get(container);
61878         this.container.update("");
61879         this.container.setStyle("overflow", "hidden");
61880     this.container.addClass('x-grid-container');
61881
61882     this.id = this.container.id;
61883
61884     Roo.apply(this, config);
61885     // check and correct shorthanded configs
61886     
61887     var rows = [];
61888     var d =1;
61889     for (var r = 0;r < 6;r++) {
61890         
61891         rows[r]=[];
61892         for (var c =0;c < 7;c++) {
61893             rows[r][c]= '';
61894         }
61895     }
61896     if (this.eventStore) {
61897         this.eventStore= Roo.factory(this.eventStore, Roo.data);
61898         this.eventStore.on('load',this.onLoad, this);
61899         this.eventStore.on('beforeload',this.clearEvents, this);
61900          
61901     }
61902     
61903     this.dataSource = new Roo.data.Store({
61904             proxy: new Roo.data.MemoryProxy(rows),
61905             reader: new Roo.data.ArrayReader({}, [
61906                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
61907     });
61908
61909     this.dataSource.load();
61910     this.ds = this.dataSource;
61911     this.ds.xmodule = this.xmodule || false;
61912     
61913     
61914     var cellRender = function(v,x,r)
61915     {
61916         return String.format(
61917             '<div class="fc-day  fc-widget-content"><div>' +
61918                 '<div class="fc-event-container"></div>' +
61919                 '<div class="fc-day-number">{0}</div>'+
61920                 
61921                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
61922             '</div></div>', v);
61923     
61924     }
61925     
61926     
61927     this.colModel = new Roo.grid.ColumnModel( [
61928         {
61929             xtype: 'ColumnModel',
61930             xns: Roo.grid,
61931             dataIndex : 'weekday0',
61932             header : 'Sunday',
61933             renderer : cellRender
61934         },
61935         {
61936             xtype: 'ColumnModel',
61937             xns: Roo.grid,
61938             dataIndex : 'weekday1',
61939             header : 'Monday',
61940             renderer : cellRender
61941         },
61942         {
61943             xtype: 'ColumnModel',
61944             xns: Roo.grid,
61945             dataIndex : 'weekday2',
61946             header : 'Tuesday',
61947             renderer : cellRender
61948         },
61949         {
61950             xtype: 'ColumnModel',
61951             xns: Roo.grid,
61952             dataIndex : 'weekday3',
61953             header : 'Wednesday',
61954             renderer : cellRender
61955         },
61956         {
61957             xtype: 'ColumnModel',
61958             xns: Roo.grid,
61959             dataIndex : 'weekday4',
61960             header : 'Thursday',
61961             renderer : cellRender
61962         },
61963         {
61964             xtype: 'ColumnModel',
61965             xns: Roo.grid,
61966             dataIndex : 'weekday5',
61967             header : 'Friday',
61968             renderer : cellRender
61969         },
61970         {
61971             xtype: 'ColumnModel',
61972             xns: Roo.grid,
61973             dataIndex : 'weekday6',
61974             header : 'Saturday',
61975             renderer : cellRender
61976         }
61977     ]);
61978     this.cm = this.colModel;
61979     this.cm.xmodule = this.xmodule || false;
61980  
61981         
61982           
61983     //this.selModel = new Roo.grid.CellSelectionModel();
61984     //this.sm = this.selModel;
61985     //this.selModel.init(this);
61986     
61987     
61988     if(this.width){
61989         this.container.setWidth(this.width);
61990     }
61991
61992     if(this.height){
61993         this.container.setHeight(this.height);
61994     }
61995     /** @private */
61996         this.addEvents({
61997         // raw events
61998         /**
61999          * @event click
62000          * The raw click event for the entire grid.
62001          * @param {Roo.EventObject} e
62002          */
62003         "click" : true,
62004         /**
62005          * @event dblclick
62006          * The raw dblclick event for the entire grid.
62007          * @param {Roo.EventObject} e
62008          */
62009         "dblclick" : true,
62010         /**
62011          * @event contextmenu
62012          * The raw contextmenu event for the entire grid.
62013          * @param {Roo.EventObject} e
62014          */
62015         "contextmenu" : true,
62016         /**
62017          * @event mousedown
62018          * The raw mousedown event for the entire grid.
62019          * @param {Roo.EventObject} e
62020          */
62021         "mousedown" : true,
62022         /**
62023          * @event mouseup
62024          * The raw mouseup event for the entire grid.
62025          * @param {Roo.EventObject} e
62026          */
62027         "mouseup" : true,
62028         /**
62029          * @event mouseover
62030          * The raw mouseover event for the entire grid.
62031          * @param {Roo.EventObject} e
62032          */
62033         "mouseover" : true,
62034         /**
62035          * @event mouseout
62036          * The raw mouseout event for the entire grid.
62037          * @param {Roo.EventObject} e
62038          */
62039         "mouseout" : true,
62040         /**
62041          * @event keypress
62042          * The raw keypress event for the entire grid.
62043          * @param {Roo.EventObject} e
62044          */
62045         "keypress" : true,
62046         /**
62047          * @event keydown
62048          * The raw keydown event for the entire grid.
62049          * @param {Roo.EventObject} e
62050          */
62051         "keydown" : true,
62052
62053         // custom events
62054
62055         /**
62056          * @event cellclick
62057          * Fires when a cell is clicked
62058          * @param {Grid} this
62059          * @param {Number} rowIndex
62060          * @param {Number} columnIndex
62061          * @param {Roo.EventObject} e
62062          */
62063         "cellclick" : true,
62064         /**
62065          * @event celldblclick
62066          * Fires when a cell is double clicked
62067          * @param {Grid} this
62068          * @param {Number} rowIndex
62069          * @param {Number} columnIndex
62070          * @param {Roo.EventObject} e
62071          */
62072         "celldblclick" : true,
62073         /**
62074          * @event rowclick
62075          * Fires when a row is clicked
62076          * @param {Grid} this
62077          * @param {Number} rowIndex
62078          * @param {Roo.EventObject} e
62079          */
62080         "rowclick" : true,
62081         /**
62082          * @event rowdblclick
62083          * Fires when a row is double clicked
62084          * @param {Grid} this
62085          * @param {Number} rowIndex
62086          * @param {Roo.EventObject} e
62087          */
62088         "rowdblclick" : true,
62089         /**
62090          * @event headerclick
62091          * Fires when a header is clicked
62092          * @param {Grid} this
62093          * @param {Number} columnIndex
62094          * @param {Roo.EventObject} e
62095          */
62096         "headerclick" : true,
62097         /**
62098          * @event headerdblclick
62099          * Fires when a header cell is double clicked
62100          * @param {Grid} this
62101          * @param {Number} columnIndex
62102          * @param {Roo.EventObject} e
62103          */
62104         "headerdblclick" : true,
62105         /**
62106          * @event rowcontextmenu
62107          * Fires when a row is right clicked
62108          * @param {Grid} this
62109          * @param {Number} rowIndex
62110          * @param {Roo.EventObject} e
62111          */
62112         "rowcontextmenu" : true,
62113         /**
62114          * @event cellcontextmenu
62115          * Fires when a cell is right clicked
62116          * @param {Grid} this
62117          * @param {Number} rowIndex
62118          * @param {Number} cellIndex
62119          * @param {Roo.EventObject} e
62120          */
62121          "cellcontextmenu" : true,
62122         /**
62123          * @event headercontextmenu
62124          * Fires when a header is right clicked
62125          * @param {Grid} this
62126          * @param {Number} columnIndex
62127          * @param {Roo.EventObject} e
62128          */
62129         "headercontextmenu" : true,
62130         /**
62131          * @event bodyscroll
62132          * Fires when the body element is scrolled
62133          * @param {Number} scrollLeft
62134          * @param {Number} scrollTop
62135          */
62136         "bodyscroll" : true,
62137         /**
62138          * @event columnresize
62139          * Fires when the user resizes a column
62140          * @param {Number} columnIndex
62141          * @param {Number} newSize
62142          */
62143         "columnresize" : true,
62144         /**
62145          * @event columnmove
62146          * Fires when the user moves a column
62147          * @param {Number} oldIndex
62148          * @param {Number} newIndex
62149          */
62150         "columnmove" : true,
62151         /**
62152          * @event startdrag
62153          * Fires when row(s) start being dragged
62154          * @param {Grid} this
62155          * @param {Roo.GridDD} dd The drag drop object
62156          * @param {event} e The raw browser event
62157          */
62158         "startdrag" : true,
62159         /**
62160          * @event enddrag
62161          * Fires when a drag operation is complete
62162          * @param {Grid} this
62163          * @param {Roo.GridDD} dd The drag drop object
62164          * @param {event} e The raw browser event
62165          */
62166         "enddrag" : true,
62167         /**
62168          * @event dragdrop
62169          * Fires when dragged row(s) are dropped on a valid DD target
62170          * @param {Grid} this
62171          * @param {Roo.GridDD} dd The drag drop object
62172          * @param {String} targetId The target drag drop object
62173          * @param {event} e The raw browser event
62174          */
62175         "dragdrop" : true,
62176         /**
62177          * @event dragover
62178          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
62179          * @param {Grid} this
62180          * @param {Roo.GridDD} dd The drag drop object
62181          * @param {String} targetId The target drag drop object
62182          * @param {event} e The raw browser event
62183          */
62184         "dragover" : true,
62185         /**
62186          * @event dragenter
62187          *  Fires when the dragged row(s) first cross another DD target while being dragged
62188          * @param {Grid} this
62189          * @param {Roo.GridDD} dd The drag drop object
62190          * @param {String} targetId The target drag drop object
62191          * @param {event} e The raw browser event
62192          */
62193         "dragenter" : true,
62194         /**
62195          * @event dragout
62196          * Fires when the dragged row(s) leave another DD target while being dragged
62197          * @param {Grid} this
62198          * @param {Roo.GridDD} dd The drag drop object
62199          * @param {String} targetId The target drag drop object
62200          * @param {event} e The raw browser event
62201          */
62202         "dragout" : true,
62203         /**
62204          * @event rowclass
62205          * Fires when a row is rendered, so you can change add a style to it.
62206          * @param {GridView} gridview   The grid view
62207          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
62208          */
62209         'rowclass' : true,
62210
62211         /**
62212          * @event render
62213          * Fires when the grid is rendered
62214          * @param {Grid} grid
62215          */
62216         'render' : true,
62217             /**
62218              * @event select
62219              * Fires when a date is selected
62220              * @param {DatePicker} this
62221              * @param {Date} date The selected date
62222              */
62223         'select': true,
62224         /**
62225              * @event monthchange
62226              * Fires when the displayed month changes 
62227              * @param {DatePicker} this
62228              * @param {Date} date The selected month
62229              */
62230         'monthchange': true,
62231         /**
62232              * @event evententer
62233              * Fires when mouse over an event
62234              * @param {Calendar} this
62235              * @param {event} Event
62236              */
62237         'evententer': true,
62238         /**
62239              * @event eventleave
62240              * Fires when the mouse leaves an
62241              * @param {Calendar} this
62242              * @param {event}
62243              */
62244         'eventleave': true,
62245         /**
62246              * @event eventclick
62247              * Fires when the mouse click an
62248              * @param {Calendar} this
62249              * @param {event}
62250              */
62251         'eventclick': true,
62252         /**
62253              * @event eventrender
62254              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
62255              * @param {Calendar} this
62256              * @param {data} data to be modified
62257              */
62258         'eventrender': true
62259         
62260     });
62261
62262     Roo.grid.Grid.superclass.constructor.call(this);
62263     this.on('render', function() {
62264         this.view.el.addClass('x-grid-cal'); 
62265         
62266         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
62267
62268     },this);
62269     
62270     if (!Roo.grid.Calendar.style) {
62271         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
62272             
62273             
62274             '.x-grid-cal .x-grid-col' :  {
62275                 height: 'auto !important',
62276                 'vertical-align': 'top'
62277             },
62278             '.x-grid-cal  .fc-event-hori' : {
62279                 height: '14px'
62280             }
62281              
62282             
62283         }, Roo.id());
62284     }
62285
62286     
62287     
62288 };
62289 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
62290     /**
62291      * @cfg {Store} eventStore The store that loads events.
62292      */
62293     eventStore : 25,
62294
62295      
62296     activeDate : false,
62297     startDay : 0,
62298     autoWidth : true,
62299     monitorWindowResize : false,
62300
62301     
62302     resizeColumns : function() {
62303         var col = (this.view.el.getWidth() / 7) - 3;
62304         // loop through cols, and setWidth
62305         for(var i =0 ; i < 7 ; i++){
62306             this.cm.setColumnWidth(i, col);
62307         }
62308     },
62309      setDate :function(date) {
62310         
62311         Roo.log('setDate?');
62312         
62313         this.resizeColumns();
62314         var vd = this.activeDate;
62315         this.activeDate = date;
62316 //        if(vd && this.el){
62317 //            var t = date.getTime();
62318 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
62319 //                Roo.log('using add remove');
62320 //                
62321 //                this.fireEvent('monthchange', this, date);
62322 //                
62323 //                this.cells.removeClass("fc-state-highlight");
62324 //                this.cells.each(function(c){
62325 //                   if(c.dateValue == t){
62326 //                       c.addClass("fc-state-highlight");
62327 //                       setTimeout(function(){
62328 //                            try{c.dom.firstChild.focus();}catch(e){}
62329 //                       }, 50);
62330 //                       return false;
62331 //                   }
62332 //                   return true;
62333 //                });
62334 //                return;
62335 //            }
62336 //        }
62337         
62338         var days = date.getDaysInMonth();
62339         
62340         var firstOfMonth = date.getFirstDateOfMonth();
62341         var startingPos = firstOfMonth.getDay()-this.startDay;
62342         
62343         if(startingPos < this.startDay){
62344             startingPos += 7;
62345         }
62346         
62347         var pm = date.add(Date.MONTH, -1);
62348         var prevStart = pm.getDaysInMonth()-startingPos;
62349 //        
62350         
62351         
62352         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
62353         
62354         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
62355         //this.cells.addClassOnOver('fc-state-hover');
62356         
62357         var cells = this.cells.elements;
62358         var textEls = this.textNodes;
62359         
62360         //Roo.each(cells, function(cell){
62361         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
62362         //});
62363         
62364         days += startingPos;
62365
62366         // convert everything to numbers so it's fast
62367         var day = 86400000;
62368         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
62369         //Roo.log(d);
62370         //Roo.log(pm);
62371         //Roo.log(prevStart);
62372         
62373         var today = new Date().clearTime().getTime();
62374         var sel = date.clearTime().getTime();
62375         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
62376         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
62377         var ddMatch = this.disabledDatesRE;
62378         var ddText = this.disabledDatesText;
62379         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
62380         var ddaysText = this.disabledDaysText;
62381         var format = this.format;
62382         
62383         var setCellClass = function(cal, cell){
62384             
62385             //Roo.log('set Cell Class');
62386             cell.title = "";
62387             var t = d.getTime();
62388             
62389             //Roo.log(d);
62390             
62391             
62392             cell.dateValue = t;
62393             if(t == today){
62394                 cell.className += " fc-today";
62395                 cell.className += " fc-state-highlight";
62396                 cell.title = cal.todayText;
62397             }
62398             if(t == sel){
62399                 // disable highlight in other month..
62400                 cell.className += " fc-state-highlight";
62401                 
62402             }
62403             // disabling
62404             if(t < min) {
62405                 //cell.className = " fc-state-disabled";
62406                 cell.title = cal.minText;
62407                 return;
62408             }
62409             if(t > max) {
62410                 //cell.className = " fc-state-disabled";
62411                 cell.title = cal.maxText;
62412                 return;
62413             }
62414             if(ddays){
62415                 if(ddays.indexOf(d.getDay()) != -1){
62416                     // cell.title = ddaysText;
62417                    // cell.className = " fc-state-disabled";
62418                 }
62419             }
62420             if(ddMatch && format){
62421                 var fvalue = d.dateFormat(format);
62422                 if(ddMatch.test(fvalue)){
62423                     cell.title = ddText.replace("%0", fvalue);
62424                    cell.className = " fc-state-disabled";
62425                 }
62426             }
62427             
62428             if (!cell.initialClassName) {
62429                 cell.initialClassName = cell.dom.className;
62430             }
62431             
62432             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
62433         };
62434
62435         var i = 0;
62436         
62437         for(; i < startingPos; i++) {
62438             cells[i].dayName =  (++prevStart);
62439             Roo.log(textEls[i]);
62440             d.setDate(d.getDate()+1);
62441             
62442             //cells[i].className = "fc-past fc-other-month";
62443             setCellClass(this, cells[i]);
62444         }
62445         
62446         var intDay = 0;
62447         
62448         for(; i < days; i++){
62449             intDay = i - startingPos + 1;
62450             cells[i].dayName =  (intDay);
62451             d.setDate(d.getDate()+1);
62452             
62453             cells[i].className = ''; // "x-date-active";
62454             setCellClass(this, cells[i]);
62455         }
62456         var extraDays = 0;
62457         
62458         for(; i < 42; i++) {
62459             //textEls[i].innerHTML = (++extraDays);
62460             
62461             d.setDate(d.getDate()+1);
62462             cells[i].dayName = (++extraDays);
62463             cells[i].className = "fc-future fc-other-month";
62464             setCellClass(this, cells[i]);
62465         }
62466         
62467         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
62468         
62469         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
62470         
62471         // this will cause all the cells to mis
62472         var rows= [];
62473         var i =0;
62474         for (var r = 0;r < 6;r++) {
62475             for (var c =0;c < 7;c++) {
62476                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
62477             }    
62478         }
62479         
62480         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
62481         for(i=0;i<cells.length;i++) {
62482             
62483             this.cells.elements[i].dayName = cells[i].dayName ;
62484             this.cells.elements[i].className = cells[i].className;
62485             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
62486             this.cells.elements[i].title = cells[i].title ;
62487             this.cells.elements[i].dateValue = cells[i].dateValue ;
62488         }
62489         
62490         
62491         
62492         
62493         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
62494         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
62495         
62496         ////if(totalRows != 6){
62497             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
62498            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
62499        // }
62500         
62501         this.fireEvent('monthchange', this, date);
62502         
62503         
62504     },
62505  /**
62506      * Returns the grid's SelectionModel.
62507      * @return {SelectionModel}
62508      */
62509     getSelectionModel : function(){
62510         if(!this.selModel){
62511             this.selModel = new Roo.grid.CellSelectionModel();
62512         }
62513         return this.selModel;
62514     },
62515
62516     load: function() {
62517         this.eventStore.load()
62518         
62519         
62520         
62521     },
62522     
62523     findCell : function(dt) {
62524         dt = dt.clearTime().getTime();
62525         var ret = false;
62526         this.cells.each(function(c){
62527             //Roo.log("check " +c.dateValue + '?=' + dt);
62528             if(c.dateValue == dt){
62529                 ret = c;
62530                 return false;
62531             }
62532             return true;
62533         });
62534         
62535         return ret;
62536     },
62537     
62538     findCells : function(rec) {
62539         var s = rec.data.start_dt.clone().clearTime().getTime();
62540        // Roo.log(s);
62541         var e= rec.data.end_dt.clone().clearTime().getTime();
62542        // Roo.log(e);
62543         var ret = [];
62544         this.cells.each(function(c){
62545              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
62546             
62547             if(c.dateValue > e){
62548                 return ;
62549             }
62550             if(c.dateValue < s){
62551                 return ;
62552             }
62553             ret.push(c);
62554         });
62555         
62556         return ret;    
62557     },
62558     
62559     findBestRow: function(cells)
62560     {
62561         var ret = 0;
62562         
62563         for (var i =0 ; i < cells.length;i++) {
62564             ret  = Math.max(cells[i].rows || 0,ret);
62565         }
62566         return ret;
62567         
62568     },
62569     
62570     
62571     addItem : function(rec)
62572     {
62573         // look for vertical location slot in
62574         var cells = this.findCells(rec);
62575         
62576         rec.row = this.findBestRow(cells);
62577         
62578         // work out the location.
62579         
62580         var crow = false;
62581         var rows = [];
62582         for(var i =0; i < cells.length; i++) {
62583             if (!crow) {
62584                 crow = {
62585                     start : cells[i],
62586                     end :  cells[i]
62587                 };
62588                 continue;
62589             }
62590             if (crow.start.getY() == cells[i].getY()) {
62591                 // on same row.
62592                 crow.end = cells[i];
62593                 continue;
62594             }
62595             // different row.
62596             rows.push(crow);
62597             crow = {
62598                 start: cells[i],
62599                 end : cells[i]
62600             };
62601             
62602         }
62603         
62604         rows.push(crow);
62605         rec.els = [];
62606         rec.rows = rows;
62607         rec.cells = cells;
62608         for (var i = 0; i < cells.length;i++) {
62609             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
62610             
62611         }
62612         
62613         
62614     },
62615     
62616     clearEvents: function() {
62617         
62618         if (!this.eventStore.getCount()) {
62619             return;
62620         }
62621         // reset number of rows in cells.
62622         Roo.each(this.cells.elements, function(c){
62623             c.rows = 0;
62624         });
62625         
62626         this.eventStore.each(function(e) {
62627             this.clearEvent(e);
62628         },this);
62629         
62630     },
62631     
62632     clearEvent : function(ev)
62633     {
62634         if (ev.els) {
62635             Roo.each(ev.els, function(el) {
62636                 el.un('mouseenter' ,this.onEventEnter, this);
62637                 el.un('mouseleave' ,this.onEventLeave, this);
62638                 el.remove();
62639             },this);
62640             ev.els = [];
62641         }
62642     },
62643     
62644     
62645     renderEvent : function(ev,ctr) {
62646         if (!ctr) {
62647              ctr = this.view.el.select('.fc-event-container',true).first();
62648         }
62649         
62650          
62651         this.clearEvent(ev);
62652             //code
62653        
62654         
62655         
62656         ev.els = [];
62657         var cells = ev.cells;
62658         var rows = ev.rows;
62659         this.fireEvent('eventrender', this, ev);
62660         
62661         for(var i =0; i < rows.length; i++) {
62662             
62663             cls = '';
62664             if (i == 0) {
62665                 cls += ' fc-event-start';
62666             }
62667             if ((i+1) == rows.length) {
62668                 cls += ' fc-event-end';
62669             }
62670             
62671             //Roo.log(ev.data);
62672             // how many rows should it span..
62673             var cg = this.eventTmpl.append(ctr,Roo.apply({
62674                 fccls : cls
62675                 
62676             }, ev.data) , true);
62677             
62678             
62679             cg.on('mouseenter' ,this.onEventEnter, this, ev);
62680             cg.on('mouseleave' ,this.onEventLeave, this, ev);
62681             cg.on('click', this.onEventClick, this, ev);
62682             
62683             ev.els.push(cg);
62684             
62685             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
62686             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
62687             //Roo.log(cg);
62688              
62689             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
62690             cg.setWidth(ebox.right - sbox.x -2);
62691         }
62692     },
62693     
62694     renderEvents: function()
62695     {   
62696         // first make sure there is enough space..
62697         
62698         if (!this.eventTmpl) {
62699             this.eventTmpl = new Roo.Template(
62700                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
62701                     '<div class="fc-event-inner">' +
62702                         '<span class="fc-event-time">{time}</span>' +
62703                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
62704                     '</div>' +
62705                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
62706                 '</div>'
62707             );
62708                 
62709         }
62710                
62711         
62712         
62713         this.cells.each(function(c) {
62714             //Roo.log(c.select('.fc-day-content div',true).first());
62715             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
62716         });
62717         
62718         var ctr = this.view.el.select('.fc-event-container',true).first();
62719         
62720         var cls;
62721         this.eventStore.each(function(ev){
62722             
62723             this.renderEvent(ev);
62724              
62725              
62726         }, this);
62727         this.view.layout();
62728         
62729     },
62730     
62731     onEventEnter: function (e, el,event,d) {
62732         this.fireEvent('evententer', this, el, event);
62733     },
62734     
62735     onEventLeave: function (e, el,event,d) {
62736         this.fireEvent('eventleave', this, el, event);
62737     },
62738     
62739     onEventClick: function (e, el,event,d) {
62740         this.fireEvent('eventclick', this, el, event);
62741     },
62742     
62743     onMonthChange: function () {
62744         this.store.load();
62745     },
62746     
62747     onLoad: function () {
62748         
62749         //Roo.log('calendar onload');
62750 //         
62751         if(this.eventStore.getCount() > 0){
62752             
62753            
62754             
62755             this.eventStore.each(function(d){
62756                 
62757                 
62758                 // FIXME..
62759                 var add =   d.data;
62760                 if (typeof(add.end_dt) == 'undefined')  {
62761                     Roo.log("Missing End time in calendar data: ");
62762                     Roo.log(d);
62763                     return;
62764                 }
62765                 if (typeof(add.start_dt) == 'undefined')  {
62766                     Roo.log("Missing Start time in calendar data: ");
62767                     Roo.log(d);
62768                     return;
62769                 }
62770                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
62771                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
62772                 add.id = add.id || d.id;
62773                 add.title = add.title || '??';
62774                 
62775                 this.addItem(d);
62776                 
62777              
62778             },this);
62779         }
62780         
62781         this.renderEvents();
62782     }
62783     
62784
62785 });
62786 /*
62787  grid : {
62788                 xtype: 'Grid',
62789                 xns: Roo.grid,
62790                 listeners : {
62791                     render : function ()
62792                     {
62793                         _this.grid = this;
62794                         
62795                         if (!this.view.el.hasClass('course-timesheet')) {
62796                             this.view.el.addClass('course-timesheet');
62797                         }
62798                         if (this.tsStyle) {
62799                             this.ds.load({});
62800                             return; 
62801                         }
62802                         Roo.log('width');
62803                         Roo.log(_this.grid.view.el.getWidth());
62804                         
62805                         
62806                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
62807                             '.course-timesheet .x-grid-row' : {
62808                                 height: '80px'
62809                             },
62810                             '.x-grid-row td' : {
62811                                 'vertical-align' : 0
62812                             },
62813                             '.course-edit-link' : {
62814                                 'color' : 'blue',
62815                                 'text-overflow' : 'ellipsis',
62816                                 'overflow' : 'hidden',
62817                                 'white-space' : 'nowrap',
62818                                 'cursor' : 'pointer'
62819                             },
62820                             '.sub-link' : {
62821                                 'color' : 'green'
62822                             },
62823                             '.de-act-sup-link' : {
62824                                 'color' : 'purple',
62825                                 'text-decoration' : 'line-through'
62826                             },
62827                             '.de-act-link' : {
62828                                 'color' : 'red',
62829                                 'text-decoration' : 'line-through'
62830                             },
62831                             '.course-timesheet .course-highlight' : {
62832                                 'border-top-style': 'dashed !important',
62833                                 'border-bottom-bottom': 'dashed !important'
62834                             },
62835                             '.course-timesheet .course-item' : {
62836                                 'font-family'   : 'tahoma, arial, helvetica',
62837                                 'font-size'     : '11px',
62838                                 'overflow'      : 'hidden',
62839                                 'padding-left'  : '10px',
62840                                 'padding-right' : '10px',
62841                                 'padding-top' : '10px' 
62842                             }
62843                             
62844                         }, Roo.id());
62845                                 this.ds.load({});
62846                     }
62847                 },
62848                 autoWidth : true,
62849                 monitorWindowResize : false,
62850                 cellrenderer : function(v,x,r)
62851                 {
62852                     return v;
62853                 },
62854                 sm : {
62855                     xtype: 'CellSelectionModel',
62856                     xns: Roo.grid
62857                 },
62858                 dataSource : {
62859                     xtype: 'Store',
62860                     xns: Roo.data,
62861                     listeners : {
62862                         beforeload : function (_self, options)
62863                         {
62864                             options.params = options.params || {};
62865                             options.params._month = _this.monthField.getValue();
62866                             options.params.limit = 9999;
62867                             options.params['sort'] = 'when_dt';    
62868                             options.params['dir'] = 'ASC';    
62869                             this.proxy.loadResponse = this.loadResponse;
62870                             Roo.log("load?");
62871                             //this.addColumns();
62872                         },
62873                         load : function (_self, records, options)
62874                         {
62875                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
62876                                 // if you click on the translation.. you can edit it...
62877                                 var el = Roo.get(this);
62878                                 var id = el.dom.getAttribute('data-id');
62879                                 var d = el.dom.getAttribute('data-date');
62880                                 var t = el.dom.getAttribute('data-time');
62881                                 //var id = this.child('span').dom.textContent;
62882                                 
62883                                 //Roo.log(this);
62884                                 Pman.Dialog.CourseCalendar.show({
62885                                     id : id,
62886                                     when_d : d,
62887                                     when_t : t,
62888                                     productitem_active : id ? 1 : 0
62889                                 }, function() {
62890                                     _this.grid.ds.load({});
62891                                 });
62892                            
62893                            });
62894                            
62895                            _this.panel.fireEvent('resize', [ '', '' ]);
62896                         }
62897                     },
62898                     loadResponse : function(o, success, response){
62899                             // this is overridden on before load..
62900                             
62901                             Roo.log("our code?");       
62902                             //Roo.log(success);
62903                             //Roo.log(response)
62904                             delete this.activeRequest;
62905                             if(!success){
62906                                 this.fireEvent("loadexception", this, o, response);
62907                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62908                                 return;
62909                             }
62910                             var result;
62911                             try {
62912                                 result = o.reader.read(response);
62913                             }catch(e){
62914                                 Roo.log("load exception?");
62915                                 this.fireEvent("loadexception", this, o, response, e);
62916                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62917                                 return;
62918                             }
62919                             Roo.log("ready...");        
62920                             // loop through result.records;
62921                             // and set this.tdate[date] = [] << array of records..
62922                             _this.tdata  = {};
62923                             Roo.each(result.records, function(r){
62924                                 //Roo.log(r.data);
62925                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
62926                                     _this.tdata[r.data.when_dt.format('j')] = [];
62927                                 }
62928                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
62929                             });
62930                             
62931                             //Roo.log(_this.tdata);
62932                             
62933                             result.records = [];
62934                             result.totalRecords = 6;
62935                     
62936                             // let's generate some duumy records for the rows.
62937                             //var st = _this.dateField.getValue();
62938                             
62939                             // work out monday..
62940                             //st = st.add(Date.DAY, -1 * st.format('w'));
62941                             
62942                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62943                             
62944                             var firstOfMonth = date.getFirstDayOfMonth();
62945                             var days = date.getDaysInMonth();
62946                             var d = 1;
62947                             var firstAdded = false;
62948                             for (var i = 0; i < result.totalRecords ; i++) {
62949                                 //var d= st.add(Date.DAY, i);
62950                                 var row = {};
62951                                 var added = 0;
62952                                 for(var w = 0 ; w < 7 ; w++){
62953                                     if(!firstAdded && firstOfMonth != w){
62954                                         continue;
62955                                     }
62956                                     if(d > days){
62957                                         continue;
62958                                     }
62959                                     firstAdded = true;
62960                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
62961                                     row['weekday'+w] = String.format(
62962                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
62963                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
62964                                                     d,
62965                                                     date.format('Y-m-')+dd
62966                                                 );
62967                                     added++;
62968                                     if(typeof(_this.tdata[d]) != 'undefined'){
62969                                         Roo.each(_this.tdata[d], function(r){
62970                                             var is_sub = '';
62971                                             var deactive = '';
62972                                             var id = r.id;
62973                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
62974                                             if(r.parent_id*1>0){
62975                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
62976                                                 id = r.parent_id;
62977                                             }
62978                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
62979                                                 deactive = 'de-act-link';
62980                                             }
62981                                             
62982                                             row['weekday'+w] += String.format(
62983                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
62984                                                     id, //0
62985                                                     r.product_id_name, //1
62986                                                     r.when_dt.format('h:ia'), //2
62987                                                     is_sub, //3
62988                                                     deactive, //4
62989                                                     desc // 5
62990                                             );
62991                                         });
62992                                     }
62993                                     d++;
62994                                 }
62995                                 
62996                                 // only do this if something added..
62997                                 if(added > 0){ 
62998                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
62999                                 }
63000                                 
63001                                 
63002                                 // push it twice. (second one with an hour..
63003                                 
63004                             }
63005                             //Roo.log(result);
63006                             this.fireEvent("load", this, o, o.request.arg);
63007                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
63008                         },
63009                     sortInfo : {field: 'when_dt', direction : 'ASC' },
63010                     proxy : {
63011                         xtype: 'HttpProxy',
63012                         xns: Roo.data,
63013                         method : 'GET',
63014                         url : baseURL + '/Roo/Shop_course.php'
63015                     },
63016                     reader : {
63017                         xtype: 'JsonReader',
63018                         xns: Roo.data,
63019                         id : 'id',
63020                         fields : [
63021                             {
63022                                 'name': 'id',
63023                                 'type': 'int'
63024                             },
63025                             {
63026                                 'name': 'when_dt',
63027                                 'type': 'string'
63028                             },
63029                             {
63030                                 'name': 'end_dt',
63031                                 'type': 'string'
63032                             },
63033                             {
63034                                 'name': 'parent_id',
63035                                 'type': 'int'
63036                             },
63037                             {
63038                                 'name': 'product_id',
63039                                 'type': 'int'
63040                             },
63041                             {
63042                                 'name': 'productitem_id',
63043                                 'type': 'int'
63044                             },
63045                             {
63046                                 'name': 'guid',
63047                                 'type': 'int'
63048                             }
63049                         ]
63050                     }
63051                 },
63052                 toolbar : {
63053                     xtype: 'Toolbar',
63054                     xns: Roo,
63055                     items : [
63056                         {
63057                             xtype: 'Button',
63058                             xns: Roo.Toolbar,
63059                             listeners : {
63060                                 click : function (_self, e)
63061                                 {
63062                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63063                                     sd.setMonth(sd.getMonth()-1);
63064                                     _this.monthField.setValue(sd.format('Y-m-d'));
63065                                     _this.grid.ds.load({});
63066                                 }
63067                             },
63068                             text : "Back"
63069                         },
63070                         {
63071                             xtype: 'Separator',
63072                             xns: Roo.Toolbar
63073                         },
63074                         {
63075                             xtype: 'MonthField',
63076                             xns: Roo.form,
63077                             listeners : {
63078                                 render : function (_self)
63079                                 {
63080                                     _this.monthField = _self;
63081                                    // _this.monthField.set  today
63082                                 },
63083                                 select : function (combo, date)
63084                                 {
63085                                     _this.grid.ds.load({});
63086                                 }
63087                             },
63088                             value : (function() { return new Date(); })()
63089                         },
63090                         {
63091                             xtype: 'Separator',
63092                             xns: Roo.Toolbar
63093                         },
63094                         {
63095                             xtype: 'TextItem',
63096                             xns: Roo.Toolbar,
63097                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
63098                         },
63099                         {
63100                             xtype: 'Fill',
63101                             xns: Roo.Toolbar
63102                         },
63103                         {
63104                             xtype: 'Button',
63105                             xns: Roo.Toolbar,
63106                             listeners : {
63107                                 click : function (_self, e)
63108                                 {
63109                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63110                                     sd.setMonth(sd.getMonth()+1);
63111                                     _this.monthField.setValue(sd.format('Y-m-d'));
63112                                     _this.grid.ds.load({});
63113                                 }
63114                             },
63115                             text : "Next"
63116                         }
63117                     ]
63118                 },
63119                  
63120             }
63121         };
63122         
63123         *//*
63124  * Based on:
63125  * Ext JS Library 1.1.1
63126  * Copyright(c) 2006-2007, Ext JS, LLC.
63127  *
63128  * Originally Released Under LGPL - original licence link has changed is not relivant.
63129  *
63130  * Fork - LGPL
63131  * <script type="text/javascript">
63132  */
63133  
63134 /**
63135  * @class Roo.LoadMask
63136  * A simple utility class for generically masking elements while loading data.  If the element being masked has
63137  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
63138  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
63139  * element's UpdateManager load indicator and will be destroyed after the initial load.
63140  * @constructor
63141  * Create a new LoadMask
63142  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
63143  * @param {Object} config The config object
63144  */
63145 Roo.LoadMask = function(el, config){
63146     this.el = Roo.get(el);
63147     Roo.apply(this, config);
63148     if(this.store){
63149         this.store.on('beforeload', this.onBeforeLoad, this);
63150         this.store.on('load', this.onLoad, this);
63151         this.store.on('loadexception', this.onLoadException, this);
63152         this.removeMask = false;
63153     }else{
63154         var um = this.el.getUpdateManager();
63155         um.showLoadIndicator = false; // disable the default indicator
63156         um.on('beforeupdate', this.onBeforeLoad, this);
63157         um.on('update', this.onLoad, this);
63158         um.on('failure', this.onLoad, this);
63159         this.removeMask = true;
63160     }
63161 };
63162
63163 Roo.LoadMask.prototype = {
63164     /**
63165      * @cfg {Boolean} removeMask
63166      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
63167      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
63168      */
63169     removeMask : false,
63170     /**
63171      * @cfg {String} msg
63172      * The text to display in a centered loading message box (defaults to 'Loading...')
63173      */
63174     msg : 'Loading...',
63175     /**
63176      * @cfg {String} msgCls
63177      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
63178      */
63179     msgCls : 'x-mask-loading',
63180
63181     /**
63182      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
63183      * @type Boolean
63184      */
63185     disabled: false,
63186
63187     /**
63188      * Disables the mask to prevent it from being displayed
63189      */
63190     disable : function(){
63191        this.disabled = true;
63192     },
63193
63194     /**
63195      * Enables the mask so that it can be displayed
63196      */
63197     enable : function(){
63198         this.disabled = false;
63199     },
63200     
63201     onLoadException : function()
63202     {
63203         Roo.log(arguments);
63204         
63205         if (typeof(arguments[3]) != 'undefined') {
63206             Roo.MessageBox.alert("Error loading",arguments[3]);
63207         } 
63208         /*
63209         try {
63210             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
63211                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
63212             }   
63213         } catch(e) {
63214             
63215         }
63216         */
63217     
63218         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
63219     },
63220     // private
63221     onLoad : function()
63222     {
63223         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
63224     },
63225
63226     // private
63227     onBeforeLoad : function(){
63228         if(!this.disabled){
63229             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
63230         }
63231     },
63232
63233     // private
63234     destroy : function(){
63235         if(this.store){
63236             this.store.un('beforeload', this.onBeforeLoad, this);
63237             this.store.un('load', this.onLoad, this);
63238             this.store.un('loadexception', this.onLoadException, this);
63239         }else{
63240             var um = this.el.getUpdateManager();
63241             um.un('beforeupdate', this.onBeforeLoad, this);
63242             um.un('update', this.onLoad, this);
63243             um.un('failure', this.onLoad, this);
63244         }
63245     }
63246 };/*
63247  * Based on:
63248  * Ext JS Library 1.1.1
63249  * Copyright(c) 2006-2007, Ext JS, LLC.
63250  *
63251  * Originally Released Under LGPL - original licence link has changed is not relivant.
63252  *
63253  * Fork - LGPL
63254  * <script type="text/javascript">
63255  */
63256
63257
63258 /**
63259  * @class Roo.XTemplate
63260  * @extends Roo.Template
63261  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
63262 <pre><code>
63263 var t = new Roo.XTemplate(
63264         '&lt;select name="{name}"&gt;',
63265                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
63266         '&lt;/select&gt;'
63267 );
63268  
63269 // then append, applying the master template values
63270  </code></pre>
63271  *
63272  * Supported features:
63273  *
63274  *  Tags:
63275
63276 <pre><code>
63277       {a_variable} - output encoded.
63278       {a_variable.format:("Y-m-d")} - call a method on the variable
63279       {a_variable:raw} - unencoded output
63280       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
63281       {a_variable:this.method_on_template(...)} - call a method on the template object.
63282  
63283 </code></pre>
63284  *  The tpl tag:
63285 <pre><code>
63286         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
63287         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
63288         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
63289         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
63290   
63291         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
63292         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
63293 </code></pre>
63294  *      
63295  */
63296 Roo.XTemplate = function()
63297 {
63298     Roo.XTemplate.superclass.constructor.apply(this, arguments);
63299     if (this.html) {
63300         this.compile();
63301     }
63302 };
63303
63304
63305 Roo.extend(Roo.XTemplate, Roo.Template, {
63306
63307     /**
63308      * The various sub templates
63309      */
63310     tpls : false,
63311     /**
63312      *
63313      * basic tag replacing syntax
63314      * WORD:WORD()
63315      *
63316      * // you can fake an object call by doing this
63317      *  x.t:(test,tesT) 
63318      * 
63319      */
63320     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
63321
63322     /**
63323      * compile the template
63324      *
63325      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
63326      *
63327      */
63328     compile: function()
63329     {
63330         var s = this.html;
63331      
63332         s = ['<tpl>', s, '</tpl>'].join('');
63333     
63334         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
63335             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
63336             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
63337             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
63338             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
63339             m,
63340             id     = 0,
63341             tpls   = [];
63342     
63343         while(true == !!(m = s.match(re))){
63344             var forMatch   = m[0].match(nameRe),
63345                 ifMatch   = m[0].match(ifRe),
63346                 execMatch   = m[0].match(execRe),
63347                 namedMatch   = m[0].match(namedRe),
63348                 
63349                 exp  = null, 
63350                 fn   = null,
63351                 exec = null,
63352                 name = forMatch && forMatch[1] ? forMatch[1] : '';
63353                 
63354             if (ifMatch) {
63355                 // if - puts fn into test..
63356                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
63357                 if(exp){
63358                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
63359                 }
63360             }
63361             
63362             if (execMatch) {
63363                 // exec - calls a function... returns empty if true is  returned.
63364                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
63365                 if(exp){
63366                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
63367                 }
63368             }
63369             
63370             
63371             if (name) {
63372                 // for = 
63373                 switch(name){
63374                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
63375                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
63376                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
63377                 }
63378             }
63379             var uid = namedMatch ? namedMatch[1] : id;
63380             
63381             
63382             tpls.push({
63383                 id:     namedMatch ? namedMatch[1] : id,
63384                 target: name,
63385                 exec:   exec,
63386                 test:   fn,
63387                 body:   m[1] || ''
63388             });
63389             if (namedMatch) {
63390                 s = s.replace(m[0], '');
63391             } else { 
63392                 s = s.replace(m[0], '{xtpl'+ id + '}');
63393             }
63394             ++id;
63395         }
63396         this.tpls = [];
63397         for(var i = tpls.length-1; i >= 0; --i){
63398             this.compileTpl(tpls[i]);
63399             this.tpls[tpls[i].id] = tpls[i];
63400         }
63401         this.master = tpls[tpls.length-1];
63402         return this;
63403     },
63404     /**
63405      * same as applyTemplate, except it's done to one of the subTemplates
63406      * when using named templates, you can do:
63407      *
63408      * var str = pl.applySubTemplate('your-name', values);
63409      *
63410      * 
63411      * @param {Number} id of the template
63412      * @param {Object} values to apply to template
63413      * @param {Object} parent (normaly the instance of this object)
63414      */
63415     applySubTemplate : function(id, values, parent)
63416     {
63417         
63418         
63419         var t = this.tpls[id];
63420         
63421         
63422         try { 
63423             if(t.test && !t.test.call(this, values, parent)){
63424                 return '';
63425             }
63426         } catch(e) {
63427             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
63428             Roo.log(e.toString());
63429             Roo.log(t.test);
63430             return ''
63431         }
63432         try { 
63433             
63434             if(t.exec && t.exec.call(this, values, parent)){
63435                 return '';
63436             }
63437         } catch(e) {
63438             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
63439             Roo.log(e.toString());
63440             Roo.log(t.exec);
63441             return ''
63442         }
63443         try {
63444             var vs = t.target ? t.target.call(this, values, parent) : values;
63445             parent = t.target ? values : parent;
63446             if(t.target && vs instanceof Array){
63447                 var buf = [];
63448                 for(var i = 0, len = vs.length; i < len; i++){
63449                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
63450                 }
63451                 return buf.join('');
63452             }
63453             return t.compiled.call(this, vs, parent);
63454         } catch (e) {
63455             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
63456             Roo.log(e.toString());
63457             Roo.log(t.compiled);
63458             return '';
63459         }
63460     },
63461
63462     compileTpl : function(tpl)
63463     {
63464         var fm = Roo.util.Format;
63465         var useF = this.disableFormats !== true;
63466         var sep = Roo.isGecko ? "+" : ",";
63467         var undef = function(str) {
63468             Roo.log("Property not found :"  + str);
63469             return '';
63470         };
63471         
63472         var fn = function(m, name, format, args)
63473         {
63474             //Roo.log(arguments);
63475             args = args ? args.replace(/\\'/g,"'") : args;
63476             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
63477             if (typeof(format) == 'undefined') {
63478                 format= 'htmlEncode';
63479             }
63480             if (format == 'raw' ) {
63481                 format = false;
63482             }
63483             
63484             if(name.substr(0, 4) == 'xtpl'){
63485                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
63486             }
63487             
63488             // build an array of options to determine if value is undefined..
63489             
63490             // basically get 'xxxx.yyyy' then do
63491             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
63492             //    (function () { Roo.log("Property not found"); return ''; })() :
63493             //    ......
63494             
63495             var udef_ar = [];
63496             var lookfor = '';
63497             Roo.each(name.split('.'), function(st) {
63498                 lookfor += (lookfor.length ? '.': '') + st;
63499                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
63500             });
63501             
63502             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
63503             
63504             
63505             if(format && useF){
63506                 
63507                 args = args ? ',' + args : "";
63508                  
63509                 if(format.substr(0, 5) != "this."){
63510                     format = "fm." + format + '(';
63511                 }else{
63512                     format = 'this.call("'+ format.substr(5) + '", ';
63513                     args = ", values";
63514                 }
63515                 
63516                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
63517             }
63518              
63519             if (args.length) {
63520                 // called with xxyx.yuu:(test,test)
63521                 // change to ()
63522                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
63523             }
63524             // raw.. - :raw modifier..
63525             return "'"+ sep + udef_st  + name + ")"+sep+"'";
63526             
63527         };
63528         var body;
63529         // branched to use + in gecko and [].join() in others
63530         if(Roo.isGecko){
63531             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
63532                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
63533                     "';};};";
63534         }else{
63535             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
63536             body.push(tpl.body.replace(/(\r\n|\n)/g,
63537                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
63538             body.push("'].join('');};};");
63539             body = body.join('');
63540         }
63541         
63542         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
63543        
63544         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
63545         eval(body);
63546         
63547         return this;
63548     },
63549
63550     applyTemplate : function(values){
63551         return this.master.compiled.call(this, values, {});
63552         //var s = this.subs;
63553     },
63554
63555     apply : function(){
63556         return this.applyTemplate.apply(this, arguments);
63557     }
63558
63559  });
63560
63561 Roo.XTemplate.from = function(el){
63562     el = Roo.getDom(el);
63563     return new Roo.XTemplate(el.value || el.innerHTML);
63564 };