e4c6e1a7fba077aa3a8d6d501e4e74ad61d050cb
[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                );
705 /*
706  * Based on:
707  * Ext JS Library 1.1.1
708  * Copyright(c) 2006-2007, Ext JS, LLC.
709  *
710  * Originally Released Under LGPL - original licence link has changed is not relivant.
711  *
712  * Fork - LGPL
713  * <script type="text/javascript">
714  */
715
716 (function() {    
717     // wrappedn so fnCleanup is not in global scope...
718     if(Roo.isIE) {
719         function fnCleanUp() {
720             var p = Function.prototype;
721             delete p.createSequence;
722             delete p.defer;
723             delete p.createDelegate;
724             delete p.createCallback;
725             delete p.createInterceptor;
726
727             window.detachEvent("onunload", fnCleanUp);
728         }
729         window.attachEvent("onunload", fnCleanUp);
730     }
731 })();
732
733
734 /**
735  * @class Function
736  * These functions are available on every Function object (any JavaScript function).
737  */
738 Roo.apply(Function.prototype, {
739      /**
740      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
741      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
742      * Will create a function that is bound to those 2 args.
743      * @return {Function} The new function
744     */
745     createCallback : function(/*args...*/){
746         // make args available, in function below
747         var args = arguments;
748         var method = this;
749         return function() {
750             return method.apply(window, args);
751         };
752     },
753
754     /**
755      * Creates a delegate (callback) that sets the scope to obj.
756      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
757      * Will create a function that is automatically scoped to this.
758      * @param {Object} obj (optional) The object for which the scope is set
759      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761      *                                             if a number the args are inserted at the specified position
762      * @return {Function} The new function
763      */
764     createDelegate : function(obj, args, appendArgs){
765         var method = this;
766         return function() {
767             var callArgs = args || arguments;
768             if(appendArgs === true){
769                 callArgs = Array.prototype.slice.call(arguments, 0);
770                 callArgs = callArgs.concat(args);
771             }else if(typeof appendArgs == "number"){
772                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
773                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
774                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
775             }
776             return method.apply(obj || window, callArgs);
777         };
778     },
779
780     /**
781      * Calls this function after the number of millseconds specified.
782      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
783      * @param {Object} obj (optional) The object for which the scope is set
784      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
785      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
786      *                                             if a number the args are inserted at the specified position
787      * @return {Number} The timeout id that can be used with clearTimeout
788      */
789     defer : function(millis, obj, args, appendArgs){
790         var fn = this.createDelegate(obj, args, appendArgs);
791         if(millis){
792             return setTimeout(fn, millis);
793         }
794         fn();
795         return 0;
796     },
797     /**
798      * Create a combined function call sequence of the original function + the passed function.
799      * The resulting function returns the results of the original function.
800      * The passed fcn is called with the parameters of the original function
801      * @param {Function} fcn The function to sequence
802      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
803      * @return {Function} The new function
804      */
805     createSequence : function(fcn, scope){
806         if(typeof fcn != "function"){
807             return this;
808         }
809         var method = this;
810         return function() {
811             var retval = method.apply(this || window, arguments);
812             fcn.apply(scope || this || window, arguments);
813             return retval;
814         };
815     },
816
817     /**
818      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
819      * The resulting function returns the results of the original function.
820      * The passed fcn is called with the parameters of the original function.
821      * @addon
822      * @param {Function} fcn The function to call before the original
823      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
824      * @return {Function} The new function
825      */
826     createInterceptor : function(fcn, scope){
827         if(typeof fcn != "function"){
828             return this;
829         }
830         var method = this;
831         return function() {
832             fcn.target = this;
833             fcn.method = method;
834             if(fcn.apply(scope || this || window, arguments) === false){
835                 return;
836             }
837             return method.apply(this || window, arguments);
838         };
839     }
840 });
841 /*
842  * Based on:
843  * Ext JS Library 1.1.1
844  * Copyright(c) 2006-2007, Ext JS, LLC.
845  *
846  * Originally Released Under LGPL - original licence link has changed is not relivant.
847  *
848  * Fork - LGPL
849  * <script type="text/javascript">
850  */
851
852 Roo.applyIf(String, {
853     
854     /** @scope String */
855     
856     /**
857      * Escapes the passed string for ' and \
858      * @param {String} string The string to escape
859      * @return {String} The escaped string
860      * @static
861      */
862     escape : function(string) {
863         return string.replace(/('|\\)/g, "\\$1");
864     },
865
866     /**
867      * Pads the left side of a string with a specified character.  This is especially useful
868      * for normalizing number and date strings.  Example usage:
869      * <pre><code>
870 var s = String.leftPad('123', 5, '0');
871 // s now contains the string: '00123'
872 </code></pre>
873      * @param {String} string The original string
874      * @param {Number} size The total length of the output string
875      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
876      * @return {String} The padded string
877      * @static
878      */
879     leftPad : function (val, size, ch) {
880         var result = new String(val);
881         if(ch === null || ch === undefined || ch === '') {
882             ch = " ";
883         }
884         while (result.length < size) {
885             result = ch + result;
886         }
887         return result;
888     },
889
890     /**
891      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
892      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
893      * <pre><code>
894 var cls = 'my-class', text = 'Some text';
895 var s = String.format('<div class="{0}">{1}</div>', cls, text);
896 // s now contains the string: '<div class="my-class">Some text</div>'
897 </code></pre>
898      * @param {String} string The tokenized string to be formatted
899      * @param {String} value1 The value to replace token {0}
900      * @param {String} value2 Etc...
901      * @return {String} The formatted string
902      * @static
903      */
904     format : function(format){
905         var args = Array.prototype.slice.call(arguments, 1);
906         return format.replace(/\{(\d+)\}/g, function(m, i){
907             return Roo.util.Format.htmlEncode(args[i]);
908         });
909     }
910   
911     
912 });
913
914 /**
915  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
916  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
917  * they are already different, the first value passed in is returned.  Note that this method returns the new value
918  * but does not change the current string.
919  * <pre><code>
920 // alternate sort directions
921 sort = sort.toggle('ASC', 'DESC');
922
923 // instead of conditional logic:
924 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
925 </code></pre>
926  * @param {String} value The value to compare to the current string
927  * @param {String} other The new value to use if the string already equals the first value passed in
928  * @return {String} The new value
929  */
930  
931 String.prototype.toggle = function(value, other){
932     return this == value ? other : value;
933 };
934
935
936 /**
937   * Remove invalid unicode characters from a string 
938   *
939   * @return {String} The clean string
940   */
941 String.prototype.unicodeClean = function () {
942     return this.replace(/[\s\S]/g,
943         function(character) {
944             if (character.charCodeAt()< 256) {
945               return character;
946            }
947            try {
948                 encodeURIComponent(character);
949            } catch(e) { 
950               return '';
951            }
952            return character;
953         }
954     );
955 };
956   
957
958 /**
959   * Make the first letter of a string uppercase
960   *
961   * @return {String} The new string.
962   */
963 String.prototype.toUpperCaseFirst = function () {
964     return this.charAt(0).toUpperCase() + this.slice(1);
965 };  
966   
967 /*
968  * Based on:
969  * Ext JS Library 1.1.1
970  * Copyright(c) 2006-2007, Ext JS, LLC.
971  *
972  * Originally Released Under LGPL - original licence link has changed is not relivant.
973  *
974  * Fork - LGPL
975  * <script type="text/javascript">
976  */
977
978  /**
979  * @class Number
980  */
981 Roo.applyIf(Number.prototype, {
982     /**
983      * Checks whether or not the current number is within a desired range.  If the number is already within the
984      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
985      * exceeded.  Note that this method returns the constrained value but does not change the current number.
986      * @param {Number} min The minimum number in the range
987      * @param {Number} max The maximum number in the range
988      * @return {Number} The constrained value if outside the range, otherwise the current value
989      */
990     constrain : function(min, max){
991         return Math.min(Math.max(this, min), max);
992     }
993 });/*
994  * Based on:
995  * Ext JS Library 1.1.1
996  * Copyright(c) 2006-2007, Ext JS, LLC.
997  *
998  * Originally Released Under LGPL - original licence link has changed is not relivant.
999  *
1000  * Fork - LGPL
1001  * <script type="text/javascript">
1002  */
1003  /**
1004  * @class Array
1005  */
1006 Roo.applyIf(Array.prototype, {
1007     /**
1008      * 
1009      * Checks whether or not the specified object exists in the array.
1010      * @param {Object} o The object to check for
1011      * @return {Number} The index of o in the array (or -1 if it is not found)
1012      */
1013     indexOf : function(o){
1014        for (var i = 0, len = this.length; i < len; i++){
1015               if(this[i] == o) { return i; }
1016        }
1017            return -1;
1018     },
1019
1020     /**
1021      * Removes the specified object from the array.  If the object is not found nothing happens.
1022      * @param {Object} o The object to remove
1023      */
1024     remove : function(o){
1025        var index = this.indexOf(o);
1026        if(index != -1){
1027            this.splice(index, 1);
1028        }
1029     },
1030     /**
1031      * Map (JS 1.6 compatibility)
1032      * @param {Function} function  to call
1033      */
1034     map : function(fun )
1035     {
1036         var len = this.length >>> 0;
1037         if (typeof fun != "function") {
1038             throw new TypeError();
1039         }
1040         var res = new Array(len);
1041         var thisp = arguments[1];
1042         for (var i = 0; i < len; i++)
1043         {
1044             if (i in this) {
1045                 res[i] = fun.call(thisp, this[i], i, this);
1046             }
1047         }
1048
1049         return res;
1050     },
1051     /**
1052      * equals
1053      * @param {Array} o The array to compare to
1054      * @returns {Boolean} true if the same
1055      */
1056     equals : function(b)
1057     {
1058             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1059         if (this === b) {
1060             return true;
1061         }
1062         if (b == null) {
1063             return false;
1064         }
1065         if (this.length !== b.length) {
1066             return false;
1067         }
1068           
1069         // sort?? a.sort().equals(b.sort());
1070           
1071         for (var i = 0; i < this.length; ++i) {
1072             if (this[i] !== b[i]) {
1073             return false;
1074             }
1075         }
1076         return true;
1077     } 
1078     
1079     
1080     
1081     
1082 });
1083
1084 Roo.applyIf(Array, {
1085  /**
1086      * from
1087      * @static
1088      * @param {Array} o Or Array like object (eg. nodelist)
1089      * @returns {Array} 
1090      */
1091     from : function(o)
1092     {
1093         var ret= [];
1094     
1095         for (var i =0; i < o.length; i++) { 
1096             ret[i] = o[i];
1097         }
1098         return ret;
1099       
1100     }
1101 });
1102 /*
1103  * Based on:
1104  * Ext JS Library 1.1.1
1105  * Copyright(c) 2006-2007, Ext JS, LLC.
1106  *
1107  * Originally Released Under LGPL - original licence link has changed is not relivant.
1108  *
1109  * Fork - LGPL
1110  * <script type="text/javascript">
1111  */
1112
1113 /**
1114  * @class Date
1115  *
1116  * The date parsing and format syntax is a subset of
1117  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1118  * supported will provide results equivalent to their PHP versions.
1119  *
1120  * Following is the list of all currently supported formats:
1121  *<pre>
1122 Sample date:
1123 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1124
1125 Format  Output      Description
1126 ------  ----------  --------------------------------------------------------------
1127   d      10         Day of the month, 2 digits with leading zeros
1128   D      Wed        A textual representation of a day, three letters
1129   j      10         Day of the month without leading zeros
1130   l      Wednesday  A full textual representation of the day of the week
1131   S      th         English ordinal day of month suffix, 2 chars (use with j)
1132   w      3          Numeric representation of the day of the week
1133   z      9          The julian date, or day of the year (0-365)
1134   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1135   F      January    A full textual representation of the month
1136   m      01         Numeric representation of a month, with leading zeros
1137   M      Jan        Month name abbreviation, three letters
1138   n      1          Numeric representation of a month, without leading zeros
1139   t      31         Number of days in the given month
1140   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1141   Y      2007       A full numeric representation of a year, 4 digits
1142   y      07         A two digit representation of a year
1143   a      pm         Lowercase Ante meridiem and Post meridiem
1144   A      PM         Uppercase Ante meridiem and Post meridiem
1145   g      3          12-hour format of an hour without leading zeros
1146   G      15         24-hour format of an hour without leading zeros
1147   h      03         12-hour format of an hour with leading zeros
1148   H      15         24-hour format of an hour with leading zeros
1149   i      05         Minutes with leading zeros
1150   s      01         Seconds, with leading zeros
1151   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1152   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1153   T      CST        Timezone setting of the machine running the code
1154   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1155 </pre>
1156  *
1157  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1158  * <pre><code>
1159 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1160 document.write(dt.format('Y-m-d'));                         //2007-01-10
1161 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1162 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
1163  </code></pre>
1164  *
1165  * Here are some standard date/time patterns that you might find helpful.  They
1166  * are not part of the source of Date.js, but to use them you can simply copy this
1167  * block of code into any script that is included after Date.js and they will also become
1168  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1169  * <pre><code>
1170 Date.patterns = {
1171     ISO8601Long:"Y-m-d H:i:s",
1172     ISO8601Short:"Y-m-d",
1173     ShortDate: "n/j/Y",
1174     LongDate: "l, F d, Y",
1175     FullDateTime: "l, F d, Y g:i:s A",
1176     MonthDay: "F d",
1177     ShortTime: "g:i A",
1178     LongTime: "g:i:s A",
1179     SortableDateTime: "Y-m-d\\TH:i:s",
1180     UniversalSortableDateTime: "Y-m-d H:i:sO",
1181     YearMonth: "F, Y"
1182 };
1183 </code></pre>
1184  *
1185  * Example usage:
1186  * <pre><code>
1187 var dt = new Date();
1188 document.write(dt.format(Date.patterns.ShortDate));
1189  </code></pre>
1190  */
1191
1192 /*
1193  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1194  * They generate precompiled functions from date formats instead of parsing and
1195  * processing the pattern every time you format a date.  These functions are available
1196  * on every Date object (any javascript function).
1197  *
1198  * The original article and download are here:
1199  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1200  *
1201  */
1202  
1203  
1204  // was in core
1205 /**
1206  Returns the number of milliseconds between this date and date
1207  @param {Date} date (optional) Defaults to now
1208  @return {Number} The diff in milliseconds
1209  @member Date getElapsed
1210  */
1211 Date.prototype.getElapsed = function(date) {
1212         return Math.abs((date || new Date()).getTime()-this.getTime());
1213 };
1214 // was in date file..
1215
1216
1217 // private
1218 Date.parseFunctions = {count:0};
1219 // private
1220 Date.parseRegexes = [];
1221 // private
1222 Date.formatFunctions = {count:0};
1223
1224 // private
1225 Date.prototype.dateFormat = function(format) {
1226     if (Date.formatFunctions[format] == null) {
1227         Date.createNewFormat(format);
1228     }
1229     var func = Date.formatFunctions[format];
1230     return this[func]();
1231 };
1232
1233
1234 /**
1235  * Formats a date given the supplied format string
1236  * @param {String} format The format string
1237  * @return {String} The formatted date
1238  * @method
1239  */
1240 Date.prototype.format = Date.prototype.dateFormat;
1241
1242 // private
1243 Date.createNewFormat = function(format) {
1244     var funcName = "format" + Date.formatFunctions.count++;
1245     Date.formatFunctions[format] = funcName;
1246     var code = "Date.prototype." + funcName + " = function(){return ";
1247     var special = false;
1248     var ch = '';
1249     for (var i = 0; i < format.length; ++i) {
1250         ch = format.charAt(i);
1251         if (!special && ch == "\\") {
1252             special = true;
1253         }
1254         else if (special) {
1255             special = false;
1256             code += "'" + String.escape(ch) + "' + ";
1257         }
1258         else {
1259             code += Date.getFormatCode(ch);
1260         }
1261     }
1262     /** eval:var:zzzzzzzzzzzzz */
1263     eval(code.substring(0, code.length - 3) + ";}");
1264 };
1265
1266 // private
1267 Date.getFormatCode = function(character) {
1268     switch (character) {
1269     case "d":
1270         return "String.leftPad(this.getDate(), 2, '0') + ";
1271     case "D":
1272         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1273     case "j":
1274         return "this.getDate() + ";
1275     case "l":
1276         return "Date.dayNames[this.getDay()] + ";
1277     case "S":
1278         return "this.getSuffix() + ";
1279     case "w":
1280         return "this.getDay() + ";
1281     case "z":
1282         return "this.getDayOfYear() + ";
1283     case "W":
1284         return "this.getWeekOfYear() + ";
1285     case "F":
1286         return "Date.monthNames[this.getMonth()] + ";
1287     case "m":
1288         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1289     case "M":
1290         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1291     case "n":
1292         return "(this.getMonth() + 1) + ";
1293     case "t":
1294         return "this.getDaysInMonth() + ";
1295     case "L":
1296         return "(this.isLeapYear() ? 1 : 0) + ";
1297     case "Y":
1298         return "this.getFullYear() + ";
1299     case "y":
1300         return "('' + this.getFullYear()).substring(2, 4) + ";
1301     case "a":
1302         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1303     case "A":
1304         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1305     case "g":
1306         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1307     case "G":
1308         return "this.getHours() + ";
1309     case "h":
1310         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1311     case "H":
1312         return "String.leftPad(this.getHours(), 2, '0') + ";
1313     case "i":
1314         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1315     case "s":
1316         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1317     case "O":
1318         return "this.getGMTOffset() + ";
1319     case "P":
1320         return "this.getGMTColonOffset() + ";
1321     case "T":
1322         return "this.getTimezone() + ";
1323     case "Z":
1324         return "(this.getTimezoneOffset() * -60) + ";
1325     default:
1326         return "'" + String.escape(character) + "' + ";
1327     }
1328 };
1329
1330 /**
1331  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1332  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1333  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1334  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1335  * string or the parse operation will fail.
1336  * Example Usage:
1337 <pre><code>
1338 //dt = Fri May 25 2007 (current date)
1339 var dt = new Date();
1340
1341 //dt = Thu May 25 2006 (today's month/day in 2006)
1342 dt = Date.parseDate("2006", "Y");
1343
1344 //dt = Sun Jan 15 2006 (all date parts specified)
1345 dt = Date.parseDate("2006-1-15", "Y-m-d");
1346
1347 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1348 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1349 </code></pre>
1350  * @param {String} input The unparsed date as a string
1351  * @param {String} format The format the date is in
1352  * @return {Date} The parsed date
1353  * @static
1354  */
1355 Date.parseDate = function(input, format) {
1356     if (Date.parseFunctions[format] == null) {
1357         Date.createParser(format);
1358     }
1359     var func = Date.parseFunctions[format];
1360     return Date[func](input);
1361 };
1362 /**
1363  * @private
1364  */
1365
1366 Date.createParser = function(format) {
1367     var funcName = "parse" + Date.parseFunctions.count++;
1368     var regexNum = Date.parseRegexes.length;
1369     var currentGroup = 1;
1370     Date.parseFunctions[format] = funcName;
1371
1372     var code = "Date." + funcName + " = function(input){\n"
1373         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1374         + "var d = new Date();\n"
1375         + "y = d.getFullYear();\n"
1376         + "m = d.getMonth();\n"
1377         + "d = d.getDate();\n"
1378         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1379         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1380         + "if (results && results.length > 0) {";
1381     var regex = "";
1382
1383     var special = false;
1384     var ch = '';
1385     for (var i = 0; i < format.length; ++i) {
1386         ch = format.charAt(i);
1387         if (!special && ch == "\\") {
1388             special = true;
1389         }
1390         else if (special) {
1391             special = false;
1392             regex += String.escape(ch);
1393         }
1394         else {
1395             var obj = Date.formatCodeToRegex(ch, currentGroup);
1396             currentGroup += obj.g;
1397             regex += obj.s;
1398             if (obj.g && obj.c) {
1399                 code += obj.c;
1400             }
1401         }
1402     }
1403
1404     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1405         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1406         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1407         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1408         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1409         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1410         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1411         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1412         + "else if (y >= 0 && m >= 0)\n"
1413         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1414         + "else if (y >= 0)\n"
1415         + "{v = new Date(y); v.setFullYear(y);}\n"
1416         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1417         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1418         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1419         + ";}";
1420
1421     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1422     /** eval:var:zzzzzzzzzzzzz */
1423     eval(code);
1424 };
1425
1426 // private
1427 Date.formatCodeToRegex = function(character, currentGroup) {
1428     switch (character) {
1429     case "D":
1430         return {g:0,
1431         c:null,
1432         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1433     case "j":
1434         return {g:1,
1435             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1436             s:"(\\d{1,2})"}; // day of month without leading zeroes
1437     case "d":
1438         return {g:1,
1439             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1440             s:"(\\d{2})"}; // day of month with leading zeroes
1441     case "l":
1442         return {g:0,
1443             c:null,
1444             s:"(?:" + Date.dayNames.join("|") + ")"};
1445     case "S":
1446         return {g:0,
1447             c:null,
1448             s:"(?:st|nd|rd|th)"};
1449     case "w":
1450         return {g:0,
1451             c:null,
1452             s:"\\d"};
1453     case "z":
1454         return {g:0,
1455             c:null,
1456             s:"(?:\\d{1,3})"};
1457     case "W":
1458         return {g:0,
1459             c:null,
1460             s:"(?:\\d{2})"};
1461     case "F":
1462         return {g:1,
1463             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1464             s:"(" + Date.monthNames.join("|") + ")"};
1465     case "M":
1466         return {g:1,
1467             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1468             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1469     case "n":
1470         return {g:1,
1471             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1472             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1473     case "m":
1474         return {g:1,
1475             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1476             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1477     case "t":
1478         return {g:0,
1479             c:null,
1480             s:"\\d{1,2}"};
1481     case "L":
1482         return {g:0,
1483             c:null,
1484             s:"(?:1|0)"};
1485     case "Y":
1486         return {g:1,
1487             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1488             s:"(\\d{4})"};
1489     case "y":
1490         return {g:1,
1491             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1492                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1493             s:"(\\d{1,2})"};
1494     case "a":
1495         return {g:1,
1496             c:"if (results[" + currentGroup + "] == 'am') {\n"
1497                 + "if (h == 12) { h = 0; }\n"
1498                 + "} else { if (h < 12) { h += 12; }}",
1499             s:"(am|pm)"};
1500     case "A":
1501         return {g:1,
1502             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1503                 + "if (h == 12) { h = 0; }\n"
1504                 + "} else { if (h < 12) { h += 12; }}",
1505             s:"(AM|PM)"};
1506     case "g":
1507     case "G":
1508         return {g:1,
1509             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1510             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1511     case "h":
1512     case "H":
1513         return {g:1,
1514             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1515             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1516     case "i":
1517         return {g:1,
1518             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1519             s:"(\\d{2})"};
1520     case "s":
1521         return {g:1,
1522             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1523             s:"(\\d{2})"};
1524     case "O":
1525         return {g:1,
1526             c:[
1527                 "o = results[", currentGroup, "];\n",
1528                 "var sn = o.substring(0,1);\n", // get + / - sign
1529                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1530                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1531                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1532                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1533             ].join(""),
1534             s:"([+\-]\\d{2,4})"};
1535     
1536     
1537     case "P":
1538         return {g:1,
1539                 c:[
1540                    "o = results[", currentGroup, "];\n",
1541                    "var sn = o.substring(0,1);\n",
1542                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1543                    "var mn = o.substring(4,6) % 60;\n",
1544                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1545                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1546             ].join(""),
1547             s:"([+\-]\\d{4})"};
1548     case "T":
1549         return {g:0,
1550             c:null,
1551             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1552     case "Z":
1553         return {g:1,
1554             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1555                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1556             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1557     default:
1558         return {g:0,
1559             c:null,
1560             s:String.escape(character)};
1561     }
1562 };
1563
1564 /**
1565  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1566  * @return {String} The abbreviated timezone name (e.g. 'CST')
1567  */
1568 Date.prototype.getTimezone = function() {
1569     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1570 };
1571
1572 /**
1573  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1574  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1575  */
1576 Date.prototype.getGMTOffset = function() {
1577     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1578         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1579         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1580 };
1581
1582 /**
1583  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1584  * @return {String} 2-characters representing hours and 2-characters representing minutes
1585  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1586  */
1587 Date.prototype.getGMTColonOffset = function() {
1588         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1589                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1590                 + ":"
1591                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1592 }
1593
1594 /**
1595  * Get the numeric day number of the year, adjusted for leap year.
1596  * @return {Number} 0 through 364 (365 in leap years)
1597  */
1598 Date.prototype.getDayOfYear = function() {
1599     var num = 0;
1600     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1601     for (var i = 0; i < this.getMonth(); ++i) {
1602         num += Date.daysInMonth[i];
1603     }
1604     return num + this.getDate() - 1;
1605 };
1606
1607 /**
1608  * Get the string representation of the numeric week number of the year
1609  * (equivalent to the format specifier 'W').
1610  * @return {String} '00' through '52'
1611  */
1612 Date.prototype.getWeekOfYear = function() {
1613     // Skip to Thursday of this week
1614     var now = this.getDayOfYear() + (4 - this.getDay());
1615     // Find the first Thursday of the year
1616     var jan1 = new Date(this.getFullYear(), 0, 1);
1617     var then = (7 - jan1.getDay() + 4);
1618     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1619 };
1620
1621 /**
1622  * Whether or not the current date is in a leap year.
1623  * @return {Boolean} True if the current date is in a leap year, else false
1624  */
1625 Date.prototype.isLeapYear = function() {
1626     var year = this.getFullYear();
1627     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1628 };
1629
1630 /**
1631  * Get the first day of the current month, adjusted for leap year.  The returned value
1632  * is the numeric day index within the week (0-6) which can be used in conjunction with
1633  * the {@link #monthNames} array to retrieve the textual day name.
1634  * Example:
1635  *<pre><code>
1636 var dt = new Date('1/10/2007');
1637 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1638 </code></pre>
1639  * @return {Number} The day number (0-6)
1640  */
1641 Date.prototype.getFirstDayOfMonth = function() {
1642     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1643     return (day < 0) ? (day + 7) : day;
1644 };
1645
1646 /**
1647  * Get the last day of the current month, adjusted for leap year.  The returned value
1648  * is the numeric day index within the week (0-6) which can be used in conjunction with
1649  * the {@link #monthNames} array to retrieve the textual day name.
1650  * Example:
1651  *<pre><code>
1652 var dt = new Date('1/10/2007');
1653 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1654 </code></pre>
1655  * @return {Number} The day number (0-6)
1656  */
1657 Date.prototype.getLastDayOfMonth = function() {
1658     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1659     return (day < 0) ? (day + 7) : day;
1660 };
1661
1662
1663 /**
1664  * Get the first date of this date's month
1665  * @return {Date}
1666  */
1667 Date.prototype.getFirstDateOfMonth = function() {
1668     return new Date(this.getFullYear(), this.getMonth(), 1);
1669 };
1670
1671 /**
1672  * Get the last date of this date's month
1673  * @return {Date}
1674  */
1675 Date.prototype.getLastDateOfMonth = function() {
1676     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1677 };
1678 /**
1679  * Get the number of days in the current month, adjusted for leap year.
1680  * @return {Number} The number of days in the month
1681  */
1682 Date.prototype.getDaysInMonth = function() {
1683     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1684     return Date.daysInMonth[this.getMonth()];
1685 };
1686
1687 /**
1688  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1689  * @return {String} 'st, 'nd', 'rd' or 'th'
1690  */
1691 Date.prototype.getSuffix = function() {
1692     switch (this.getDate()) {
1693         case 1:
1694         case 21:
1695         case 31:
1696             return "st";
1697         case 2:
1698         case 22:
1699             return "nd";
1700         case 3:
1701         case 23:
1702             return "rd";
1703         default:
1704             return "th";
1705     }
1706 };
1707
1708 // private
1709 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1710
1711 /**
1712  * An array of textual month names.
1713  * Override these values for international dates, for example...
1714  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1715  * @type Array
1716  * @static
1717  */
1718 Date.monthNames =
1719    ["January",
1720     "February",
1721     "March",
1722     "April",
1723     "May",
1724     "June",
1725     "July",
1726     "August",
1727     "September",
1728     "October",
1729     "November",
1730     "December"];
1731
1732 /**
1733  * An array of textual day names.
1734  * Override these values for international dates, for example...
1735  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1736  * @type Array
1737  * @static
1738  */
1739 Date.dayNames =
1740    ["Sunday",
1741     "Monday",
1742     "Tuesday",
1743     "Wednesday",
1744     "Thursday",
1745     "Friday",
1746     "Saturday"];
1747
1748 // private
1749 Date.y2kYear = 50;
1750 // private
1751 Date.monthNumbers = {
1752     Jan:0,
1753     Feb:1,
1754     Mar:2,
1755     Apr:3,
1756     May:4,
1757     Jun:5,
1758     Jul:6,
1759     Aug:7,
1760     Sep:8,
1761     Oct:9,
1762     Nov:10,
1763     Dec:11};
1764
1765 /**
1766  * Creates and returns a new Date instance with the exact same date value as the called instance.
1767  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1768  * variable will also be changed.  When the intention is to create a new variable that will not
1769  * modify the original instance, you should create a clone.
1770  *
1771  * Example of correctly cloning a date:
1772  * <pre><code>
1773 //wrong way:
1774 var orig = new Date('10/1/2006');
1775 var copy = orig;
1776 copy.setDate(5);
1777 document.write(orig);  //returns 'Thu Oct 05 2006'!
1778
1779 //correct way:
1780 var orig = new Date('10/1/2006');
1781 var copy = orig.clone();
1782 copy.setDate(5);
1783 document.write(orig);  //returns 'Thu Oct 01 2006'
1784 </code></pre>
1785  * @return {Date} The new Date instance
1786  */
1787 Date.prototype.clone = function() {
1788         return new Date(this.getTime());
1789 };
1790
1791 /**
1792  * Clears any time information from this date
1793  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1794  @return {Date} this or the clone
1795  */
1796 Date.prototype.clearTime = function(clone){
1797     if(clone){
1798         return this.clone().clearTime();
1799     }
1800     this.setHours(0);
1801     this.setMinutes(0);
1802     this.setSeconds(0);
1803     this.setMilliseconds(0);
1804     return this;
1805 };
1806
1807 // private
1808 // safari setMonth is broken -- check that this is only donw once...
1809 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1810     Date.brokenSetMonth = Date.prototype.setMonth;
1811         Date.prototype.setMonth = function(num){
1812                 if(num <= -1){
1813                         var n = Math.ceil(-num);
1814                         var back_year = Math.ceil(n/12);
1815                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1816                         this.setFullYear(this.getFullYear() - back_year);
1817                         return Date.brokenSetMonth.call(this, month);
1818                 } else {
1819                         return Date.brokenSetMonth.apply(this, arguments);
1820                 }
1821         };
1822 }
1823
1824 /** Date interval constant 
1825 * @static 
1826 * @type String */
1827 Date.MILLI = "ms";
1828 /** Date interval constant 
1829 * @static 
1830 * @type String */
1831 Date.SECOND = "s";
1832 /** Date interval constant 
1833 * @static 
1834 * @type String */
1835 Date.MINUTE = "mi";
1836 /** Date interval constant 
1837 * @static 
1838 * @type String */
1839 Date.HOUR = "h";
1840 /** Date interval constant 
1841 * @static 
1842 * @type String */
1843 Date.DAY = "d";
1844 /** Date interval constant 
1845 * @static 
1846 * @type String */
1847 Date.MONTH = "mo";
1848 /** Date interval constant 
1849 * @static 
1850 * @type String */
1851 Date.YEAR = "y";
1852
1853 /**
1854  * Provides a convenient method of performing basic date arithmetic.  This method
1855  * does not modify the Date instance being called - it creates and returns
1856  * a new Date instance containing the resulting date value.
1857  *
1858  * Examples:
1859  * <pre><code>
1860 //Basic usage:
1861 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1862 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1863
1864 //Negative values will subtract correctly:
1865 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1866 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1867
1868 //You can even chain several calls together in one line!
1869 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1870 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1871  </code></pre>
1872  *
1873  * @param {String} interval   A valid date interval enum value
1874  * @param {Number} value      The amount to add to the current date
1875  * @return {Date} The new Date instance
1876  */
1877 Date.prototype.add = function(interval, value){
1878   var d = this.clone();
1879   if (!interval || value === 0) { return d; }
1880   switch(interval.toLowerCase()){
1881     case Date.MILLI:
1882       d.setMilliseconds(this.getMilliseconds() + value);
1883       break;
1884     case Date.SECOND:
1885       d.setSeconds(this.getSeconds() + value);
1886       break;
1887     case Date.MINUTE:
1888       d.setMinutes(this.getMinutes() + value);
1889       break;
1890     case Date.HOUR:
1891       d.setHours(this.getHours() + value);
1892       break;
1893     case Date.DAY:
1894       d.setDate(this.getDate() + value);
1895       break;
1896     case Date.MONTH:
1897       var day = this.getDate();
1898       if(day > 28){
1899           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1900       }
1901       d.setDate(day);
1902       d.setMonth(this.getMonth() + value);
1903       break;
1904     case Date.YEAR:
1905       d.setFullYear(this.getFullYear() + value);
1906       break;
1907   }
1908   return d;
1909 };
1910 /**
1911  * @class Roo.lib.Dom
1912  * @licence LGPL
1913  * @static
1914  * 
1915  * Dom utils (from YIU afaik)
1916  *
1917  * 
1918  **/
1919 Roo.lib.Dom = {
1920     /**
1921      * Get the view width
1922      * @param {Boolean} full True will get the full document, otherwise it's the view width
1923      * @return {Number} The width
1924      */
1925      
1926     getViewWidth : function(full) {
1927         return full ? this.getDocumentWidth() : this.getViewportWidth();
1928     },
1929     /**
1930      * Get the view height
1931      * @param {Boolean} full True will get the full document, otherwise it's the view height
1932      * @return {Number} The height
1933      */
1934     getViewHeight : function(full) {
1935         return full ? this.getDocumentHeight() : this.getViewportHeight();
1936     },
1937     /**
1938      * Get the Full Document height 
1939      * @return {Number} The height
1940      */
1941     getDocumentHeight: function() {
1942         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1943         return Math.max(scrollHeight, this.getViewportHeight());
1944     },
1945     /**
1946      * Get the Full Document width
1947      * @return {Number} The width
1948      */
1949     getDocumentWidth: function() {
1950         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1951         return Math.max(scrollWidth, this.getViewportWidth());
1952     },
1953     /**
1954      * Get the Window Viewport height
1955      * @return {Number} The height
1956      */
1957     getViewportHeight: function() {
1958         var height = self.innerHeight;
1959         var mode = document.compatMode;
1960
1961         if ((mode || Roo.isIE) && !Roo.isOpera) {
1962             height = (mode == "CSS1Compat") ?
1963                      document.documentElement.clientHeight :
1964                      document.body.clientHeight;
1965         }
1966
1967         return height;
1968     },
1969     /**
1970      * Get the Window Viewport width
1971      * @return {Number} The width
1972      */
1973     getViewportWidth: function() {
1974         var width = self.innerWidth;
1975         var mode = document.compatMode;
1976
1977         if (mode || Roo.isIE) {
1978             width = (mode == "CSS1Compat") ?
1979                     document.documentElement.clientWidth :
1980                     document.body.clientWidth;
1981         }
1982         return width;
1983     },
1984
1985     isAncestor : function(p, c) {
1986         p = Roo.getDom(p);
1987         c = Roo.getDom(c);
1988         if (!p || !c) {
1989             return false;
1990         }
1991
1992         if (p.contains && !Roo.isSafari) {
1993             return p.contains(c);
1994         } else if (p.compareDocumentPosition) {
1995             return !!(p.compareDocumentPosition(c) & 16);
1996         } else {
1997             var parent = c.parentNode;
1998             while (parent) {
1999                 if (parent == p) {
2000                     return true;
2001                 }
2002                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
2003                     return false;
2004                 }
2005                 parent = parent.parentNode;
2006             }
2007             return false;
2008         }
2009     },
2010
2011     getRegion : function(el) {
2012         return Roo.lib.Region.getRegion(el);
2013     },
2014
2015     getY : function(el) {
2016         return this.getXY(el)[1];
2017     },
2018
2019     getX : function(el) {
2020         return this.getXY(el)[0];
2021     },
2022
2023     getXY : function(el) {
2024         var p, pe, b, scroll, bd = document.body;
2025         el = Roo.getDom(el);
2026         var fly = Roo.lib.AnimBase.fly;
2027         if (el.getBoundingClientRect) {
2028             b = el.getBoundingClientRect();
2029             scroll = fly(document).getScroll();
2030             return [b.left + scroll.left, b.top + scroll.top];
2031         }
2032         var x = 0, y = 0;
2033
2034         p = el;
2035
2036         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2037
2038         while (p) {
2039
2040             x += p.offsetLeft;
2041             y += p.offsetTop;
2042
2043             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2044                 hasAbsolute = true;
2045             }
2046
2047             if (Roo.isGecko) {
2048                 pe = fly(p);
2049
2050                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2051                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2052
2053
2054                 x += bl;
2055                 y += bt;
2056
2057
2058                 if (p != el && pe.getStyle('overflow') != 'visible') {
2059                     x += bl;
2060                     y += bt;
2061                 }
2062             }
2063             p = p.offsetParent;
2064         }
2065
2066         if (Roo.isSafari && hasAbsolute) {
2067             x -= bd.offsetLeft;
2068             y -= bd.offsetTop;
2069         }
2070
2071         if (Roo.isGecko && !hasAbsolute) {
2072             var dbd = fly(bd);
2073             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2074             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2075         }
2076
2077         p = el.parentNode;
2078         while (p && p != bd) {
2079             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2080                 x -= p.scrollLeft;
2081                 y -= p.scrollTop;
2082             }
2083             p = p.parentNode;
2084         }
2085         return [x, y];
2086     },
2087  
2088   
2089
2090
2091     setXY : function(el, xy) {
2092         el = Roo.fly(el, '_setXY');
2093         el.position();
2094         var pts = el.translatePoints(xy);
2095         if (xy[0] !== false) {
2096             el.dom.style.left = pts.left + "px";
2097         }
2098         if (xy[1] !== false) {
2099             el.dom.style.top = pts.top + "px";
2100         }
2101     },
2102
2103     setX : function(el, x) {
2104         this.setXY(el, [x, false]);
2105     },
2106
2107     setY : function(el, y) {
2108         this.setXY(el, [false, y]);
2109     }
2110 };
2111 /*
2112  * Portions of this file are based on pieces of Yahoo User Interface Library
2113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2114  * YUI licensed under the BSD License:
2115  * http://developer.yahoo.net/yui/license.txt
2116  * <script type="text/javascript">
2117  *
2118  */
2119
2120 Roo.lib.Event = function() {
2121     var loadComplete = false;
2122     var listeners = [];
2123     var unloadListeners = [];
2124     var retryCount = 0;
2125     var onAvailStack = [];
2126     var counter = 0;
2127     var lastError = null;
2128
2129     return {
2130         POLL_RETRYS: 200,
2131         POLL_INTERVAL: 20,
2132         EL: 0,
2133         TYPE: 1,
2134         FN: 2,
2135         WFN: 3,
2136         OBJ: 3,
2137         ADJ_SCOPE: 4,
2138         _interval: null,
2139
2140         startInterval: function() {
2141             if (!this._interval) {
2142                 var self = this;
2143                 var callback = function() {
2144                     self._tryPreloadAttach();
2145                 };
2146                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2147
2148             }
2149         },
2150
2151         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2152             onAvailStack.push({ id:         p_id,
2153                 fn:         p_fn,
2154                 obj:        p_obj,
2155                 override:   p_override,
2156                 checkReady: false    });
2157
2158             retryCount = this.POLL_RETRYS;
2159             this.startInterval();
2160         },
2161
2162
2163         addListener: function(el, eventName, fn) {
2164             el = Roo.getDom(el);
2165             if (!el || !fn) {
2166                 return false;
2167             }
2168
2169             if ("unload" == eventName) {
2170                 unloadListeners[unloadListeners.length] =
2171                 [el, eventName, fn];
2172                 return true;
2173             }
2174
2175             var wrappedFn = function(e) {
2176                 return fn(Roo.lib.Event.getEvent(e));
2177             };
2178
2179             var li = [el, eventName, fn, wrappedFn];
2180
2181             var index = listeners.length;
2182             listeners[index] = li;
2183
2184             this.doAdd(el, eventName, wrappedFn, false);
2185             return true;
2186
2187         },
2188
2189
2190         removeListener: function(el, eventName, fn) {
2191             var i, len;
2192
2193             el = Roo.getDom(el);
2194
2195             if(!fn) {
2196                 return this.purgeElement(el, false, eventName);
2197             }
2198
2199
2200             if ("unload" == eventName) {
2201
2202                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2203                     var li = unloadListeners[i];
2204                     if (li &&
2205                         li[0] == el &&
2206                         li[1] == eventName &&
2207                         li[2] == fn) {
2208                         unloadListeners.splice(i, 1);
2209                         return true;
2210                     }
2211                 }
2212
2213                 return false;
2214             }
2215
2216             var cacheItem = null;
2217
2218
2219             var index = arguments[3];
2220
2221             if ("undefined" == typeof index) {
2222                 index = this._getCacheIndex(el, eventName, fn);
2223             }
2224
2225             if (index >= 0) {
2226                 cacheItem = listeners[index];
2227             }
2228
2229             if (!el || !cacheItem) {
2230                 return false;
2231             }
2232
2233             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2234
2235             delete listeners[index][this.WFN];
2236             delete listeners[index][this.FN];
2237             listeners.splice(index, 1);
2238
2239             return true;
2240
2241         },
2242
2243
2244         getTarget: function(ev, resolveTextNode) {
2245             ev = ev.browserEvent || ev;
2246             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2247             var t = ev.target || ev.srcElement;
2248             return this.resolveTextNode(t);
2249         },
2250
2251
2252         resolveTextNode: function(node) {
2253             if (Roo.isSafari && node && 3 == node.nodeType) {
2254                 return node.parentNode;
2255             } else {
2256                 return node;
2257             }
2258         },
2259
2260
2261         getPageX: function(ev) {
2262             ev = ev.browserEvent || ev;
2263             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2264             var x = ev.pageX;
2265             if (!x && 0 !== x) {
2266                 x = ev.clientX || 0;
2267
2268                 if (Roo.isIE) {
2269                     x += this.getScroll()[1];
2270                 }
2271             }
2272
2273             return x;
2274         },
2275
2276
2277         getPageY: function(ev) {
2278             ev = ev.browserEvent || ev;
2279             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2280             var y = ev.pageY;
2281             if (!y && 0 !== y) {
2282                 y = ev.clientY || 0;
2283
2284                 if (Roo.isIE) {
2285                     y += this.getScroll()[0];
2286                 }
2287             }
2288
2289
2290             return y;
2291         },
2292
2293
2294         getXY: function(ev) {
2295             ev = ev.browserEvent || ev;
2296             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2297             return [this.getPageX(ev), this.getPageY(ev)];
2298         },
2299
2300
2301         getRelatedTarget: function(ev) {
2302             ev = ev.browserEvent || ev;
2303             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2304             var t = ev.relatedTarget;
2305             if (!t) {
2306                 if (ev.type == "mouseout") {
2307                     t = ev.toElement;
2308                 } else if (ev.type == "mouseover") {
2309                     t = ev.fromElement;
2310                 }
2311             }
2312
2313             return this.resolveTextNode(t);
2314         },
2315
2316
2317         getTime: function(ev) {
2318             ev = ev.browserEvent || ev;
2319             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2320             if (!ev.time) {
2321                 var t = new Date().getTime();
2322                 try {
2323                     ev.time = t;
2324                 } catch(ex) {
2325                     this.lastError = ex;
2326                     return t;
2327                 }
2328             }
2329
2330             return ev.time;
2331         },
2332
2333
2334         stopEvent: function(ev) {
2335             this.stopPropagation(ev);
2336             this.preventDefault(ev);
2337         },
2338
2339
2340         stopPropagation: function(ev) {
2341             ev = ev.browserEvent || ev;
2342             if (ev.stopPropagation) {
2343                 ev.stopPropagation();
2344             } else {
2345                 ev.cancelBubble = true;
2346             }
2347         },
2348
2349
2350         preventDefault: function(ev) {
2351             ev = ev.browserEvent || ev;
2352             if(ev.preventDefault) {
2353                 ev.preventDefault();
2354             } else {
2355                 ev.returnValue = false;
2356             }
2357         },
2358
2359
2360         getEvent: function(e) {
2361             var ev = e || window.event;
2362             if (!ev) {
2363                 var c = this.getEvent.caller;
2364                 while (c) {
2365                     ev = c.arguments[0];
2366                     if (ev && Event == ev.constructor) {
2367                         break;
2368                     }
2369                     c = c.caller;
2370                 }
2371             }
2372             return ev;
2373         },
2374
2375
2376         getCharCode: function(ev) {
2377             ev = ev.browserEvent || ev;
2378             return ev.charCode || ev.keyCode || 0;
2379         },
2380
2381
2382         _getCacheIndex: function(el, eventName, fn) {
2383             for (var i = 0,len = listeners.length; i < len; ++i) {
2384                 var li = listeners[i];
2385                 if (li &&
2386                     li[this.FN] == fn &&
2387                     li[this.EL] == el &&
2388                     li[this.TYPE] == eventName) {
2389                     return i;
2390                 }
2391             }
2392
2393             return -1;
2394         },
2395
2396
2397         elCache: {},
2398
2399
2400         getEl: function(id) {
2401             return document.getElementById(id);
2402         },
2403
2404
2405         clearCache: function() {
2406         },
2407
2408
2409         _load: function(e) {
2410             loadComplete = true;
2411             var EU = Roo.lib.Event;
2412
2413
2414             if (Roo.isIE) {
2415                 EU.doRemove(window, "load", EU._load);
2416             }
2417         },
2418
2419
2420         _tryPreloadAttach: function() {
2421
2422             if (this.locked) {
2423                 return false;
2424             }
2425
2426             this.locked = true;
2427
2428
2429             var tryAgain = !loadComplete;
2430             if (!tryAgain) {
2431                 tryAgain = (retryCount > 0);
2432             }
2433
2434
2435             var notAvail = [];
2436             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2437                 var item = onAvailStack[i];
2438                 if (item) {
2439                     var el = this.getEl(item.id);
2440
2441                     if (el) {
2442                         if (!item.checkReady ||
2443                             loadComplete ||
2444                             el.nextSibling ||
2445                             (document && document.body)) {
2446
2447                             var scope = el;
2448                             if (item.override) {
2449                                 if (item.override === true) {
2450                                     scope = item.obj;
2451                                 } else {
2452                                     scope = item.override;
2453                                 }
2454                             }
2455                             item.fn.call(scope, item.obj);
2456                             onAvailStack[i] = null;
2457                         }
2458                     } else {
2459                         notAvail.push(item);
2460                     }
2461                 }
2462             }
2463
2464             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2465
2466             if (tryAgain) {
2467
2468                 this.startInterval();
2469             } else {
2470                 clearInterval(this._interval);
2471                 this._interval = null;
2472             }
2473
2474             this.locked = false;
2475
2476             return true;
2477
2478         },
2479
2480
2481         purgeElement: function(el, recurse, eventName) {
2482             var elListeners = this.getListeners(el, eventName);
2483             if (elListeners) {
2484                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2485                     var l = elListeners[i];
2486                     this.removeListener(el, l.type, l.fn);
2487                 }
2488             }
2489
2490             if (recurse && el && el.childNodes) {
2491                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2492                     this.purgeElement(el.childNodes[i], recurse, eventName);
2493                 }
2494             }
2495         },
2496
2497
2498         getListeners: function(el, eventName) {
2499             var results = [], searchLists;
2500             if (!eventName) {
2501                 searchLists = [listeners, unloadListeners];
2502             } else if (eventName == "unload") {
2503                 searchLists = [unloadListeners];
2504             } else {
2505                 searchLists = [listeners];
2506             }
2507
2508             for (var j = 0; j < searchLists.length; ++j) {
2509                 var searchList = searchLists[j];
2510                 if (searchList && searchList.length > 0) {
2511                     for (var i = 0,len = searchList.length; i < len; ++i) {
2512                         var l = searchList[i];
2513                         if (l && l[this.EL] === el &&
2514                             (!eventName || eventName === l[this.TYPE])) {
2515                             results.push({
2516                                 type:   l[this.TYPE],
2517                                 fn:     l[this.FN],
2518                                 obj:    l[this.OBJ],
2519                                 adjust: l[this.ADJ_SCOPE],
2520                                 index:  i
2521                             });
2522                         }
2523                     }
2524                 }
2525             }
2526
2527             return (results.length) ? results : null;
2528         },
2529
2530
2531         _unload: function(e) {
2532
2533             var EU = Roo.lib.Event, i, j, l, len, index;
2534
2535             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2536                 l = unloadListeners[i];
2537                 if (l) {
2538                     var scope = window;
2539                     if (l[EU.ADJ_SCOPE]) {
2540                         if (l[EU.ADJ_SCOPE] === true) {
2541                             scope = l[EU.OBJ];
2542                         } else {
2543                             scope = l[EU.ADJ_SCOPE];
2544                         }
2545                     }
2546                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2547                     unloadListeners[i] = null;
2548                     l = null;
2549                     scope = null;
2550                 }
2551             }
2552
2553             unloadListeners = null;
2554
2555             if (listeners && listeners.length > 0) {
2556                 j = listeners.length;
2557                 while (j) {
2558                     index = j - 1;
2559                     l = listeners[index];
2560                     if (l) {
2561                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2562                                 l[EU.FN], index);
2563                     }
2564                     j = j - 1;
2565                 }
2566                 l = null;
2567
2568                 EU.clearCache();
2569             }
2570
2571             EU.doRemove(window, "unload", EU._unload);
2572
2573         },
2574
2575
2576         getScroll: function() {
2577             var dd = document.documentElement, db = document.body;
2578             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2579                 return [dd.scrollTop, dd.scrollLeft];
2580             } else if (db) {
2581                 return [db.scrollTop, db.scrollLeft];
2582             } else {
2583                 return [0, 0];
2584             }
2585         },
2586
2587
2588         doAdd: function () {
2589             if (window.addEventListener) {
2590                 return function(el, eventName, fn, capture) {
2591                     el.addEventListener(eventName, fn, (capture));
2592                 };
2593             } else if (window.attachEvent) {
2594                 return function(el, eventName, fn, capture) {
2595                     el.attachEvent("on" + eventName, fn);
2596                 };
2597             } else {
2598                 return function() {
2599                 };
2600             }
2601         }(),
2602
2603
2604         doRemove: function() {
2605             if (window.removeEventListener) {
2606                 return function (el, eventName, fn, capture) {
2607                     el.removeEventListener(eventName, fn, (capture));
2608                 };
2609             } else if (window.detachEvent) {
2610                 return function (el, eventName, fn) {
2611                     el.detachEvent("on" + eventName, fn);
2612                 };
2613             } else {
2614                 return function() {
2615                 };
2616             }
2617         }()
2618     };
2619     
2620 }();
2621 (function() {     
2622    
2623     var E = Roo.lib.Event;
2624     E.on = E.addListener;
2625     E.un = E.removeListener;
2626
2627     if (document && document.body) {
2628         E._load();
2629     } else {
2630         E.doAdd(window, "load", E._load);
2631     }
2632     E.doAdd(window, "unload", E._unload);
2633     E._tryPreloadAttach();
2634 })();
2635
2636  
2637
2638 (function() {
2639     /**
2640      * @class Roo.lib.Ajax
2641      *
2642      * provide a simple Ajax request utility functions
2643      * 
2644      * Portions of this file are based on pieces of Yahoo User Interface Library
2645     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2646     * YUI licensed under the BSD License:
2647     * http://developer.yahoo.net/yui/license.txt
2648     * <script type="text/javascript">
2649     *
2650      *
2651      */
2652     Roo.lib.Ajax = {
2653         /**
2654          * @static 
2655          */
2656         request : function(method, uri, cb, data, options) {
2657             if(options){
2658                 var hs = options.headers;
2659                 if(hs){
2660                     for(var h in hs){
2661                         if(hs.hasOwnProperty(h)){
2662                             this.initHeader(h, hs[h], false);
2663                         }
2664                     }
2665                 }
2666                 if(options.xmlData){
2667                     this.initHeader('Content-Type', 'text/xml', false);
2668                     method = 'POST';
2669                     data = options.xmlData;
2670                 }
2671             }
2672
2673             return this.asyncRequest(method, uri, cb, data);
2674         },
2675         /**
2676          * serialize a form
2677          *
2678          * @static
2679          * @param {DomForm} form element
2680          * @return {String} urlencode form output.
2681          */
2682         serializeForm : function(form) {
2683             if(typeof form == 'string') {
2684                 form = (document.getElementById(form) || document.forms[form]);
2685             }
2686
2687             var el, name, val, disabled, data = '', hasSubmit = false;
2688             for (var i = 0; i < form.elements.length; i++) {
2689                 el = form.elements[i];
2690                 disabled = form.elements[i].disabled;
2691                 name = form.elements[i].name;
2692                 val = form.elements[i].value;
2693
2694                 if (!disabled && name){
2695                     switch (el.type)
2696                             {
2697                         case 'select-one':
2698                         case 'select-multiple':
2699                             for (var j = 0; j < el.options.length; j++) {
2700                                 if (el.options[j].selected) {
2701                                     if (Roo.isIE) {
2702                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2703                                     }
2704                                     else {
2705                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2706                                     }
2707                                 }
2708                             }
2709                             break;
2710                         case 'radio':
2711                         case 'checkbox':
2712                             if (el.checked) {
2713                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2714                             }
2715                             break;
2716                         case 'file':
2717
2718                         case undefined:
2719
2720                         case 'reset':
2721
2722                         case 'button':
2723
2724                             break;
2725                         case 'submit':
2726                             if(hasSubmit == false) {
2727                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2728                                 hasSubmit = true;
2729                             }
2730                             break;
2731                         default:
2732                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2733                             break;
2734                     }
2735                 }
2736             }
2737             data = data.substr(0, data.length - 1);
2738             return data;
2739         },
2740
2741         headers:{},
2742
2743         hasHeaders:false,
2744
2745         useDefaultHeader:true,
2746
2747         defaultPostHeader:'application/x-www-form-urlencoded',
2748
2749         useDefaultXhrHeader:true,
2750
2751         defaultXhrHeader:'XMLHttpRequest',
2752
2753         hasDefaultHeaders:true,
2754
2755         defaultHeaders:{},
2756
2757         poll:{},
2758
2759         timeout:{},
2760
2761         pollInterval:50,
2762
2763         transactionId:0,
2764
2765         setProgId:function(id)
2766         {
2767             this.activeX.unshift(id);
2768         },
2769
2770         setDefaultPostHeader:function(b)
2771         {
2772             this.useDefaultHeader = b;
2773         },
2774
2775         setDefaultXhrHeader:function(b)
2776         {
2777             this.useDefaultXhrHeader = b;
2778         },
2779
2780         setPollingInterval:function(i)
2781         {
2782             if (typeof i == 'number' && isFinite(i)) {
2783                 this.pollInterval = i;
2784             }
2785         },
2786
2787         createXhrObject:function(transactionId)
2788         {
2789             var obj,http;
2790             try
2791             {
2792
2793                 http = new XMLHttpRequest();
2794
2795                 obj = { conn:http, tId:transactionId };
2796             }
2797             catch(e)
2798             {
2799                 for (var i = 0; i < this.activeX.length; ++i) {
2800                     try
2801                     {
2802
2803                         http = new ActiveXObject(this.activeX[i]);
2804
2805                         obj = { conn:http, tId:transactionId };
2806                         break;
2807                     }
2808                     catch(e) {
2809                     }
2810                 }
2811             }
2812             finally
2813             {
2814                 return obj;
2815             }
2816         },
2817
2818         getConnectionObject:function()
2819         {
2820             var o;
2821             var tId = this.transactionId;
2822
2823             try
2824             {
2825                 o = this.createXhrObject(tId);
2826                 if (o) {
2827                     this.transactionId++;
2828                 }
2829             }
2830             catch(e) {
2831             }
2832             finally
2833             {
2834                 return o;
2835             }
2836         },
2837
2838         asyncRequest:function(method, uri, callback, postData)
2839         {
2840             var o = this.getConnectionObject();
2841
2842             if (!o) {
2843                 return null;
2844             }
2845             else {
2846                 o.conn.open(method, uri, true);
2847
2848                 if (this.useDefaultXhrHeader) {
2849                     if (!this.defaultHeaders['X-Requested-With']) {
2850                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2851                     }
2852                 }
2853
2854                 if(postData && this.useDefaultHeader){
2855                     this.initHeader('Content-Type', this.defaultPostHeader);
2856                 }
2857
2858                  if (this.hasDefaultHeaders || this.hasHeaders) {
2859                     this.setHeader(o);
2860                 }
2861
2862                 this.handleReadyState(o, callback);
2863                 o.conn.send(postData || null);
2864
2865                 return o;
2866             }
2867         },
2868
2869         handleReadyState:function(o, callback)
2870         {
2871             var oConn = this;
2872
2873             if (callback && callback.timeout) {
2874                 
2875                 this.timeout[o.tId] = window.setTimeout(function() {
2876                     oConn.abort(o, callback, true);
2877                 }, callback.timeout);
2878             }
2879
2880             this.poll[o.tId] = window.setInterval(
2881                     function() {
2882                         if (o.conn && o.conn.readyState == 4) {
2883                             window.clearInterval(oConn.poll[o.tId]);
2884                             delete oConn.poll[o.tId];
2885
2886                             if(callback && callback.timeout) {
2887                                 window.clearTimeout(oConn.timeout[o.tId]);
2888                                 delete oConn.timeout[o.tId];
2889                             }
2890
2891                             oConn.handleTransactionResponse(o, callback);
2892                         }
2893                     }
2894                     , this.pollInterval);
2895         },
2896
2897         handleTransactionResponse:function(o, callback, isAbort)
2898         {
2899
2900             if (!callback) {
2901                 this.releaseObject(o);
2902                 return;
2903             }
2904
2905             var httpStatus, responseObject;
2906
2907             try
2908             {
2909                 if (o.conn.status !== undefined && o.conn.status != 0) {
2910                     httpStatus = o.conn.status;
2911                 }
2912                 else {
2913                     httpStatus = 13030;
2914                 }
2915             }
2916             catch(e) {
2917
2918
2919                 httpStatus = 13030;
2920             }
2921
2922             if (httpStatus >= 200 && httpStatus < 300) {
2923                 responseObject = this.createResponseObject(o, callback.argument);
2924                 if (callback.success) {
2925                     if (!callback.scope) {
2926                         callback.success(responseObject);
2927                     }
2928                     else {
2929
2930
2931                         callback.success.apply(callback.scope, [responseObject]);
2932                     }
2933                 }
2934             }
2935             else {
2936                 switch (httpStatus) {
2937
2938                     case 12002:
2939                     case 12029:
2940                     case 12030:
2941                     case 12031:
2942                     case 12152:
2943                     case 13030:
2944                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2945                         if (callback.failure) {
2946                             if (!callback.scope) {
2947                                 callback.failure(responseObject);
2948                             }
2949                             else {
2950                                 callback.failure.apply(callback.scope, [responseObject]);
2951                             }
2952                         }
2953                         break;
2954                     default:
2955                         responseObject = this.createResponseObject(o, callback.argument);
2956                         if (callback.failure) {
2957                             if (!callback.scope) {
2958                                 callback.failure(responseObject);
2959                             }
2960                             else {
2961                                 callback.failure.apply(callback.scope, [responseObject]);
2962                             }
2963                         }
2964                 }
2965             }
2966
2967             this.releaseObject(o);
2968             responseObject = null;
2969         },
2970
2971         createResponseObject:function(o, callbackArg)
2972         {
2973             var obj = {};
2974             var headerObj = {};
2975
2976             try
2977             {
2978                 var headerStr = o.conn.getAllResponseHeaders();
2979                 var header = headerStr.split('\n');
2980                 for (var i = 0; i < header.length; i++) {
2981                     var delimitPos = header[i].indexOf(':');
2982                     if (delimitPos != -1) {
2983                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2984                     }
2985                 }
2986             }
2987             catch(e) {
2988             }
2989
2990             obj.tId = o.tId;
2991             obj.status = o.conn.status;
2992             obj.statusText = o.conn.statusText;
2993             obj.getResponseHeader = headerObj;
2994             obj.getAllResponseHeaders = headerStr;
2995             obj.responseText = o.conn.responseText;
2996             obj.responseXML = o.conn.responseXML;
2997
2998             if (typeof callbackArg !== undefined) {
2999                 obj.argument = callbackArg;
3000             }
3001
3002             return obj;
3003         },
3004
3005         createExceptionObject:function(tId, callbackArg, isAbort)
3006         {
3007             var COMM_CODE = 0;
3008             var COMM_ERROR = 'communication failure';
3009             var ABORT_CODE = -1;
3010             var ABORT_ERROR = 'transaction aborted';
3011
3012             var obj = {};
3013
3014             obj.tId = tId;
3015             if (isAbort) {
3016                 obj.status = ABORT_CODE;
3017                 obj.statusText = ABORT_ERROR;
3018             }
3019             else {
3020                 obj.status = COMM_CODE;
3021                 obj.statusText = COMM_ERROR;
3022             }
3023
3024             if (callbackArg) {
3025                 obj.argument = callbackArg;
3026             }
3027
3028             return obj;
3029         },
3030
3031         initHeader:function(label, value, isDefault)
3032         {
3033             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3034
3035             if (headerObj[label] === undefined) {
3036                 headerObj[label] = value;
3037             }
3038             else {
3039
3040
3041                 headerObj[label] = value + "," + headerObj[label];
3042             }
3043
3044             if (isDefault) {
3045                 this.hasDefaultHeaders = true;
3046             }
3047             else {
3048                 this.hasHeaders = true;
3049             }
3050         },
3051
3052
3053         setHeader:function(o)
3054         {
3055             if (this.hasDefaultHeaders) {
3056                 for (var prop in this.defaultHeaders) {
3057                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3058                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3059                     }
3060                 }
3061             }
3062
3063             if (this.hasHeaders) {
3064                 for (var prop in this.headers) {
3065                     if (this.headers.hasOwnProperty(prop)) {
3066                         o.conn.setRequestHeader(prop, this.headers[prop]);
3067                     }
3068                 }
3069                 this.headers = {};
3070                 this.hasHeaders = false;
3071             }
3072         },
3073
3074         resetDefaultHeaders:function() {
3075             delete this.defaultHeaders;
3076             this.defaultHeaders = {};
3077             this.hasDefaultHeaders = false;
3078         },
3079
3080         abort:function(o, callback, isTimeout)
3081         {
3082             if(this.isCallInProgress(o)) {
3083                 o.conn.abort();
3084                 window.clearInterval(this.poll[o.tId]);
3085                 delete this.poll[o.tId];
3086                 if (isTimeout) {
3087                     delete this.timeout[o.tId];
3088                 }
3089
3090                 this.handleTransactionResponse(o, callback, true);
3091
3092                 return true;
3093             }
3094             else {
3095                 return false;
3096             }
3097         },
3098
3099
3100         isCallInProgress:function(o)
3101         {
3102             if (o && o.conn) {
3103                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3104             }
3105             else {
3106
3107                 return false;
3108             }
3109         },
3110
3111
3112         releaseObject:function(o)
3113         {
3114
3115             o.conn = null;
3116
3117             o = null;
3118         },
3119
3120         activeX:[
3121         'MSXML2.XMLHTTP.3.0',
3122         'MSXML2.XMLHTTP',
3123         'Microsoft.XMLHTTP'
3124         ]
3125
3126
3127     };
3128 })();/*
3129  * Portions of this file are based on pieces of Yahoo User Interface Library
3130  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3131  * YUI licensed under the BSD License:
3132  * http://developer.yahoo.net/yui/license.txt
3133  * <script type="text/javascript">
3134  *
3135  */
3136
3137 Roo.lib.Region = function(t, r, b, l) {
3138     this.top = t;
3139     this[1] = t;
3140     this.right = r;
3141     this.bottom = b;
3142     this.left = l;
3143     this[0] = l;
3144 };
3145
3146
3147 Roo.lib.Region.prototype = {
3148     contains : function(region) {
3149         return ( region.left >= this.left &&
3150                  region.right <= this.right &&
3151                  region.top >= this.top &&
3152                  region.bottom <= this.bottom    );
3153
3154     },
3155
3156     getArea : function() {
3157         return ( (this.bottom - this.top) * (this.right - this.left) );
3158     },
3159
3160     intersect : function(region) {
3161         var t = Math.max(this.top, region.top);
3162         var r = Math.min(this.right, region.right);
3163         var b = Math.min(this.bottom, region.bottom);
3164         var l = Math.max(this.left, region.left);
3165
3166         if (b >= t && r >= l) {
3167             return new Roo.lib.Region(t, r, b, l);
3168         } else {
3169             return null;
3170         }
3171     },
3172     union : function(region) {
3173         var t = Math.min(this.top, region.top);
3174         var r = Math.max(this.right, region.right);
3175         var b = Math.max(this.bottom, region.bottom);
3176         var l = Math.min(this.left, region.left);
3177
3178         return new Roo.lib.Region(t, r, b, l);
3179     },
3180
3181     adjust : function(t, l, b, r) {
3182         this.top += t;
3183         this.left += l;
3184         this.right += r;
3185         this.bottom += b;
3186         return this;
3187     }
3188 };
3189
3190 Roo.lib.Region.getRegion = function(el) {
3191     var p = Roo.lib.Dom.getXY(el);
3192
3193     var t = p[1];
3194     var r = p[0] + el.offsetWidth;
3195     var b = p[1] + el.offsetHeight;
3196     var l = p[0];
3197
3198     return new Roo.lib.Region(t, r, b, l);
3199 };
3200 /*
3201  * Portions of this file are based on pieces of Yahoo User Interface Library
3202  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3203  * YUI licensed under the BSD License:
3204  * http://developer.yahoo.net/yui/license.txt
3205  * <script type="text/javascript">
3206  *
3207  */
3208 //@@dep Roo.lib.Region
3209
3210
3211 Roo.lib.Point = function(x, y) {
3212     if (x instanceof Array) {
3213         y = x[1];
3214         x = x[0];
3215     }
3216     this.x = this.right = this.left = this[0] = x;
3217     this.y = this.top = this.bottom = this[1] = y;
3218 };
3219
3220 Roo.lib.Point.prototype = new Roo.lib.Region();
3221 /*
3222  * Portions of this file are based on pieces of Yahoo User Interface Library
3223  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3224  * YUI licensed under the BSD License:
3225  * http://developer.yahoo.net/yui/license.txt
3226  * <script type="text/javascript">
3227  *
3228  */
3229  
3230 (function() {   
3231
3232     Roo.lib.Anim = {
3233         scroll : function(el, args, duration, easing, cb, scope) {
3234             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3235         },
3236
3237         motion : function(el, args, duration, easing, cb, scope) {
3238             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3239         },
3240
3241         color : function(el, args, duration, easing, cb, scope) {
3242             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3243         },
3244
3245         run : function(el, args, duration, easing, cb, scope, type) {
3246             type = type || Roo.lib.AnimBase;
3247             if (typeof easing == "string") {
3248                 easing = Roo.lib.Easing[easing];
3249             }
3250             var anim = new type(el, args, duration, easing);
3251             anim.animateX(function() {
3252                 Roo.callback(cb, scope);
3253             });
3254             return anim;
3255         }
3256     };
3257 })();/*
3258  * Portions of this file are based on pieces of Yahoo User Interface Library
3259  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3260  * YUI licensed under the BSD License:
3261  * http://developer.yahoo.net/yui/license.txt
3262  * <script type="text/javascript">
3263  *
3264  */
3265
3266 (function() {    
3267     var libFlyweight;
3268     
3269     function fly(el) {
3270         if (!libFlyweight) {
3271             libFlyweight = new Roo.Element.Flyweight();
3272         }
3273         libFlyweight.dom = el;
3274         return libFlyweight;
3275     }
3276
3277     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3278     
3279    
3280     
3281     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3282         if (el) {
3283             this.init(el, attributes, duration, method);
3284         }
3285     };
3286
3287     Roo.lib.AnimBase.fly = fly;
3288     
3289     
3290     
3291     Roo.lib.AnimBase.prototype = {
3292
3293         toString: function() {
3294             var el = this.getEl();
3295             var id = el.id || el.tagName;
3296             return ("Anim " + id);
3297         },
3298
3299         patterns: {
3300             noNegatives:        /width|height|opacity|padding/i,
3301             offsetAttribute:  /^((width|height)|(top|left))$/,
3302             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3303             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3304         },
3305
3306
3307         doMethod: function(attr, start, end) {
3308             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3309         },
3310
3311
3312         setAttribute: function(attr, val, unit) {
3313             if (this.patterns.noNegatives.test(attr)) {
3314                 val = (val > 0) ? val : 0;
3315             }
3316
3317             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3318         },
3319
3320
3321         getAttribute: function(attr) {
3322             var el = this.getEl();
3323             var val = fly(el).getStyle(attr);
3324
3325             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3326                 return parseFloat(val);
3327             }
3328
3329             var a = this.patterns.offsetAttribute.exec(attr) || [];
3330             var pos = !!( a[3] );
3331             var box = !!( a[2] );
3332
3333
3334             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3335                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3336             } else {
3337                 val = 0;
3338             }
3339
3340             return val;
3341         },
3342
3343
3344         getDefaultUnit: function(attr) {
3345             if (this.patterns.defaultUnit.test(attr)) {
3346                 return 'px';
3347             }
3348
3349             return '';
3350         },
3351
3352         animateX : function(callback, scope) {
3353             var f = function() {
3354                 this.onComplete.removeListener(f);
3355                 if (typeof callback == "function") {
3356                     callback.call(scope || this, this);
3357                 }
3358             };
3359             this.onComplete.addListener(f, this);
3360             this.animate();
3361         },
3362
3363
3364         setRuntimeAttribute: function(attr) {
3365             var start;
3366             var end;
3367             var attributes = this.attributes;
3368
3369             this.runtimeAttributes[attr] = {};
3370
3371             var isset = function(prop) {
3372                 return (typeof prop !== 'undefined');
3373             };
3374
3375             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3376                 return false;
3377             }
3378
3379             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3380
3381
3382             if (isset(attributes[attr]['to'])) {
3383                 end = attributes[attr]['to'];
3384             } else if (isset(attributes[attr]['by'])) {
3385                 if (start.constructor == Array) {
3386                     end = [];
3387                     for (var i = 0, len = start.length; i < len; ++i) {
3388                         end[i] = start[i] + attributes[attr]['by'][i];
3389                     }
3390                 } else {
3391                     end = start + attributes[attr]['by'];
3392                 }
3393             }
3394
3395             this.runtimeAttributes[attr].start = start;
3396             this.runtimeAttributes[attr].end = end;
3397
3398
3399             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3400         },
3401
3402
3403         init: function(el, attributes, duration, method) {
3404
3405             var isAnimated = false;
3406
3407
3408             var startTime = null;
3409
3410
3411             var actualFrames = 0;
3412
3413
3414             el = Roo.getDom(el);
3415
3416
3417             this.attributes = attributes || {};
3418
3419
3420             this.duration = duration || 1;
3421
3422
3423             this.method = method || Roo.lib.Easing.easeNone;
3424
3425
3426             this.useSeconds = true;
3427
3428
3429             this.currentFrame = 0;
3430
3431
3432             this.totalFrames = Roo.lib.AnimMgr.fps;
3433
3434
3435             this.getEl = function() {
3436                 return el;
3437             };
3438
3439
3440             this.isAnimated = function() {
3441                 return isAnimated;
3442             };
3443
3444
3445             this.getStartTime = function() {
3446                 return startTime;
3447             };
3448
3449             this.runtimeAttributes = {};
3450
3451
3452             this.animate = function() {
3453                 if (this.isAnimated()) {
3454                     return false;
3455                 }
3456
3457                 this.currentFrame = 0;
3458
3459                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3460
3461                 Roo.lib.AnimMgr.registerElement(this);
3462             };
3463
3464
3465             this.stop = function(finish) {
3466                 if (finish) {
3467                     this.currentFrame = this.totalFrames;
3468                     this._onTween.fire();
3469                 }
3470                 Roo.lib.AnimMgr.stop(this);
3471             };
3472
3473             var onStart = function() {
3474                 this.onStart.fire();
3475
3476                 this.runtimeAttributes = {};
3477                 for (var attr in this.attributes) {
3478                     this.setRuntimeAttribute(attr);
3479                 }
3480
3481                 isAnimated = true;
3482                 actualFrames = 0;
3483                 startTime = new Date();
3484             };
3485
3486
3487             var onTween = function() {
3488                 var data = {
3489                     duration: new Date() - this.getStartTime(),
3490                     currentFrame: this.currentFrame
3491                 };
3492
3493                 data.toString = function() {
3494                     return (
3495                             'duration: ' + data.duration +
3496                             ', currentFrame: ' + data.currentFrame
3497                             );
3498                 };
3499
3500                 this.onTween.fire(data);
3501
3502                 var runtimeAttributes = this.runtimeAttributes;
3503
3504                 for (var attr in runtimeAttributes) {
3505                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3506                 }
3507
3508                 actualFrames += 1;
3509             };
3510
3511             var onComplete = function() {
3512                 var actual_duration = (new Date() - startTime) / 1000 ;
3513
3514                 var data = {
3515                     duration: actual_duration,
3516                     frames: actualFrames,
3517                     fps: actualFrames / actual_duration
3518                 };
3519
3520                 data.toString = function() {
3521                     return (
3522                             'duration: ' + data.duration +
3523                             ', frames: ' + data.frames +
3524                             ', fps: ' + data.fps
3525                             );
3526                 };
3527
3528                 isAnimated = false;
3529                 actualFrames = 0;
3530                 this.onComplete.fire(data);
3531             };
3532
3533
3534             this._onStart = new Roo.util.Event(this);
3535             this.onStart = new Roo.util.Event(this);
3536             this.onTween = new Roo.util.Event(this);
3537             this._onTween = new Roo.util.Event(this);
3538             this.onComplete = new Roo.util.Event(this);
3539             this._onComplete = new Roo.util.Event(this);
3540             this._onStart.addListener(onStart);
3541             this._onTween.addListener(onTween);
3542             this._onComplete.addListener(onComplete);
3543         }
3544     };
3545 })();
3546 /*
3547  * Portions of this file are based on pieces of Yahoo User Interface Library
3548  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3549  * YUI licensed under the BSD License:
3550  * http://developer.yahoo.net/yui/license.txt
3551  * <script type="text/javascript">
3552  *
3553  */
3554
3555 Roo.lib.AnimMgr = new function() {
3556
3557     var thread = null;
3558
3559
3560     var queue = [];
3561
3562
3563     var tweenCount = 0;
3564
3565
3566     this.fps = 1000;
3567
3568
3569     this.delay = 1;
3570
3571
3572     this.registerElement = function(tween) {
3573         queue[queue.length] = tween;
3574         tweenCount += 1;
3575         tween._onStart.fire();
3576         this.start();
3577     };
3578
3579
3580     this.unRegister = function(tween, index) {
3581         tween._onComplete.fire();
3582         index = index || getIndex(tween);
3583         if (index != -1) {
3584             queue.splice(index, 1);
3585         }
3586
3587         tweenCount -= 1;
3588         if (tweenCount <= 0) {
3589             this.stop();
3590         }
3591     };
3592
3593
3594     this.start = function() {
3595         if (thread === null) {
3596             thread = setInterval(this.run, this.delay);
3597         }
3598     };
3599
3600
3601     this.stop = function(tween) {
3602         if (!tween) {
3603             clearInterval(thread);
3604
3605             for (var i = 0, len = queue.length; i < len; ++i) {
3606                 if (queue[0].isAnimated()) {
3607                     this.unRegister(queue[0], 0);
3608                 }
3609             }
3610
3611             queue = [];
3612             thread = null;
3613             tweenCount = 0;
3614         }
3615         else {
3616             this.unRegister(tween);
3617         }
3618     };
3619
3620
3621     this.run = function() {
3622         for (var i = 0, len = queue.length; i < len; ++i) {
3623             var tween = queue[i];
3624             if (!tween || !tween.isAnimated()) {
3625                 continue;
3626             }
3627
3628             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3629             {
3630                 tween.currentFrame += 1;
3631
3632                 if (tween.useSeconds) {
3633                     correctFrame(tween);
3634                 }
3635                 tween._onTween.fire();
3636             }
3637             else {
3638                 Roo.lib.AnimMgr.stop(tween, i);
3639             }
3640         }
3641     };
3642
3643     var getIndex = function(anim) {
3644         for (var i = 0, len = queue.length; i < len; ++i) {
3645             if (queue[i] == anim) {
3646                 return i;
3647             }
3648         }
3649         return -1;
3650     };
3651
3652
3653     var correctFrame = function(tween) {
3654         var frames = tween.totalFrames;
3655         var frame = tween.currentFrame;
3656         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3657         var elapsed = (new Date() - tween.getStartTime());
3658         var tweak = 0;
3659
3660         if (elapsed < tween.duration * 1000) {
3661             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3662         } else {
3663             tweak = frames - (frame + 1);
3664         }
3665         if (tweak > 0 && isFinite(tweak)) {
3666             if (tween.currentFrame + tweak >= frames) {
3667                 tweak = frames - (frame + 1);
3668             }
3669
3670             tween.currentFrame += tweak;
3671         }
3672     };
3673 };
3674
3675     /*
3676  * Portions of this file are based on pieces of Yahoo User Interface Library
3677  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3678  * YUI licensed under the BSD License:
3679  * http://developer.yahoo.net/yui/license.txt
3680  * <script type="text/javascript">
3681  *
3682  */
3683 Roo.lib.Bezier = new function() {
3684
3685         this.getPosition = function(points, t) {
3686             var n = points.length;
3687             var tmp = [];
3688
3689             for (var i = 0; i < n; ++i) {
3690                 tmp[i] = [points[i][0], points[i][1]];
3691             }
3692
3693             for (var j = 1; j < n; ++j) {
3694                 for (i = 0; i < n - j; ++i) {
3695                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3696                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3697                 }
3698             }
3699
3700             return [ tmp[0][0], tmp[0][1] ];
3701
3702         };
3703     }; 
3704
3705 /**
3706  * @class Roo.lib.Color
3707  * @constructor
3708  * An abstract Color implementation. Concrete Color implementations should use
3709  * an instance of this function as their prototype, and implement the getRGB and
3710  * getHSL functions. getRGB should return an object representing the RGB
3711  * components of this Color, with the red, green, and blue components in the
3712  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3713  * return an object representing the HSL components of this Color, with the hue
3714  * component in the range [0,360), the saturation and lightness components in
3715  * the range [0,100], and the alpha component in the range [0,1].
3716  *
3717  *
3718  * Color.js
3719  *
3720  * Functions for Color handling and processing.
3721  *
3722  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3723  *
3724  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3725  * rights to this program, with the intention of it becoming part of the public
3726  * domain. Because this program is released into the public domain, it comes with
3727  * no warranty either expressed or implied, to the extent permitted by law.
3728  * 
3729  * For more free and public domain JavaScript code by the same author, visit:
3730  * http://www.safalra.com/web-design/javascript/
3731  * 
3732  */
3733 Roo.lib.Color = function() { }
3734
3735
3736 Roo.apply(Roo.lib.Color.prototype, {
3737   
3738   rgb : null,
3739   hsv : null,
3740   hsl : null,
3741   
3742   /**
3743    * getIntegerRGB
3744    * @return {Object} an object representing the RGBA components of this Color. The red,
3745    * green, and blue components are converted to integers in the range [0,255].
3746    * The alpha is a value in the range [0,1].
3747    */
3748   getIntegerRGB : function(){
3749
3750     // get the RGB components of this Color
3751     var rgb = this.getRGB();
3752
3753     // return the integer components
3754     return {
3755       'r' : Math.round(rgb.r),
3756       'g' : Math.round(rgb.g),
3757       'b' : Math.round(rgb.b),
3758       'a' : rgb.a
3759     };
3760
3761   },
3762
3763   /**
3764    * getPercentageRGB
3765    * @return {Object} an object representing the RGBA components of this Color. The red,
3766    * green, and blue components are converted to numbers in the range [0,100].
3767    * The alpha is a value in the range [0,1].
3768    */
3769   getPercentageRGB : function(){
3770
3771     // get the RGB components of this Color
3772     var rgb = this.getRGB();
3773
3774     // return the percentage components
3775     return {
3776       'r' : 100 * rgb.r / 255,
3777       'g' : 100 * rgb.g / 255,
3778       'b' : 100 * rgb.b / 255,
3779       'a' : rgb.a
3780     };
3781
3782   },
3783
3784   /**
3785    * getCSSHexadecimalRGB
3786    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3787    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3788    * are two-digit hexadecimal numbers.
3789    */
3790   getCSSHexadecimalRGB : function()
3791   {
3792
3793     // get the integer RGB components
3794     var rgb = this.getIntegerRGB();
3795
3796     // determine the hexadecimal equivalents
3797     var r16 = rgb.r.toString(16);
3798     var g16 = rgb.g.toString(16);
3799     var b16 = rgb.b.toString(16);
3800
3801     // return the CSS RGB Color value
3802     return '#'
3803         + (r16.length == 2 ? r16 : '0' + r16)
3804         + (g16.length == 2 ? g16 : '0' + g16)
3805         + (b16.length == 2 ? b16 : '0' + b16);
3806
3807   },
3808
3809   /**
3810    * getCSSIntegerRGB
3811    * @return {String} a string representing this Color as a CSS integer RGB Color
3812    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3813    * are integers in the range [0,255].
3814    */
3815   getCSSIntegerRGB : function(){
3816
3817     // get the integer RGB components
3818     var rgb = this.getIntegerRGB();
3819
3820     // return the CSS RGB Color value
3821     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3822
3823   },
3824
3825   /**
3826    * getCSSIntegerRGBA
3827    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3828    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3829    * b are integers in the range [0,255] and a is in the range [0,1].
3830    */
3831   getCSSIntegerRGBA : function(){
3832
3833     // get the integer RGB components
3834     var rgb = this.getIntegerRGB();
3835
3836     // return the CSS integer RGBA Color value
3837     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3838
3839   },
3840
3841   /**
3842    * getCSSPercentageRGB
3843    * @return {String} a string representing this Color as a CSS percentage RGB Color
3844    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3845    * b are in the range [0,100].
3846    */
3847   getCSSPercentageRGB : function(){
3848
3849     // get the percentage RGB components
3850     var rgb = this.getPercentageRGB();
3851
3852     // return the CSS RGB Color value
3853     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3854
3855   },
3856
3857   /**
3858    * getCSSPercentageRGBA
3859    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3860    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3861    * and b are in the range [0,100] and a is in the range [0,1].
3862    */
3863   getCSSPercentageRGBA : function(){
3864
3865     // get the percentage RGB components
3866     var rgb = this.getPercentageRGB();
3867
3868     // return the CSS percentage RGBA Color value
3869     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3870
3871   },
3872
3873   /**
3874    * getCSSHSL
3875    * @return {String} a string representing this Color as a CSS HSL Color value - that
3876    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3877    * s and l are in the range [0,100].
3878    */
3879   getCSSHSL : function(){
3880
3881     // get the HSL components
3882     var hsl = this.getHSL();
3883
3884     // return the CSS HSL Color value
3885     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3886
3887   },
3888
3889   /**
3890    * getCSSHSLA
3891    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3892    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3893    * s and l are in the range [0,100], and a is in the range [0,1].
3894    */
3895   getCSSHSLA : function(){
3896
3897     // get the HSL components
3898     var hsl = this.getHSL();
3899
3900     // return the CSS HSL Color value
3901     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3902
3903   },
3904
3905   /**
3906    * Sets the Color of the specified node to this Color. This functions sets
3907    * the CSS 'color' property for the node. The parameter is:
3908    * 
3909    * @param {DomElement} node - the node whose Color should be set
3910    */
3911   setNodeColor : function(node){
3912
3913     // set the Color of the node
3914     node.style.color = this.getCSSHexadecimalRGB();
3915
3916   },
3917
3918   /**
3919    * Sets the background Color of the specified node to this Color. This
3920    * functions sets the CSS 'background-color' property for the node. The
3921    * parameter is:
3922    *
3923    * @param {DomElement} node - the node whose background Color should be set
3924    */
3925   setNodeBackgroundColor : function(node){
3926
3927     // set the background Color of the node
3928     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3929
3930   },
3931   // convert between formats..
3932   toRGB: function()
3933   {
3934     var r = this.getIntegerRGB();
3935     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3936     
3937   },
3938   toHSL : function()
3939   {
3940      var hsl = this.getHSL();
3941   // return the CSS HSL Color value
3942     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3943     
3944   },
3945   
3946   toHSV : function()
3947   {
3948     var rgb = this.toRGB();
3949     var hsv = rgb.getHSV();
3950    // return the CSS HSL Color value
3951     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3952     
3953   },
3954   
3955   // modify  v = 0 ... 1 (eg. 0.5)
3956   saturate : function(v)
3957   {
3958       var rgb = this.toRGB();
3959       var hsv = rgb.getHSV();
3960       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3961       
3962   
3963   },
3964   
3965    
3966   /**
3967    * getRGB
3968    * @return {Object} the RGB and alpha components of this Color as an object with r,
3969    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3970    * the range [0,1].
3971    */
3972   getRGB: function(){
3973    
3974     // return the RGB components
3975     return {
3976       'r' : this.rgb.r,
3977       'g' : this.rgb.g,
3978       'b' : this.rgb.b,
3979       'a' : this.alpha
3980     };
3981
3982   },
3983
3984   /**
3985    * getHSV
3986    * @return {Object} the HSV and alpha components of this Color as an object with h,
3987    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3988    * [0,100], and a is in the range [0,1].
3989    */
3990   getHSV : function()
3991   {
3992     
3993     // calculate the HSV components if necessary
3994     if (this.hsv == null) {
3995       this.calculateHSV();
3996     }
3997
3998     // return the HSV components
3999     return {
4000       'h' : this.hsv.h,
4001       's' : this.hsv.s,
4002       'v' : this.hsv.v,
4003       'a' : this.alpha
4004     };
4005
4006   },
4007
4008   /**
4009    * getHSL
4010    * @return {Object} the HSL and alpha components of this Color as an object with h,
4011    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4012    * [0,100], and a is in the range [0,1].
4013    */
4014   getHSL : function(){
4015     
4016      
4017     // calculate the HSV components if necessary
4018     if (this.hsl == null) { this.calculateHSL(); }
4019
4020     // return the HSL components
4021     return {
4022       'h' : this.hsl.h,
4023       's' : this.hsl.s,
4024       'l' : this.hsl.l,
4025       'a' : this.alpha
4026     };
4027
4028   }
4029   
4030
4031 });
4032
4033
4034 /**
4035  * @class Roo.lib.RGBColor
4036  * @extends Roo.lib.Color
4037  * Creates a Color specified in the RGB Color space, with an optional alpha
4038  * component. The parameters are:
4039  * @constructor
4040  * 
4041
4042  * @param {Number} r - the red component, clipped to the range [0,255]
4043  * @param {Number} g - the green component, clipped to the range [0,255]
4044  * @param {Number} b - the blue component, clipped to the range [0,255]
4045  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4046  *     optional and defaults to 1
4047  */
4048 Roo.lib.RGBColor = function (r, g, b, a){
4049
4050   // store the alpha component after clipping it if necessary
4051   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4052
4053   // store the RGB components after clipping them if necessary
4054   this.rgb =
4055       {
4056         'r' : Math.max(0, Math.min(255, r)),
4057         'g' : Math.max(0, Math.min(255, g)),
4058         'b' : Math.max(0, Math.min(255, b))
4059       };
4060
4061   // initialise the HSV and HSL components to null
4062   
4063
4064   /* 
4065    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4066    * range [0,360). The parameters are:
4067    *
4068    * maximum - the maximum of the RGB component values
4069    * range   - the range of the RGB component values
4070    */
4071    
4072
4073 }
4074 // this does an 'exteds'
4075 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4076
4077   
4078     getHue  : function(maximum, range)
4079     {
4080       var rgb = this.rgb;
4081        
4082       // check whether the range is zero
4083       if (range == 0){
4084   
4085         // set the hue to zero (any hue is acceptable as the Color is grey)
4086         var hue = 0;
4087   
4088       }else{
4089   
4090         // determine which of the components has the highest value and set the hue
4091         switch (maximum){
4092   
4093           // red has the highest value
4094           case rgb.r:
4095             var hue = (rgb.g - rgb.b) / range * 60;
4096             if (hue < 0) { hue += 360; }
4097             break;
4098   
4099           // green has the highest value
4100           case rgb.g:
4101             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4102             break;
4103   
4104           // blue has the highest value
4105           case rgb.b:
4106             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4107             break;
4108   
4109         }
4110   
4111       }
4112   
4113       // return the hue
4114       return hue;
4115   
4116     },
4117
4118   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4119    * be returned be the getHSV function.
4120    */
4121    calculateHSV : function(){
4122     var rgb = this.rgb;
4123     // get the maximum and range of the RGB component values
4124     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4125     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4126
4127     // store the HSV components
4128     this.hsv =
4129         {
4130           'h' : this.getHue(maximum, range),
4131           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4132           'v' : maximum / 2.55
4133         };
4134
4135   },
4136
4137   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4138    * be returned be the getHSL function.
4139    */
4140    calculateHSL : function(){
4141     var rgb = this.rgb;
4142     // get the maximum and range of the RGB component values
4143     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4144     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4145
4146     // determine the lightness in the range [0,1]
4147     var l = maximum / 255 - range / 510;
4148
4149     // store the HSL components
4150     this.hsl =
4151         {
4152           'h' : this.getHue(maximum, range),
4153           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4154           'l' : 100 * l
4155         };
4156
4157   }
4158
4159 });
4160
4161 /**
4162  * @class Roo.lib.HSVColor
4163  * @extends Roo.lib.Color
4164  * Creates a Color specified in the HSV Color space, with an optional alpha
4165  * component. The parameters are:
4166  * @constructor
4167  *
4168  * @param {Number} h - the hue component, wrapped to the range [0,360)
4169  * @param {Number} s - the saturation component, clipped to the range [0,100]
4170  * @param {Number} v - the value component, clipped to the range [0,100]
4171  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4172  *     optional and defaults to 1
4173  */
4174 Roo.lib.HSVColor = function (h, s, v, a){
4175
4176   // store the alpha component after clipping it if necessary
4177   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4178
4179   // store the HSV components after clipping or wrapping them if necessary
4180   this.hsv =
4181       {
4182         'h' : (h % 360 + 360) % 360,
4183         's' : Math.max(0, Math.min(100, s)),
4184         'v' : Math.max(0, Math.min(100, v))
4185       };
4186
4187   // initialise the RGB and HSL components to null
4188   this.rgb = null;
4189   this.hsl = null;
4190 }
4191
4192 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4193   /* Calculates and stores the RGB components of this HSVColor so that they can
4194    * be returned be the getRGB function.
4195    */
4196   calculateRGB: function ()
4197   {
4198     var hsv = this.hsv;
4199     // check whether the saturation is zero
4200     if (hsv.s == 0){
4201
4202       // set the Color to the appropriate shade of grey
4203       var r = hsv.v;
4204       var g = hsv.v;
4205       var b = hsv.v;
4206
4207     }else{
4208
4209       // set some temporary values
4210       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4211       var p  = hsv.v * (1 - hsv.s / 100);
4212       var q  = hsv.v * (1 - hsv.s / 100 * f);
4213       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4214
4215       // set the RGB Color components to their temporary values
4216       switch (Math.floor(hsv.h / 60)){
4217         case 0: var r = hsv.v; var g = t; var b = p; break;
4218         case 1: var r = q; var g = hsv.v; var b = p; break;
4219         case 2: var r = p; var g = hsv.v; var b = t; break;
4220         case 3: var r = p; var g = q; var b = hsv.v; break;
4221         case 4: var r = t; var g = p; var b = hsv.v; break;
4222         case 5: var r = hsv.v; var g = p; var b = q; break;
4223       }
4224
4225     }
4226
4227     // store the RGB components
4228     this.rgb =
4229         {
4230           'r' : r * 2.55,
4231           'g' : g * 2.55,
4232           'b' : b * 2.55
4233         };
4234
4235   },
4236
4237   /* Calculates and stores the HSL components of this HSVColor so that they can
4238    * be returned be the getHSL function.
4239    */
4240   calculateHSL : function (){
4241
4242     var hsv = this.hsv;
4243     // determine the lightness in the range [0,100]
4244     var l = (2 - hsv.s / 100) * hsv.v / 2;
4245
4246     // store the HSL components
4247     this.hsl =
4248         {
4249           'h' : hsv.h,
4250           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4251           'l' : l
4252         };
4253
4254     // correct a division-by-zero error
4255     if (isNaN(hsl.s)) { hsl.s = 0; }
4256
4257   } 
4258  
4259
4260 });
4261  
4262
4263 /**
4264  * @class Roo.lib.HSLColor
4265  * @extends Roo.lib.Color
4266  *
4267  * @constructor
4268  * Creates a Color specified in the HSL Color space, with an optional alpha
4269  * component. The parameters are:
4270  *
4271  * @param {Number} h - the hue component, wrapped to the range [0,360)
4272  * @param {Number} s - the saturation component, clipped to the range [0,100]
4273  * @param {Number} l - the lightness component, clipped to the range [0,100]
4274  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4275  *     optional and defaults to 1
4276  */
4277
4278 Roo.lib.HSLColor = function(h, s, l, a){
4279
4280   // store the alpha component after clipping it if necessary
4281   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4282
4283   // store the HSL components after clipping or wrapping them if necessary
4284   this.hsl =
4285       {
4286         'h' : (h % 360 + 360) % 360,
4287         's' : Math.max(0, Math.min(100, s)),
4288         'l' : Math.max(0, Math.min(100, l))
4289       };
4290
4291   // initialise the RGB and HSV components to null
4292 }
4293
4294 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4295
4296   /* Calculates and stores the RGB components of this HSLColor so that they can
4297    * be returned be the getRGB function.
4298    */
4299   calculateRGB: function (){
4300
4301     // check whether the saturation is zero
4302     if (this.hsl.s == 0){
4303
4304       // store the RGB components representing the appropriate shade of grey
4305       this.rgb =
4306           {
4307             'r' : this.hsl.l * 2.55,
4308             'g' : this.hsl.l * 2.55,
4309             'b' : this.hsl.l * 2.55
4310           };
4311
4312     }else{
4313
4314       // set some temporary values
4315       var p = this.hsl.l < 50
4316             ? this.hsl.l * (1 + hsl.s / 100)
4317             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4318       var q = 2 * hsl.l - p;
4319
4320       // initialise the RGB components
4321       this.rgb =
4322           {
4323             'r' : (h + 120) / 60 % 6,
4324             'g' : h / 60,
4325             'b' : (h + 240) / 60 % 6
4326           };
4327
4328       // loop over the RGB components
4329       for (var key in this.rgb){
4330
4331         // ensure that the property is not inherited from the root object
4332         if (this.rgb.hasOwnProperty(key)){
4333
4334           // set the component to its value in the range [0,100]
4335           if (this.rgb[key] < 1){
4336             this.rgb[key] = q + (p - q) * this.rgb[key];
4337           }else if (this.rgb[key] < 3){
4338             this.rgb[key] = p;
4339           }else if (this.rgb[key] < 4){
4340             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4341           }else{
4342             this.rgb[key] = q;
4343           }
4344
4345           // set the component to its value in the range [0,255]
4346           this.rgb[key] *= 2.55;
4347
4348         }
4349
4350       }
4351
4352     }
4353
4354   },
4355
4356   /* Calculates and stores the HSV components of this HSLColor so that they can
4357    * be returned be the getHSL function.
4358    */
4359    calculateHSV : function(){
4360
4361     // set a temporary value
4362     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4363
4364     // store the HSV components
4365     this.hsv =
4366         {
4367           'h' : this.hsl.h,
4368           's' : 200 * t / (this.hsl.l + t),
4369           'v' : t + this.hsl.l
4370         };
4371
4372     // correct a division-by-zero error
4373     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4374
4375   }
4376  
4377
4378 });
4379 /*
4380  * Portions of this file are based on pieces of Yahoo User Interface Library
4381  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4382  * YUI licensed under the BSD License:
4383  * http://developer.yahoo.net/yui/license.txt
4384  * <script type="text/javascript">
4385  *
4386  */
4387 (function() {
4388
4389     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4390         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4391     };
4392
4393     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4394
4395     var fly = Roo.lib.AnimBase.fly;
4396     var Y = Roo.lib;
4397     var superclass = Y.ColorAnim.superclass;
4398     var proto = Y.ColorAnim.prototype;
4399
4400     proto.toString = function() {
4401         var el = this.getEl();
4402         var id = el.id || el.tagName;
4403         return ("ColorAnim " + id);
4404     };
4405
4406     proto.patterns.color = /color$/i;
4407     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4408     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4409     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4410     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4411
4412
4413     proto.parseColor = function(s) {
4414         if (s.length == 3) {
4415             return s;
4416         }
4417
4418         var c = this.patterns.hex.exec(s);
4419         if (c && c.length == 4) {
4420             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4421         }
4422
4423         c = this.patterns.rgb.exec(s);
4424         if (c && c.length == 4) {
4425             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4426         }
4427
4428         c = this.patterns.hex3.exec(s);
4429         if (c && c.length == 4) {
4430             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4431         }
4432
4433         return null;
4434     };
4435     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4436     proto.getAttribute = function(attr) {
4437         var el = this.getEl();
4438         if (this.patterns.color.test(attr)) {
4439             var val = fly(el).getStyle(attr);
4440
4441             if (this.patterns.transparent.test(val)) {
4442                 var parent = el.parentNode;
4443                 val = fly(parent).getStyle(attr);
4444
4445                 while (parent && this.patterns.transparent.test(val)) {
4446                     parent = parent.parentNode;
4447                     val = fly(parent).getStyle(attr);
4448                     if (parent.tagName.toUpperCase() == 'HTML') {
4449                         val = '#fff';
4450                     }
4451                 }
4452             }
4453         } else {
4454             val = superclass.getAttribute.call(this, attr);
4455         }
4456
4457         return val;
4458     };
4459     proto.getAttribute = function(attr) {
4460         var el = this.getEl();
4461         if (this.patterns.color.test(attr)) {
4462             var val = fly(el).getStyle(attr);
4463
4464             if (this.patterns.transparent.test(val)) {
4465                 var parent = el.parentNode;
4466                 val = fly(parent).getStyle(attr);
4467
4468                 while (parent && this.patterns.transparent.test(val)) {
4469                     parent = parent.parentNode;
4470                     val = fly(parent).getStyle(attr);
4471                     if (parent.tagName.toUpperCase() == 'HTML') {
4472                         val = '#fff';
4473                     }
4474                 }
4475             }
4476         } else {
4477             val = superclass.getAttribute.call(this, attr);
4478         }
4479
4480         return val;
4481     };
4482
4483     proto.doMethod = function(attr, start, end) {
4484         var val;
4485
4486         if (this.patterns.color.test(attr)) {
4487             val = [];
4488             for (var i = 0, len = start.length; i < len; ++i) {
4489                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4490             }
4491
4492             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4493         }
4494         else {
4495             val = superclass.doMethod.call(this, attr, start, end);
4496         }
4497
4498         return val;
4499     };
4500
4501     proto.setRuntimeAttribute = function(attr) {
4502         superclass.setRuntimeAttribute.call(this, attr);
4503
4504         if (this.patterns.color.test(attr)) {
4505             var attributes = this.attributes;
4506             var start = this.parseColor(this.runtimeAttributes[attr].start);
4507             var end = this.parseColor(this.runtimeAttributes[attr].end);
4508
4509             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4510                 end = this.parseColor(attributes[attr].by);
4511
4512                 for (var i = 0, len = start.length; i < len; ++i) {
4513                     end[i] = start[i] + end[i];
4514                 }
4515             }
4516
4517             this.runtimeAttributes[attr].start = start;
4518             this.runtimeAttributes[attr].end = end;
4519         }
4520     };
4521 })();
4522
4523 /*
4524  * Portions of this file are based on pieces of Yahoo User Interface Library
4525  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4526  * YUI licensed under the BSD License:
4527  * http://developer.yahoo.net/yui/license.txt
4528  * <script type="text/javascript">
4529  *
4530  */
4531 Roo.lib.Easing = {
4532
4533
4534     easeNone: function (t, b, c, d) {
4535         return c * t / d + b;
4536     },
4537
4538
4539     easeIn: function (t, b, c, d) {
4540         return c * (t /= d) * t + b;
4541     },
4542
4543
4544     easeOut: function (t, b, c, d) {
4545         return -c * (t /= d) * (t - 2) + b;
4546     },
4547
4548
4549     easeBoth: function (t, b, c, d) {
4550         if ((t /= d / 2) < 1) {
4551             return c / 2 * t * t + b;
4552         }
4553
4554         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4555     },
4556
4557
4558     easeInStrong: function (t, b, c, d) {
4559         return c * (t /= d) * t * t * t + b;
4560     },
4561
4562
4563     easeOutStrong: function (t, b, c, d) {
4564         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4565     },
4566
4567
4568     easeBothStrong: function (t, b, c, d) {
4569         if ((t /= d / 2) < 1) {
4570             return c / 2 * t * t * t * t + b;
4571         }
4572
4573         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4574     },
4575
4576
4577
4578     elasticIn: function (t, b, c, d, a, p) {
4579         if (t == 0) {
4580             return b;
4581         }
4582         if ((t /= d) == 1) {
4583             return b + c;
4584         }
4585         if (!p) {
4586             p = d * .3;
4587         }
4588
4589         if (!a || a < Math.abs(c)) {
4590             a = c;
4591             var s = p / 4;
4592         }
4593         else {
4594             var s = p / (2 * Math.PI) * Math.asin(c / a);
4595         }
4596
4597         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4598     },
4599
4600
4601     elasticOut: function (t, b, c, d, a, p) {
4602         if (t == 0) {
4603             return b;
4604         }
4605         if ((t /= d) == 1) {
4606             return b + c;
4607         }
4608         if (!p) {
4609             p = d * .3;
4610         }
4611
4612         if (!a || a < Math.abs(c)) {
4613             a = c;
4614             var s = p / 4;
4615         }
4616         else {
4617             var s = p / (2 * Math.PI) * Math.asin(c / a);
4618         }
4619
4620         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4621     },
4622
4623
4624     elasticBoth: function (t, b, c, d, a, p) {
4625         if (t == 0) {
4626             return b;
4627         }
4628
4629         if ((t /= d / 2) == 2) {
4630             return b + c;
4631         }
4632
4633         if (!p) {
4634             p = d * (.3 * 1.5);
4635         }
4636
4637         if (!a || a < Math.abs(c)) {
4638             a = c;
4639             var s = p / 4;
4640         }
4641         else {
4642             var s = p / (2 * Math.PI) * Math.asin(c / a);
4643         }
4644
4645         if (t < 1) {
4646             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4647                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4648         }
4649         return a * Math.pow(2, -10 * (t -= 1)) *
4650                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4651     },
4652
4653
4654
4655     backIn: function (t, b, c, d, s) {
4656         if (typeof s == 'undefined') {
4657             s = 1.70158;
4658         }
4659         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4660     },
4661
4662
4663     backOut: function (t, b, c, d, s) {
4664         if (typeof s == 'undefined') {
4665             s = 1.70158;
4666         }
4667         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4668     },
4669
4670
4671     backBoth: function (t, b, c, d, s) {
4672         if (typeof s == 'undefined') {
4673             s = 1.70158;
4674         }
4675
4676         if ((t /= d / 2 ) < 1) {
4677             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4678         }
4679         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4680     },
4681
4682
4683     bounceIn: function (t, b, c, d) {
4684         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4685     },
4686
4687
4688     bounceOut: function (t, b, c, d) {
4689         if ((t /= d) < (1 / 2.75)) {
4690             return c * (7.5625 * t * t) + b;
4691         } else if (t < (2 / 2.75)) {
4692             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4693         } else if (t < (2.5 / 2.75)) {
4694             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4695         }
4696         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4697     },
4698
4699
4700     bounceBoth: function (t, b, c, d) {
4701         if (t < d / 2) {
4702             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4703         }
4704         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4705     }
4706 };/*
4707  * Portions of this file are based on pieces of Yahoo User Interface Library
4708  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4709  * YUI licensed under the BSD License:
4710  * http://developer.yahoo.net/yui/license.txt
4711  * <script type="text/javascript">
4712  *
4713  */
4714     (function() {
4715         Roo.lib.Motion = function(el, attributes, duration, method) {
4716             if (el) {
4717                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4718             }
4719         };
4720
4721         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4722
4723
4724         var Y = Roo.lib;
4725         var superclass = Y.Motion.superclass;
4726         var proto = Y.Motion.prototype;
4727
4728         proto.toString = function() {
4729             var el = this.getEl();
4730             var id = el.id || el.tagName;
4731             return ("Motion " + id);
4732         };
4733
4734         proto.patterns.points = /^points$/i;
4735
4736         proto.setAttribute = function(attr, val, unit) {
4737             if (this.patterns.points.test(attr)) {
4738                 unit = unit || 'px';
4739                 superclass.setAttribute.call(this, 'left', val[0], unit);
4740                 superclass.setAttribute.call(this, 'top', val[1], unit);
4741             } else {
4742                 superclass.setAttribute.call(this, attr, val, unit);
4743             }
4744         };
4745
4746         proto.getAttribute = function(attr) {
4747             if (this.patterns.points.test(attr)) {
4748                 var val = [
4749                         superclass.getAttribute.call(this, 'left'),
4750                         superclass.getAttribute.call(this, 'top')
4751                         ];
4752             } else {
4753                 val = superclass.getAttribute.call(this, attr);
4754             }
4755
4756             return val;
4757         };
4758
4759         proto.doMethod = function(attr, start, end) {
4760             var val = null;
4761
4762             if (this.patterns.points.test(attr)) {
4763                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4764                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4765             } else {
4766                 val = superclass.doMethod.call(this, attr, start, end);
4767             }
4768             return val;
4769         };
4770
4771         proto.setRuntimeAttribute = function(attr) {
4772             if (this.patterns.points.test(attr)) {
4773                 var el = this.getEl();
4774                 var attributes = this.attributes;
4775                 var start;
4776                 var control = attributes['points']['control'] || [];
4777                 var end;
4778                 var i, len;
4779
4780                 if (control.length > 0 && !(control[0] instanceof Array)) {
4781                     control = [control];
4782                 } else {
4783                     var tmp = [];
4784                     for (i = 0,len = control.length; i < len; ++i) {
4785                         tmp[i] = control[i];
4786                     }
4787                     control = tmp;
4788                 }
4789
4790                 Roo.fly(el).position();
4791
4792                 if (isset(attributes['points']['from'])) {
4793                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4794                 }
4795                 else {
4796                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4797                 }
4798
4799                 start = this.getAttribute('points');
4800
4801
4802                 if (isset(attributes['points']['to'])) {
4803                     end = translateValues.call(this, attributes['points']['to'], start);
4804
4805                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4806                     for (i = 0,len = control.length; i < len; ++i) {
4807                         control[i] = translateValues.call(this, control[i], start);
4808                     }
4809
4810
4811                 } else if (isset(attributes['points']['by'])) {
4812                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4813
4814                     for (i = 0,len = control.length; i < len; ++i) {
4815                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4816                     }
4817                 }
4818
4819                 this.runtimeAttributes[attr] = [start];
4820
4821                 if (control.length > 0) {
4822                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4823                 }
4824
4825                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4826             }
4827             else {
4828                 superclass.setRuntimeAttribute.call(this, attr);
4829             }
4830         };
4831
4832         var translateValues = function(val, start) {
4833             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4834             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4835
4836             return val;
4837         };
4838
4839         var isset = function(prop) {
4840             return (typeof prop !== 'undefined');
4841         };
4842     })();
4843 /*
4844  * Portions of this file are based on pieces of Yahoo User Interface Library
4845  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4846  * YUI licensed under the BSD License:
4847  * http://developer.yahoo.net/yui/license.txt
4848  * <script type="text/javascript">
4849  *
4850  */
4851     (function() {
4852         Roo.lib.Scroll = function(el, attributes, duration, method) {
4853             if (el) {
4854                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4855             }
4856         };
4857
4858         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4859
4860
4861         var Y = Roo.lib;
4862         var superclass = Y.Scroll.superclass;
4863         var proto = Y.Scroll.prototype;
4864
4865         proto.toString = function() {
4866             var el = this.getEl();
4867             var id = el.id || el.tagName;
4868             return ("Scroll " + id);
4869         };
4870
4871         proto.doMethod = function(attr, start, end) {
4872             var val = null;
4873
4874             if (attr == 'scroll') {
4875                 val = [
4876                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4877                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4878                         ];
4879
4880             } else {
4881                 val = superclass.doMethod.call(this, attr, start, end);
4882             }
4883             return val;
4884         };
4885
4886         proto.getAttribute = function(attr) {
4887             var val = null;
4888             var el = this.getEl();
4889
4890             if (attr == 'scroll') {
4891                 val = [ el.scrollLeft, el.scrollTop ];
4892             } else {
4893                 val = superclass.getAttribute.call(this, attr);
4894             }
4895
4896             return val;
4897         };
4898
4899         proto.setAttribute = function(attr, val, unit) {
4900             var el = this.getEl();
4901
4902             if (attr == 'scroll') {
4903                 el.scrollLeft = val[0];
4904                 el.scrollTop = val[1];
4905             } else {
4906                 superclass.setAttribute.call(this, attr, val, unit);
4907             }
4908         };
4909     })();
4910 /**
4911  * Originally based of this code... - refactored for Roo...
4912  * https://github.com/aaalsaleh/undo-manager
4913  
4914  * undo-manager.js
4915  * @author  Abdulrahman Alsaleh 
4916  * @copyright 2015 Abdulrahman Alsaleh 
4917  * @license  MIT License (c) 
4918  *
4919  * Hackily modifyed by alan@roojs.com
4920  *
4921  *
4922  *  
4923  *
4924  *  TOTALLY UNTESTED...
4925  *
4926  *  Documentation to be done....
4927  */
4928  
4929
4930 /**
4931 * @class Roo.lib.UndoManager
4932 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4933 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4934
4935  * Usage:
4936  * <pre><code>
4937
4938
4939 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4940  
4941 </code></pre>
4942
4943 * For more information see this blog post with examples:
4944 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4945      - Create Elements using DOM, HTML fragments and Templates</a>. 
4946 * @constructor
4947 * @param {Number} limit how far back to go ... use 1000?
4948 * @param {Object} scope usually use document..
4949 */
4950
4951 Roo.lib.UndoManager = function (limit, undoScopeHost)
4952 {
4953     this.stack = [];
4954     this.limit = limit;
4955     this.scope = undoScopeHost;
4956     this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4957     if (this.fireEvent) {
4958         this.bindEvents();
4959     }
4960     this.reset();
4961     
4962 };
4963         
4964 Roo.lib.UndoManager.prototype = {
4965     
4966     limit : false,
4967     stack : false,
4968     scope :  false,
4969     fireEvent : false,
4970     position : 0,
4971     length : 0,
4972     
4973     
4974      /**
4975      * To push and execute a transaction, the method undoManager.transact
4976      * must be called by passing a transaction object as the first argument, and a merge
4977      * flag as the second argument. A transaction object has the following properties:
4978      *
4979      * Usage:
4980 <pre><code>
4981 undoManager.transact({
4982     label: 'Typing',
4983     execute: function() { ... },
4984     undo: function() { ... },
4985     // redo same as execute
4986     redo: function() { this.execute(); }
4987 }, false);
4988
4989 // merge transaction
4990 undoManager.transact({
4991     label: 'Typing',
4992     execute: function() { ... },  // this will be run...
4993     undo: function() { ... }, // what to do when undo is run.
4994     // redo same as execute
4995     redo: function() { this.execute(); }
4996 }, true); 
4997 </code></pre> 
4998      *
4999      * 
5000      * @param {Object} transaction The transaction to add to the stack.
5001      * @return {String} The HTML fragment
5002      */
5003     
5004     
5005     transact : function (transaction, merge)
5006     {
5007         if (arguments.length < 2) {
5008             throw new TypeError('Not enough arguments to UndoManager.transact.');
5009         }
5010
5011         transaction.execute();
5012
5013         this.stack.splice(0, this.position);
5014         if (merge && this.length) {
5015             this.stack[0].push(transaction);
5016         } else {
5017             this.stack.unshift([transaction]);
5018         }
5019     
5020         this.position = 0;
5021
5022         if (this.limit && this.stack.length > this.limit) {
5023             this.length = this.stack.length = this.limit;
5024         } else {
5025             this.length = this.stack.length;
5026         }
5027
5028         if (this.fireEvent) {
5029             this.scope.dispatchEvent(
5030                 new CustomEvent('DOMTransaction', {
5031                     detail: {
5032                         transactions: this.stack[0].slice()
5033                     },
5034                     bubbles: true,
5035                     cancelable: false
5036                 })
5037             );
5038         }
5039         
5040         //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5041       
5042         
5043     },
5044
5045     undo : function ()
5046     {
5047         //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5048         
5049         if (this.position < this.length) {
5050             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5051                 this.stack[this.position][i].undo();
5052             }
5053             this.position++;
5054
5055             if (this.fireEvent) {
5056                 this.scope.dispatchEvent(
5057                     new CustomEvent('undo', {
5058                         detail: {
5059                             transactions: this.stack[this.position - 1].slice()
5060                         },
5061                         bubbles: true,
5062                         cancelable: false
5063                     })
5064                 );
5065             }
5066         }
5067     },
5068
5069     redo : function ()
5070     {
5071         if (this.position > 0) {
5072             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5073                 this.stack[this.position - 1][i].redo();
5074             }
5075             this.position--;
5076
5077             if (this.fireEvent) {
5078                 this.scope.dispatchEvent(
5079                     new CustomEvent('redo', {
5080                         detail: {
5081                             transactions: this.stack[this.position].slice()
5082                         },
5083                         bubbles: true,
5084                         cancelable: false
5085                     })
5086                 );
5087             }
5088         }
5089     },
5090
5091     item : function (index)
5092     {
5093         if (index >= 0 && index < this.length) {
5094             return this.stack[index].slice();
5095         }
5096         return null;
5097     },
5098
5099     clearUndo : function () {
5100         this.stack.length = this.length = this.position;
5101     },
5102
5103     clearRedo : function () {
5104         this.stack.splice(0, this.position);
5105         this.position = 0;
5106         this.length = this.stack.length;
5107     },
5108     /**
5109      * Reset the undo - probaly done on load to clear all history.
5110      */
5111     reset : function()
5112     {
5113         this.stack = [];
5114         this.position = 0;
5115         this.length = 0;
5116         this.current_html = this.scope.innerHTML;
5117         if (this.timer !== false) {
5118             clearTimeout(this.timer);
5119         }
5120         this.timer = false;
5121         this.merge = false;
5122         this.addEvent();
5123         
5124     },
5125     current_html : '',
5126     timer : false,
5127     merge : false,
5128     
5129     
5130     // this will handle the undo/redo on the element.?
5131     bindEvents : function()
5132     {
5133         var el  = this.scope;
5134         el.undoManager = this;
5135         
5136         
5137         this.scope.addEventListener('keydown', function(e) {
5138             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5139                 if (e.shiftKey) {
5140                     el.undoManager.redo(); // Ctrl/Command + Shift + Z
5141                 } else {
5142                     el.undoManager.undo(); // Ctrl/Command + Z
5143                 }
5144         
5145                 e.preventDefault();
5146             }
5147         });
5148         /// ignore keyup..
5149         this.scope.addEventListener('keyup', function(e) {
5150             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5151                 e.preventDefault();
5152             }
5153         });
5154         
5155         
5156         
5157         var t = this;
5158         
5159         el.addEventListener('input', function(e) {
5160             if(el.innerHTML == t.current_html) {
5161                 return;
5162             }
5163             // only record events every second.
5164             if (t.timer !== false) {
5165                clearTimeout(t.timer);
5166                t.timer = false;
5167             }
5168             t.timer = setTimeout(function() { t.merge = false; }, 1000);
5169             
5170             t.addEvent(t.merge);
5171             t.merge = true; // ignore changes happening every second..
5172         });
5173         },
5174     /**
5175      * Manually add an event.
5176      * Normall called without arguements - and it will just get added to the stack.
5177      * 
5178      */
5179     
5180     addEvent : function(merge)
5181     {
5182         //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5183         // not sure if this should clear the timer 
5184         merge = typeof(merge) == 'undefined' ? false : merge; 
5185         
5186         this.scope.undoManager.transact({
5187             scope : this.scope,
5188             oldHTML: this.current_html,
5189             newHTML: this.scope.innerHTML,
5190             // nothing to execute (content already changed when input is fired)
5191             execute: function() { },
5192             undo: function() {
5193                 this.scope.innerHTML = this.current_html = this.oldHTML;
5194             },
5195             redo: function() {
5196                 this.scope.innerHTML = this.current_html = this.newHTML;
5197             }
5198         }, false); //merge);
5199         
5200         this.merge = merge;
5201         
5202         this.current_html = this.scope.innerHTML;
5203     }
5204     
5205     
5206      
5207     
5208     
5209     
5210 };
5211 /**
5212  * @class Roo.lib.Range
5213  * @constructor
5214  * This is a toolkit, normally used to copy features into a Dom Range element
5215  * Roo.lib.Range.wrap(x);
5216  *
5217  *
5218  *
5219  */
5220 Roo.lib.Range = function() { };
5221
5222 /**
5223  * Wrap a Dom Range object, to give it new features...
5224  * @static
5225  * @param {Range} the range to wrap
5226  */
5227 Roo.lib.Range.wrap = function(r) {
5228     return Roo.apply(r, Roo.lib.Range.prototype);
5229 };
5230 /**
5231  * find a parent node eg. LI / OL
5232  * @param {string|Array} node name or array of nodenames
5233  * @return {DomElement|false}
5234  */
5235 Roo.apply(Roo.lib.Range.prototype,
5236 {
5237     
5238     closest : function(str)
5239     {
5240         if (typeof(str) != 'string') {
5241             // assume it's a array.
5242             for(var i = 0;i < str.length;i++) {
5243                 var r = this.closest(str[i]);
5244                 if (r !== false) {
5245                     return r;
5246                 }
5247                 
5248             }
5249             return false;
5250         }
5251         str = str.toLowerCase();
5252         var n = this.commonAncestorContainer; // might not be a node
5253         while (n.nodeType != 1) {
5254             n = n.parentNode;
5255         }
5256         
5257         if (n.nodeName.toLowerCase() == str ) {
5258             return n;
5259         }
5260         if (n.nodeName.toLowerCase() == 'body') {
5261             return false;
5262         }
5263             
5264         return n.closest(str) || false;
5265         
5266     },
5267     cloneRange : function()
5268     {
5269         return Roo.lib.Range.wrap(Range.prototype.cloneRange.call(this));
5270     }
5271 });/**
5272  * @class Roo.lib.Selection
5273  * @constructor
5274  * This is a toolkit, normally used to copy features into a Dom Selection element
5275  * Roo.lib.Selection.wrap(x);
5276  *
5277  *
5278  *
5279  */
5280 Roo.lib.Selection = function() { };
5281
5282 /**
5283  * Wrap a Dom Range object, to give it new features...
5284  * @static
5285  * @param {Range} the range to wrap
5286  */
5287 Roo.lib.Selection.wrap = function(r, doc) {
5288     Roo.apply(r, Roo.lib.Selection.prototype);
5289     r.ownerDocument = doc; // usefull so we dont have to keep referening to it.
5290     return r;
5291 };
5292 /**
5293  * find a parent node eg. LI / OL
5294  * @param {string|Array} node name or array of nodenames
5295  * @return {DomElement|false}
5296  */
5297 Roo.apply(Roo.lib.Selection.prototype,
5298 {
5299     /**
5300      * the owner document
5301      */
5302     ownerDocument : false,
5303     
5304     getRangeAt : function(n)
5305     {
5306         return Roo.lib.Range.wrap(Selection.prototype.getRangeAt.call(this,n));
5307     },
5308     
5309     /**
5310      * insert node at selection 
5311      * @param {DomElement|string} node
5312      * @param {string} cursor (after|in|none) where to place the cursor after inserting.
5313      */
5314     insertNode: function(node, cursor)
5315     {
5316         if (typeof(node) == 'string') {
5317             node = this.ownerDocument.createElement(node);
5318             if (cursor == 'in') {
5319                 node.innerHTML = '&nbsp;';
5320             }
5321         }
5322         
5323         var range = this.getRangeAt(0);
5324         
5325         if (this.type != 'Caret') {
5326             range.deleteContents();
5327         }
5328         var sn = node.childNodes[0]; // select the contents.
5329
5330         
5331         
5332         range.insertNode(node);
5333         if (cursor == 'after') {
5334             node.insertAdjacentHTML('afterend', '&nbsp;');
5335             sn = node.nextSibling;
5336         }
5337         
5338         if (cursor == 'none') {
5339             return;
5340         }
5341         
5342         this.cursorText(sn);
5343     },
5344     
5345     cursorText : function(n)
5346     {
5347        
5348         //var range = this.getRangeAt(0);
5349         range = Roo.lib.Range.wrap(new Range());
5350         //range.selectNode(n);
5351         
5352         var ix = Array.from(n.parentNode.childNodes).indexOf(n);
5353         range.setStart(n.parentNode,ix);
5354         range.setEnd(n.parentNode,ix+1);
5355         //range.collapse(false);
5356          
5357         this.removeAllRanges();
5358         this.addRange(range);
5359         
5360         Roo.log([n, range, this,this.baseOffset,this.extentOffset, this.type]);
5361     },
5362     cursorAfter : function(n)
5363     {
5364         if (!n.nextSibling || n.nextSibling.nodeValue != '&nbsp;') {
5365             n.insertAdjacentHTML('afterend', '&nbsp;');
5366         }
5367         this.cursorText (n.nextSibling);
5368     }
5369         
5370     
5371 });/*
5372  * Based on:
5373  * Ext JS Library 1.1.1
5374  * Copyright(c) 2006-2007, Ext JS, LLC.
5375  *
5376  * Originally Released Under LGPL - original licence link has changed is not relivant.
5377  *
5378  * Fork - LGPL
5379  * <script type="text/javascript">
5380  */
5381
5382
5383 // nasty IE9 hack - what a pile of crap that is..
5384
5385  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5386     Range.prototype.createContextualFragment = function (html) {
5387         var doc = window.document;
5388         var container = doc.createElement("div");
5389         container.innerHTML = html;
5390         var frag = doc.createDocumentFragment(), n;
5391         while ((n = container.firstChild)) {
5392             frag.appendChild(n);
5393         }
5394         return frag;
5395     };
5396 }
5397
5398 /**
5399  * @class Roo.DomHelper
5400  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5401  * 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>.
5402  * @static
5403  */
5404 Roo.DomHelper = function(){
5405     var tempTableEl = null;
5406     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5407     var tableRe = /^table|tbody|tr|td$/i;
5408     var xmlns = {};
5409     // build as innerHTML where available
5410     /** @ignore */
5411     var createHtml = function(o){
5412         if(typeof o == 'string'){
5413             return o;
5414         }
5415         var b = "";
5416         if(!o.tag){
5417             o.tag = "div";
5418         }
5419         b += "<" + o.tag;
5420         for(var attr in o){
5421             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5422             if(attr == "style"){
5423                 var s = o["style"];
5424                 if(typeof s == "function"){
5425                     s = s.call();
5426                 }
5427                 if(typeof s == "string"){
5428                     b += ' style="' + s + '"';
5429                 }else if(typeof s == "object"){
5430                     b += ' style="';
5431                     for(var key in s){
5432                         if(typeof s[key] != "function"){
5433                             b += key + ":" + s[key] + ";";
5434                         }
5435                     }
5436                     b += '"';
5437                 }
5438             }else{
5439                 if(attr == "cls"){
5440                     b += ' class="' + o["cls"] + '"';
5441                 }else if(attr == "htmlFor"){
5442                     b += ' for="' + o["htmlFor"] + '"';
5443                 }else{
5444                     b += " " + attr + '="' + o[attr] + '"';
5445                 }
5446             }
5447         }
5448         if(emptyTags.test(o.tag)){
5449             b += "/>";
5450         }else{
5451             b += ">";
5452             var cn = o.children || o.cn;
5453             if(cn){
5454                 //http://bugs.kde.org/show_bug.cgi?id=71506
5455                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5456                     for(var i = 0, len = cn.length; i < len; i++) {
5457                         b += createHtml(cn[i], b);
5458                     }
5459                 }else{
5460                     b += createHtml(cn, b);
5461                 }
5462             }
5463             if(o.html){
5464                 b += o.html;
5465             }
5466             b += "</" + o.tag + ">";
5467         }
5468         return b;
5469     };
5470
5471     // build as dom
5472     /** @ignore */
5473     var createDom = function(o, parentNode){
5474          
5475         // defininition craeted..
5476         var ns = false;
5477         if (o.ns && o.ns != 'html') {
5478                
5479             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5480                 xmlns[o.ns] = o.xmlns;
5481                 ns = o.xmlns;
5482             }
5483             if (typeof(xmlns[o.ns]) == 'undefined') {
5484                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5485             }
5486             ns = xmlns[o.ns];
5487         }
5488         
5489         
5490         if (typeof(o) == 'string') {
5491             return parentNode.appendChild(document.createTextNode(o));
5492         }
5493         o.tag = o.tag || div;
5494         if (o.ns && Roo.isIE) {
5495             ns = false;
5496             o.tag = o.ns + ':' + o.tag;
5497             
5498         }
5499         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5500         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5501         for(var attr in o){
5502             
5503             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5504                     attr == "style" || typeof o[attr] == "function") { continue; }
5505                     
5506             if(attr=="cls" && Roo.isIE){
5507                 el.className = o["cls"];
5508             }else{
5509                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5510                 else { 
5511                     el[attr] = o[attr];
5512                 }
5513             }
5514         }
5515         Roo.DomHelper.applyStyles(el, o.style);
5516         var cn = o.children || o.cn;
5517         if(cn){
5518             //http://bugs.kde.org/show_bug.cgi?id=71506
5519              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5520                 for(var i = 0, len = cn.length; i < len; i++) {
5521                     createDom(cn[i], el);
5522                 }
5523             }else{
5524                 createDom(cn, el);
5525             }
5526         }
5527         if(o.html){
5528             el.innerHTML = o.html;
5529         }
5530         if(parentNode){
5531            parentNode.appendChild(el);
5532         }
5533         return el;
5534     };
5535
5536     var ieTable = function(depth, s, h, e){
5537         tempTableEl.innerHTML = [s, h, e].join('');
5538         var i = -1, el = tempTableEl;
5539         while(++i < depth && el.firstChild){
5540             el = el.firstChild;
5541         }
5542         return el;
5543     };
5544
5545     // kill repeat to save bytes
5546     var ts = '<table>',
5547         te = '</table>',
5548         tbs = ts+'<tbody>',
5549         tbe = '</tbody>'+te,
5550         trs = tbs + '<tr>',
5551         tre = '</tr>'+tbe;
5552
5553     /**
5554      * @ignore
5555      * Nasty code for IE's broken table implementation
5556      */
5557     var insertIntoTable = function(tag, where, el, html){
5558         if(!tempTableEl){
5559             tempTableEl = document.createElement('div');
5560         }
5561         var node;
5562         var before = null;
5563         if(tag == 'td'){
5564             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5565                 return;
5566             }
5567             if(where == 'beforebegin'){
5568                 before = el;
5569                 el = el.parentNode;
5570             } else{
5571                 before = el.nextSibling;
5572                 el = el.parentNode;
5573             }
5574             node = ieTable(4, trs, html, tre);
5575         }
5576         else if(tag == 'tr'){
5577             if(where == 'beforebegin'){
5578                 before = el;
5579                 el = el.parentNode;
5580                 node = ieTable(3, tbs, html, tbe);
5581             } else if(where == 'afterend'){
5582                 before = el.nextSibling;
5583                 el = el.parentNode;
5584                 node = ieTable(3, tbs, html, tbe);
5585             } else{ // INTO a TR
5586                 if(where == 'afterbegin'){
5587                     before = el.firstChild;
5588                 }
5589                 node = ieTable(4, trs, html, tre);
5590             }
5591         } else if(tag == 'tbody'){
5592             if(where == 'beforebegin'){
5593                 before = el;
5594                 el = el.parentNode;
5595                 node = ieTable(2, ts, html, te);
5596             } else if(where == 'afterend'){
5597                 before = el.nextSibling;
5598                 el = el.parentNode;
5599                 node = ieTable(2, ts, html, te);
5600             } else{
5601                 if(where == 'afterbegin'){
5602                     before = el.firstChild;
5603                 }
5604                 node = ieTable(3, tbs, html, tbe);
5605             }
5606         } else{ // TABLE
5607             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5608                 return;
5609             }
5610             if(where == 'afterbegin'){
5611                 before = el.firstChild;
5612             }
5613             node = ieTable(2, ts, html, te);
5614         }
5615         el.insertBefore(node, before);
5616         return node;
5617     };
5618     
5619     // this is a bit like the react update code...
5620     // 
5621     
5622     var updateNode = function(from, to)
5623     {
5624         // should we handle non-standard elements?
5625         Roo.log(["UpdateNode" , from, to]);
5626         if (from.nodeType != to.nodeType) {
5627             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5628             from.parentNode.replaceChild(to, from);
5629         }
5630         
5631         if (from.nodeType == 3) {
5632             // assume it's text?!
5633             if (from.data == to.data) {
5634                 return;
5635             }
5636             from.data = to.data;
5637             return;
5638         }
5639         if (!from.parentNode) {
5640             // not sure why this is happening?
5641             return;
5642         }
5643         // assume 'to' doesnt have '1/3 nodetypes!
5644         // not sure why, by from, parent node might not exist?
5645         if (from.nodeType !=1 || from.tagName != to.tagName) {
5646             Roo.log(["ReplaceChild" , from, to ]);
5647             
5648             from.parentNode.replaceChild(to, from);
5649             return;
5650         }
5651         // compare attributes
5652         var ar = Array.from(from.attributes);
5653         for(var i = 0; i< ar.length;i++) {
5654             if (to.hasAttribute(ar[i].name)) {
5655                 continue;
5656             }
5657             if (ar[i].name == 'id') { // always keep ids?
5658                continue;
5659             }
5660             //if (ar[i].name == 'style') {
5661             //   throw "style removed?";
5662             //}
5663             Roo.log("removeAttribute" + ar[i].name);
5664             from.removeAttribute(ar[i].name);
5665         }
5666         ar = to.attributes;
5667         for(var i = 0; i< ar.length;i++) {
5668             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5669                 Roo.log("skipAttribute " + ar[i].name  + '=' + to.getAttribute(ar[i].name));
5670                 continue;
5671             }
5672             Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5673             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5674         }
5675         // children
5676         var far = Array.from(from.childNodes);
5677         var tar = Array.from(to.childNodes);
5678         // if the lengths are different.. then it's probably a editable content change, rather than
5679         // a change of the block definition..
5680         
5681         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5682          /*if (from.innerHTML == to.innerHTML) {
5683             return;
5684         }
5685         if (far.length != tar.length) {
5686             from.innerHTML = to.innerHTML;
5687             return;
5688         }
5689         */
5690         
5691         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5692             if (i >= far.length) {
5693                 from.appendChild(tar[i]);
5694                 Roo.log(["add", tar[i]]);
5695                 
5696             } else if ( i  >= tar.length) {
5697                 from.removeChild(far[i]);
5698                 Roo.log(["remove", far[i]]);
5699             } else {
5700                 
5701                 updateNode(far[i], tar[i]);
5702             }    
5703         }
5704         
5705         
5706         
5707         
5708     };
5709     
5710     
5711
5712     return {
5713         /** True to force the use of DOM instead of html fragments @type Boolean */
5714         useDom : false,
5715     
5716         /**
5717          * Returns the markup for the passed Element(s) config
5718          * @param {Object} o The Dom object spec (and children)
5719          * @return {String}
5720          */
5721         markup : function(o){
5722             return createHtml(o);
5723         },
5724     
5725         /**
5726          * Applies a style specification to an element
5727          * @param {String/HTMLElement} el The element to apply styles to
5728          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5729          * a function which returns such a specification.
5730          */
5731         applyStyles : function(el, styles){
5732             if(styles){
5733                el = Roo.fly(el);
5734                if(typeof styles == "string"){
5735                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5736                    var matches;
5737                    while ((matches = re.exec(styles)) != null){
5738                        el.setStyle(matches[1], matches[2]);
5739                    }
5740                }else if (typeof styles == "object"){
5741                    for (var style in styles){
5742                       el.setStyle(style, styles[style]);
5743                    }
5744                }else if (typeof styles == "function"){
5745                     Roo.DomHelper.applyStyles(el, styles.call());
5746                }
5747             }
5748         },
5749     
5750         /**
5751          * Inserts an HTML fragment into the Dom
5752          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5753          * @param {HTMLElement} el The context element
5754          * @param {String} html The HTML fragmenet
5755          * @return {HTMLElement} The new node
5756          */
5757         insertHtml : function(where, el, html){
5758             where = where.toLowerCase();
5759             if(el.insertAdjacentHTML){
5760                 if(tableRe.test(el.tagName)){
5761                     var rs;
5762                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5763                         return rs;
5764                     }
5765                 }
5766                 switch(where){
5767                     case "beforebegin":
5768                         el.insertAdjacentHTML('BeforeBegin', html);
5769                         return el.previousSibling;
5770                     case "afterbegin":
5771                         el.insertAdjacentHTML('AfterBegin', html);
5772                         return el.firstChild;
5773                     case "beforeend":
5774                         el.insertAdjacentHTML('BeforeEnd', html);
5775                         return el.lastChild;
5776                     case "afterend":
5777                         el.insertAdjacentHTML('AfterEnd', html);
5778                         return el.nextSibling;
5779                 }
5780                 throw 'Illegal insertion point -> "' + where + '"';
5781             }
5782             var range = el.ownerDocument.createRange();
5783             var frag;
5784             switch(where){
5785                  case "beforebegin":
5786                     range.setStartBefore(el);
5787                     frag = range.createContextualFragment(html);
5788                     el.parentNode.insertBefore(frag, el);
5789                     return el.previousSibling;
5790                  case "afterbegin":
5791                     if(el.firstChild){
5792                         range.setStartBefore(el.firstChild);
5793                         frag = range.createContextualFragment(html);
5794                         el.insertBefore(frag, el.firstChild);
5795                         return el.firstChild;
5796                     }else{
5797                         el.innerHTML = html;
5798                         return el.firstChild;
5799                     }
5800                 case "beforeend":
5801                     if(el.lastChild){
5802                         range.setStartAfter(el.lastChild);
5803                         frag = range.createContextualFragment(html);
5804                         el.appendChild(frag);
5805                         return el.lastChild;
5806                     }else{
5807                         el.innerHTML = html;
5808                         return el.lastChild;
5809                     }
5810                 case "afterend":
5811                     range.setStartAfter(el);
5812                     frag = range.createContextualFragment(html);
5813                     el.parentNode.insertBefore(frag, el.nextSibling);
5814                     return el.nextSibling;
5815                 }
5816                 throw 'Illegal insertion point -> "' + where + '"';
5817         },
5818     
5819         /**
5820          * Creates new Dom element(s) and inserts them before el
5821          * @param {String/HTMLElement/Element} el The context element
5822          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5823          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5824          * @return {HTMLElement/Roo.Element} The new node
5825          */
5826         insertBefore : function(el, o, returnElement){
5827             return this.doInsert(el, o, returnElement, "beforeBegin");
5828         },
5829     
5830         /**
5831          * Creates new Dom element(s) and inserts them after el
5832          * @param {String/HTMLElement/Element} el The context element
5833          * @param {Object} o The Dom object spec (and children)
5834          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5835          * @return {HTMLElement/Roo.Element} The new node
5836          */
5837         insertAfter : function(el, o, returnElement){
5838             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5839         },
5840     
5841         /**
5842          * Creates new Dom element(s) and inserts them as the first child of el
5843          * @param {String/HTMLElement/Element} el The context element
5844          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5845          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5846          * @return {HTMLElement/Roo.Element} The new node
5847          */
5848         insertFirst : function(el, o, returnElement){
5849             return this.doInsert(el, o, returnElement, "afterBegin");
5850         },
5851     
5852         // private
5853         doInsert : function(el, o, returnElement, pos, sibling){
5854             el = Roo.getDom(el);
5855             var newNode;
5856             if(this.useDom || o.ns){
5857                 newNode = createDom(o, null);
5858                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5859             }else{
5860                 var html = createHtml(o);
5861                 newNode = this.insertHtml(pos, el, html);
5862             }
5863             return returnElement ? Roo.get(newNode, true) : newNode;
5864         },
5865     
5866         /**
5867          * Creates new Dom element(s) and appends them to el
5868          * @param {String/HTMLElement/Element} el The context element
5869          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5870          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5871          * @return {HTMLElement/Roo.Element} The new node
5872          */
5873         append : function(el, o, returnElement){
5874             el = Roo.getDom(el);
5875             var newNode;
5876             if(this.useDom || o.ns){
5877                 newNode = createDom(o, null);
5878                 el.appendChild(newNode);
5879             }else{
5880                 var html = createHtml(o);
5881                 newNode = this.insertHtml("beforeEnd", el, html);
5882             }
5883             return returnElement ? Roo.get(newNode, true) : newNode;
5884         },
5885     
5886         /**
5887          * Creates new Dom element(s) and overwrites the contents of el with them
5888          * @param {String/HTMLElement/Element} el The context element
5889          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5890          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5891          * @return {HTMLElement/Roo.Element} The new node
5892          */
5893         overwrite : function(el, o, returnElement)
5894         {
5895             el = Roo.getDom(el);
5896             if (o.ns) {
5897               
5898                 while (el.childNodes.length) {
5899                     el.removeChild(el.firstChild);
5900                 }
5901                 createDom(o, el);
5902             } else {
5903                 el.innerHTML = createHtml(o);   
5904             }
5905             
5906             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5907         },
5908     
5909         /**
5910          * Creates a new Roo.DomHelper.Template from the Dom object spec
5911          * @param {Object} o The Dom object spec (and children)
5912          * @return {Roo.DomHelper.Template} The new template
5913          */
5914         createTemplate : function(o){
5915             var html = createHtml(o);
5916             return new Roo.Template(html);
5917         },
5918          /**
5919          * Updates the first element with the spec from the o (replacing if necessary)
5920          * This iterates through the children, and updates attributes / children etc..
5921          * @param {String/HTMLElement/Element} el The context element
5922          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5923          */
5924         
5925         update : function(el, o)
5926         {
5927             updateNode(Roo.getDom(el), createDom(o));
5928             
5929         }
5930         
5931         
5932     };
5933 }();
5934 /*
5935  * Based on:
5936  * Ext JS Library 1.1.1
5937  * Copyright(c) 2006-2007, Ext JS, LLC.
5938  *
5939  * Originally Released Under LGPL - original licence link has changed is not relivant.
5940  *
5941  * Fork - LGPL
5942  * <script type="text/javascript">
5943  */
5944  
5945 /**
5946 * @class Roo.Template
5947 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5948 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5949 * Usage:
5950 <pre><code>
5951 var t = new Roo.Template({
5952     html :  '&lt;div name="{id}"&gt;' + 
5953         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5954         '&lt;/div&gt;',
5955     myformat: function (value, allValues) {
5956         return 'XX' + value;
5957     }
5958 });
5959 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5960 </code></pre>
5961 * For more information see this blog post with examples:
5962 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5963      - Create Elements using DOM, HTML fragments and Templates</a>. 
5964 * @constructor
5965 * @param {Object} cfg - Configuration object.
5966 */
5967 Roo.Template = function(cfg){
5968     // BC!
5969     if(cfg instanceof Array){
5970         cfg = cfg.join("");
5971     }else if(arguments.length > 1){
5972         cfg = Array.prototype.join.call(arguments, "");
5973     }
5974     
5975     
5976     if (typeof(cfg) == 'object') {
5977         Roo.apply(this,cfg)
5978     } else {
5979         // bc
5980         this.html = cfg;
5981     }
5982     if (this.url) {
5983         this.load();
5984     }
5985     
5986 };
5987 Roo.Template.prototype = {
5988     
5989     /**
5990      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5991      */
5992     onLoad : false,
5993     
5994     
5995     /**
5996      * @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..
5997      *                    it should be fixed so that template is observable...
5998      */
5999     url : false,
6000     /**
6001      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
6002      */
6003     html : '',
6004     
6005     
6006     compiled : false,
6007     loaded : false,
6008     /**
6009      * Returns an HTML fragment of this template with the specified values applied.
6010      * @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'})
6011      * @return {String} The HTML fragment
6012      */
6013     
6014    
6015     
6016     applyTemplate : function(values){
6017         //Roo.log(["applyTemplate", values]);
6018         try {
6019            
6020             if(this.compiled){
6021                 return this.compiled(values);
6022             }
6023             var useF = this.disableFormats !== true;
6024             var fm = Roo.util.Format, tpl = this;
6025             var fn = function(m, name, format, args){
6026                 if(format && useF){
6027                     if(format.substr(0, 5) == "this."){
6028                         return tpl.call(format.substr(5), values[name], values);
6029                     }else{
6030                         if(args){
6031                             // quoted values are required for strings in compiled templates, 
6032                             // but for non compiled we need to strip them
6033                             // quoted reversed for jsmin
6034                             var re = /^\s*['"](.*)["']\s*$/;
6035                             args = args.split(',');
6036                             for(var i = 0, len = args.length; i < len; i++){
6037                                 args[i] = args[i].replace(re, "$1");
6038                             }
6039                             args = [values[name]].concat(args);
6040                         }else{
6041                             args = [values[name]];
6042                         }
6043                         return fm[format].apply(fm, args);
6044                     }
6045                 }else{
6046                     return values[name] !== undefined ? values[name] : "";
6047                 }
6048             };
6049             return this.html.replace(this.re, fn);
6050         } catch (e) {
6051             Roo.log(e);
6052             throw e;
6053         }
6054          
6055     },
6056     
6057     loading : false,
6058       
6059     load : function ()
6060     {
6061          
6062         if (this.loading) {
6063             return;
6064         }
6065         var _t = this;
6066         
6067         this.loading = true;
6068         this.compiled = false;
6069         
6070         var cx = new Roo.data.Connection();
6071         cx.request({
6072             url : this.url,
6073             method : 'GET',
6074             success : function (response) {
6075                 _t.loading = false;
6076                 _t.url = false;
6077                 
6078                 _t.set(response.responseText,true);
6079                 _t.loaded = true;
6080                 if (_t.onLoad) {
6081                     _t.onLoad();
6082                 }
6083              },
6084             failure : function(response) {
6085                 Roo.log("Template failed to load from " + _t.url);
6086                 _t.loading = false;
6087             }
6088         });
6089     },
6090
6091     /**
6092      * Sets the HTML used as the template and optionally compiles it.
6093      * @param {String} html
6094      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
6095      * @return {Roo.Template} this
6096      */
6097     set : function(html, compile){
6098         this.html = html;
6099         this.compiled = false;
6100         if(compile){
6101             this.compile();
6102         }
6103         return this;
6104     },
6105     
6106     /**
6107      * True to disable format functions (defaults to false)
6108      * @type Boolean
6109      */
6110     disableFormats : false,
6111     
6112     /**
6113     * The regular expression used to match template variables 
6114     * @type RegExp
6115     * @property 
6116     */
6117     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
6118     
6119     /**
6120      * Compiles the template into an internal function, eliminating the RegEx overhead.
6121      * @return {Roo.Template} this
6122      */
6123     compile : function(){
6124         var fm = Roo.util.Format;
6125         var useF = this.disableFormats !== true;
6126         var sep = Roo.isGecko ? "+" : ",";
6127         var fn = function(m, name, format, args){
6128             if(format && useF){
6129                 args = args ? ',' + args : "";
6130                 if(format.substr(0, 5) != "this."){
6131                     format = "fm." + format + '(';
6132                 }else{
6133                     format = 'this.call("'+ format.substr(5) + '", ';
6134                     args = ", values";
6135                 }
6136             }else{
6137                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
6138             }
6139             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
6140         };
6141         var body;
6142         // branched to use + in gecko and [].join() in others
6143         if(Roo.isGecko){
6144             body = "this.compiled = function(values){ return '" +
6145                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
6146                     "';};";
6147         }else{
6148             body = ["this.compiled = function(values){ return ['"];
6149             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
6150             body.push("'].join('');};");
6151             body = body.join('');
6152         }
6153         /**
6154          * eval:var:values
6155          * eval:var:fm
6156          */
6157         eval(body);
6158         return this;
6159     },
6160     
6161     // private function used to call members
6162     call : function(fnName, value, allValues){
6163         return this[fnName](value, allValues);
6164     },
6165     
6166     /**
6167      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
6168      * @param {String/HTMLElement/Roo.Element} el The context element
6169      * @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'})
6170      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6171      * @return {HTMLElement/Roo.Element} The new node or Element
6172      */
6173     insertFirst: function(el, values, returnElement){
6174         return this.doInsert('afterBegin', el, values, returnElement);
6175     },
6176
6177     /**
6178      * Applies the supplied values to the template and inserts the new node(s) before el.
6179      * @param {String/HTMLElement/Roo.Element} el The context element
6180      * @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'})
6181      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6182      * @return {HTMLElement/Roo.Element} The new node or Element
6183      */
6184     insertBefore: function(el, values, returnElement){
6185         return this.doInsert('beforeBegin', el, values, returnElement);
6186     },
6187
6188     /**
6189      * Applies the supplied values to the template and inserts the new node(s) after el.
6190      * @param {String/HTMLElement/Roo.Element} el The context element
6191      * @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'})
6192      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6193      * @return {HTMLElement/Roo.Element} The new node or Element
6194      */
6195     insertAfter : function(el, values, returnElement){
6196         return this.doInsert('afterEnd', el, values, returnElement);
6197     },
6198     
6199     /**
6200      * Applies the supplied values to the template and appends the new node(s) to el.
6201      * @param {String/HTMLElement/Roo.Element} el The context element
6202      * @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'})
6203      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6204      * @return {HTMLElement/Roo.Element} The new node or Element
6205      */
6206     append : function(el, values, returnElement){
6207         return this.doInsert('beforeEnd', el, values, returnElement);
6208     },
6209
6210     doInsert : function(where, el, values, returnEl){
6211         el = Roo.getDom(el);
6212         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6213         return returnEl ? Roo.get(newNode, true) : newNode;
6214     },
6215
6216     /**
6217      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6218      * @param {String/HTMLElement/Roo.Element} el The context element
6219      * @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'})
6220      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6221      * @return {HTMLElement/Roo.Element} The new node or Element
6222      */
6223     overwrite : function(el, values, returnElement){
6224         el = Roo.getDom(el);
6225         el.innerHTML = this.applyTemplate(values);
6226         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6227     }
6228 };
6229 /**
6230  * Alias for {@link #applyTemplate}
6231  * @method
6232  */
6233 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6234
6235 // backwards compat
6236 Roo.DomHelper.Template = Roo.Template;
6237
6238 /**
6239  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6240  * @param {String/HTMLElement} el A DOM element or its id
6241  * @returns {Roo.Template} The created template
6242  * @static
6243  */
6244 Roo.Template.from = function(el){
6245     el = Roo.getDom(el);
6246     return new Roo.Template(el.value || el.innerHTML);
6247 };/*
6248  * Based on:
6249  * Ext JS Library 1.1.1
6250  * Copyright(c) 2006-2007, Ext JS, LLC.
6251  *
6252  * Originally Released Under LGPL - original licence link has changed is not relivant.
6253  *
6254  * Fork - LGPL
6255  * <script type="text/javascript">
6256  */
6257  
6258
6259 /*
6260  * This is code is also distributed under MIT license for use
6261  * with jQuery and prototype JavaScript libraries.
6262  */
6263 /**
6264  * @class Roo.DomQuery
6265 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).
6266 <p>
6267 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>
6268
6269 <p>
6270 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.
6271 </p>
6272 <h4>Element Selectors:</h4>
6273 <ul class="list">
6274     <li> <b>*</b> any element</li>
6275     <li> <b>E</b> an element with the tag E</li>
6276     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6277     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6278     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6279     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6280 </ul>
6281 <h4>Attribute Selectors:</h4>
6282 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6283 <ul class="list">
6284     <li> <b>E[foo]</b> has an attribute "foo"</li>
6285     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6286     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6287     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6288     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6289     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6290     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6291 </ul>
6292 <h4>Pseudo Classes:</h4>
6293 <ul class="list">
6294     <li> <b>E:first-child</b> E is the first child of its parent</li>
6295     <li> <b>E:last-child</b> E is the last child of its parent</li>
6296     <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>
6297     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6298     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6299     <li> <b>E:only-child</b> E is the only child of its parent</li>
6300     <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>
6301     <li> <b>E:first</b> the first E in the resultset</li>
6302     <li> <b>E:last</b> the last E in the resultset</li>
6303     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6304     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6305     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6306     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6307     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6308     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6309     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6310     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6311     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6312 </ul>
6313 <h4>CSS Value Selectors:</h4>
6314 <ul class="list">
6315     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6316     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6317     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6318     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6319     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6320     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6321 </ul>
6322  * @static
6323  */
6324 Roo.DomQuery = function(){
6325     var cache = {}, simpleCache = {}, valueCache = {};
6326     var nonSpace = /\S/;
6327     var trimRe = /^\s+|\s+$/g;
6328     var tplRe = /\{(\d+)\}/g;
6329     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6330     var tagTokenRe = /^(#)?([\w-\*]+)/;
6331     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6332
6333     function child(p, index){
6334         var i = 0;
6335         var n = p.firstChild;
6336         while(n){
6337             if(n.nodeType == 1){
6338                if(++i == index){
6339                    return n;
6340                }
6341             }
6342             n = n.nextSibling;
6343         }
6344         return null;
6345     };
6346
6347     function next(n){
6348         while((n = n.nextSibling) && n.nodeType != 1);
6349         return n;
6350     };
6351
6352     function prev(n){
6353         while((n = n.previousSibling) && n.nodeType != 1);
6354         return n;
6355     };
6356
6357     function children(d){
6358         var n = d.firstChild, ni = -1;
6359             while(n){
6360                 var nx = n.nextSibling;
6361                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6362                     d.removeChild(n);
6363                 }else{
6364                     n.nodeIndex = ++ni;
6365                 }
6366                 n = nx;
6367             }
6368             return this;
6369         };
6370
6371     function byClassName(c, a, v){
6372         if(!v){
6373             return c;
6374         }
6375         var r = [], ri = -1, cn;
6376         for(var i = 0, ci; ci = c[i]; i++){
6377             
6378             
6379             if((' '+
6380                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6381                  +' ').indexOf(v) != -1){
6382                 r[++ri] = ci;
6383             }
6384         }
6385         return r;
6386     };
6387
6388     function attrValue(n, attr){
6389         if(!n.tagName && typeof n.length != "undefined"){
6390             n = n[0];
6391         }
6392         if(!n){
6393             return null;
6394         }
6395         if(attr == "for"){
6396             return n.htmlFor;
6397         }
6398         if(attr == "class" || attr == "className"){
6399             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6400         }
6401         return n.getAttribute(attr) || n[attr];
6402
6403     };
6404
6405     function getNodes(ns, mode, tagName){
6406         var result = [], ri = -1, cs;
6407         if(!ns){
6408             return result;
6409         }
6410         tagName = tagName || "*";
6411         if(typeof ns.getElementsByTagName != "undefined"){
6412             ns = [ns];
6413         }
6414         if(!mode){
6415             for(var i = 0, ni; ni = ns[i]; i++){
6416                 cs = ni.getElementsByTagName(tagName);
6417                 for(var j = 0, ci; ci = cs[j]; j++){
6418                     result[++ri] = ci;
6419                 }
6420             }
6421         }else if(mode == "/" || mode == ">"){
6422             var utag = tagName.toUpperCase();
6423             for(var i = 0, ni, cn; ni = ns[i]; i++){
6424                 cn = ni.children || ni.childNodes;
6425                 for(var j = 0, cj; cj = cn[j]; j++){
6426                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6427                         result[++ri] = cj;
6428                     }
6429                 }
6430             }
6431         }else if(mode == "+"){
6432             var utag = tagName.toUpperCase();
6433             for(var i = 0, n; n = ns[i]; i++){
6434                 while((n = n.nextSibling) && n.nodeType != 1);
6435                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6436                     result[++ri] = n;
6437                 }
6438             }
6439         }else if(mode == "~"){
6440             for(var i = 0, n; n = ns[i]; i++){
6441                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6442                 if(n){
6443                     result[++ri] = n;
6444                 }
6445             }
6446         }
6447         return result;
6448     };
6449
6450     function concat(a, b){
6451         if(b.slice){
6452             return a.concat(b);
6453         }
6454         for(var i = 0, l = b.length; i < l; i++){
6455             a[a.length] = b[i];
6456         }
6457         return a;
6458     }
6459
6460     function byTag(cs, tagName){
6461         if(cs.tagName || cs == document){
6462             cs = [cs];
6463         }
6464         if(!tagName){
6465             return cs;
6466         }
6467         var r = [], ri = -1;
6468         tagName = tagName.toLowerCase();
6469         for(var i = 0, ci; ci = cs[i]; i++){
6470             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6471                 r[++ri] = ci;
6472             }
6473         }
6474         return r;
6475     };
6476
6477     function byId(cs, attr, id){
6478         if(cs.tagName || cs == document){
6479             cs = [cs];
6480         }
6481         if(!id){
6482             return cs;
6483         }
6484         var r = [], ri = -1;
6485         for(var i = 0,ci; ci = cs[i]; i++){
6486             if(ci && ci.id == id){
6487                 r[++ri] = ci;
6488                 return r;
6489             }
6490         }
6491         return r;
6492     };
6493
6494     function byAttribute(cs, attr, value, op, custom){
6495         var r = [], ri = -1, st = custom=="{";
6496         var f = Roo.DomQuery.operators[op];
6497         for(var i = 0, ci; ci = cs[i]; i++){
6498             var a;
6499             if(st){
6500                 a = Roo.DomQuery.getStyle(ci, attr);
6501             }
6502             else if(attr == "class" || attr == "className"){
6503                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6504             }else if(attr == "for"){
6505                 a = ci.htmlFor;
6506             }else if(attr == "href"){
6507                 a = ci.getAttribute("href", 2);
6508             }else{
6509                 a = ci.getAttribute(attr);
6510             }
6511             if((f && f(a, value)) || (!f && a)){
6512                 r[++ri] = ci;
6513             }
6514         }
6515         return r;
6516     };
6517
6518     function byPseudo(cs, name, value){
6519         return Roo.DomQuery.pseudos[name](cs, value);
6520     };
6521
6522     // This is for IE MSXML which does not support expandos.
6523     // IE runs the same speed using setAttribute, however FF slows way down
6524     // and Safari completely fails so they need to continue to use expandos.
6525     var isIE = window.ActiveXObject ? true : false;
6526
6527     // this eval is stop the compressor from
6528     // renaming the variable to something shorter
6529     
6530     /** eval:var:batch */
6531     var batch = 30803; 
6532
6533     var key = 30803;
6534
6535     function nodupIEXml(cs){
6536         var d = ++key;
6537         cs[0].setAttribute("_nodup", d);
6538         var r = [cs[0]];
6539         for(var i = 1, len = cs.length; i < len; i++){
6540             var c = cs[i];
6541             if(!c.getAttribute("_nodup") != d){
6542                 c.setAttribute("_nodup", d);
6543                 r[r.length] = c;
6544             }
6545         }
6546         for(var i = 0, len = cs.length; i < len; i++){
6547             cs[i].removeAttribute("_nodup");
6548         }
6549         return r;
6550     }
6551
6552     function nodup(cs){
6553         if(!cs){
6554             return [];
6555         }
6556         var len = cs.length, c, i, r = cs, cj, ri = -1;
6557         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6558             return cs;
6559         }
6560         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6561             return nodupIEXml(cs);
6562         }
6563         var d = ++key;
6564         cs[0]._nodup = d;
6565         for(i = 1; c = cs[i]; i++){
6566             if(c._nodup != d){
6567                 c._nodup = d;
6568             }else{
6569                 r = [];
6570                 for(var j = 0; j < i; j++){
6571                     r[++ri] = cs[j];
6572                 }
6573                 for(j = i+1; cj = cs[j]; j++){
6574                     if(cj._nodup != d){
6575                         cj._nodup = d;
6576                         r[++ri] = cj;
6577                     }
6578                 }
6579                 return r;
6580             }
6581         }
6582         return r;
6583     }
6584
6585     function quickDiffIEXml(c1, c2){
6586         var d = ++key;
6587         for(var i = 0, len = c1.length; i < len; i++){
6588             c1[i].setAttribute("_qdiff", d);
6589         }
6590         var r = [];
6591         for(var i = 0, len = c2.length; i < len; i++){
6592             if(c2[i].getAttribute("_qdiff") != d){
6593                 r[r.length] = c2[i];
6594             }
6595         }
6596         for(var i = 0, len = c1.length; i < len; i++){
6597            c1[i].removeAttribute("_qdiff");
6598         }
6599         return r;
6600     }
6601
6602     function quickDiff(c1, c2){
6603         var len1 = c1.length;
6604         if(!len1){
6605             return c2;
6606         }
6607         if(isIE && c1[0].selectSingleNode){
6608             return quickDiffIEXml(c1, c2);
6609         }
6610         var d = ++key;
6611         for(var i = 0; i < len1; i++){
6612             c1[i]._qdiff = d;
6613         }
6614         var r = [];
6615         for(var i = 0, len = c2.length; i < len; i++){
6616             if(c2[i]._qdiff != d){
6617                 r[r.length] = c2[i];
6618             }
6619         }
6620         return r;
6621     }
6622
6623     function quickId(ns, mode, root, id){
6624         if(ns == root){
6625            var d = root.ownerDocument || root;
6626            return d.getElementById(id);
6627         }
6628         ns = getNodes(ns, mode, "*");
6629         return byId(ns, null, id);
6630     }
6631
6632     return {
6633         getStyle : function(el, name){
6634             return Roo.fly(el).getStyle(name);
6635         },
6636         /**
6637          * Compiles a selector/xpath query into a reusable function. The returned function
6638          * takes one parameter "root" (optional), which is the context node from where the query should start.
6639          * @param {String} selector The selector/xpath query
6640          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6641          * @return {Function}
6642          */
6643         compile : function(path, type){
6644             type = type || "select";
6645             
6646             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6647             var q = path, mode, lq;
6648             var tk = Roo.DomQuery.matchers;
6649             var tklen = tk.length;
6650             var mm;
6651
6652             // accept leading mode switch
6653             var lmode = q.match(modeRe);
6654             if(lmode && lmode[1]){
6655                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6656                 q = q.replace(lmode[1], "");
6657             }
6658             // strip leading slashes
6659             while(path.substr(0, 1)=="/"){
6660                 path = path.substr(1);
6661             }
6662
6663             while(q && lq != q){
6664                 lq = q;
6665                 var tm = q.match(tagTokenRe);
6666                 if(type == "select"){
6667                     if(tm){
6668                         if(tm[1] == "#"){
6669                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6670                         }else{
6671                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6672                         }
6673                         q = q.replace(tm[0], "");
6674                     }else if(q.substr(0, 1) != '@'){
6675                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6676                     }
6677                 }else{
6678                     if(tm){
6679                         if(tm[1] == "#"){
6680                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6681                         }else{
6682                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6683                         }
6684                         q = q.replace(tm[0], "");
6685                     }
6686                 }
6687                 while(!(mm = q.match(modeRe))){
6688                     var matched = false;
6689                     for(var j = 0; j < tklen; j++){
6690                         var t = tk[j];
6691                         var m = q.match(t.re);
6692                         if(m){
6693                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6694                                                     return m[i];
6695                                                 });
6696                             q = q.replace(m[0], "");
6697                             matched = true;
6698                             break;
6699                         }
6700                     }
6701                     // prevent infinite loop on bad selector
6702                     if(!matched){
6703                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6704                     }
6705                 }
6706                 if(mm[1]){
6707                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6708                     q = q.replace(mm[1], "");
6709                 }
6710             }
6711             fn[fn.length] = "return nodup(n);\n}";
6712             
6713              /** 
6714               * list of variables that need from compression as they are used by eval.
6715              *  eval:var:batch 
6716              *  eval:var:nodup
6717              *  eval:var:byTag
6718              *  eval:var:ById
6719              *  eval:var:getNodes
6720              *  eval:var:quickId
6721              *  eval:var:mode
6722              *  eval:var:root
6723              *  eval:var:n
6724              *  eval:var:byClassName
6725              *  eval:var:byPseudo
6726              *  eval:var:byAttribute
6727              *  eval:var:attrValue
6728              * 
6729              **/ 
6730             eval(fn.join(""));
6731             return f;
6732         },
6733
6734         /**
6735          * Selects a group of elements.
6736          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6737          * @param {Node} root (optional) The start of the query (defaults to document).
6738          * @return {Array}
6739          */
6740         select : function(path, root, type){
6741             if(!root || root == document){
6742                 root = document;
6743             }
6744             if(typeof root == "string"){
6745                 root = document.getElementById(root);
6746             }
6747             var paths = path.split(",");
6748             var results = [];
6749             for(var i = 0, len = paths.length; i < len; i++){
6750                 var p = paths[i].replace(trimRe, "");
6751                 if(!cache[p]){
6752                     cache[p] = Roo.DomQuery.compile(p);
6753                     if(!cache[p]){
6754                         throw p + " is not a valid selector";
6755                     }
6756                 }
6757                 var result = cache[p](root);
6758                 if(result && result != document){
6759                     results = results.concat(result);
6760                 }
6761             }
6762             if(paths.length > 1){
6763                 return nodup(results);
6764             }
6765             return results;
6766         },
6767
6768         /**
6769          * Selects a single element.
6770          * @param {String} selector The selector/xpath query
6771          * @param {Node} root (optional) The start of the query (defaults to document).
6772          * @return {Element}
6773          */
6774         selectNode : function(path, root){
6775             return Roo.DomQuery.select(path, root)[0];
6776         },
6777
6778         /**
6779          * Selects the value of a node, optionally replacing null with the defaultValue.
6780          * @param {String} selector The selector/xpath query
6781          * @param {Node} root (optional) The start of the query (defaults to document).
6782          * @param {String} defaultValue
6783          */
6784         selectValue : function(path, root, defaultValue){
6785             path = path.replace(trimRe, "");
6786             if(!valueCache[path]){
6787                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6788             }
6789             var n = valueCache[path](root);
6790             n = n[0] ? n[0] : n;
6791             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6792             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6793         },
6794
6795         /**
6796          * Selects the value of a node, parsing integers and floats.
6797          * @param {String} selector The selector/xpath query
6798          * @param {Node} root (optional) The start of the query (defaults to document).
6799          * @param {Number} defaultValue
6800          * @return {Number}
6801          */
6802         selectNumber : function(path, root, defaultValue){
6803             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6804             return parseFloat(v);
6805         },
6806
6807         /**
6808          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6809          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6810          * @param {String} selector The simple selector to test
6811          * @return {Boolean}
6812          */
6813         is : function(el, ss){
6814             if(typeof el == "string"){
6815                 el = document.getElementById(el);
6816             }
6817             var isArray = (el instanceof Array);
6818             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6819             return isArray ? (result.length == el.length) : (result.length > 0);
6820         },
6821
6822         /**
6823          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6824          * @param {Array} el An array of elements to filter
6825          * @param {String} selector The simple selector to test
6826          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6827          * the selector instead of the ones that match
6828          * @return {Array}
6829          */
6830         filter : function(els, ss, nonMatches){
6831             ss = ss.replace(trimRe, "");
6832             if(!simpleCache[ss]){
6833                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6834             }
6835             var result = simpleCache[ss](els);
6836             return nonMatches ? quickDiff(result, els) : result;
6837         },
6838
6839         /**
6840          * Collection of matching regular expressions and code snippets.
6841          */
6842         matchers : [{
6843                 re: /^\.([\w-]+)/,
6844                 select: 'n = byClassName(n, null, " {1} ");'
6845             }, {
6846                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6847                 select: 'n = byPseudo(n, "{1}", "{2}");'
6848             },{
6849                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6850                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6851             }, {
6852                 re: /^#([\w-]+)/,
6853                 select: 'n = byId(n, null, "{1}");'
6854             },{
6855                 re: /^@([\w-]+)/,
6856                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6857             }
6858         ],
6859
6860         /**
6861          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6862          * 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;.
6863          */
6864         operators : {
6865             "=" : function(a, v){
6866                 return a == v;
6867             },
6868             "!=" : function(a, v){
6869                 return a != v;
6870             },
6871             "^=" : function(a, v){
6872                 return a && a.substr(0, v.length) == v;
6873             },
6874             "$=" : function(a, v){
6875                 return a && a.substr(a.length-v.length) == v;
6876             },
6877             "*=" : function(a, v){
6878                 return a && a.indexOf(v) !== -1;
6879             },
6880             "%=" : function(a, v){
6881                 return (a % v) == 0;
6882             },
6883             "|=" : function(a, v){
6884                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6885             },
6886             "~=" : function(a, v){
6887                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6888             }
6889         },
6890
6891         /**
6892          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6893          * and the argument (if any) supplied in the selector.
6894          */
6895         pseudos : {
6896             "first-child" : function(c){
6897                 var r = [], ri = -1, n;
6898                 for(var i = 0, ci; ci = n = c[i]; i++){
6899                     while((n = n.previousSibling) && n.nodeType != 1);
6900                     if(!n){
6901                         r[++ri] = ci;
6902                     }
6903                 }
6904                 return r;
6905             },
6906
6907             "last-child" : function(c){
6908                 var r = [], ri = -1, n;
6909                 for(var i = 0, ci; ci = n = c[i]; i++){
6910                     while((n = n.nextSibling) && n.nodeType != 1);
6911                     if(!n){
6912                         r[++ri] = ci;
6913                     }
6914                 }
6915                 return r;
6916             },
6917
6918             "nth-child" : function(c, a) {
6919                 var r = [], ri = -1;
6920                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6921                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6922                 for(var i = 0, n; n = c[i]; i++){
6923                     var pn = n.parentNode;
6924                     if (batch != pn._batch) {
6925                         var j = 0;
6926                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6927                             if(cn.nodeType == 1){
6928                                cn.nodeIndex = ++j;
6929                             }
6930                         }
6931                         pn._batch = batch;
6932                     }
6933                     if (f == 1) {
6934                         if (l == 0 || n.nodeIndex == l){
6935                             r[++ri] = n;
6936                         }
6937                     } else if ((n.nodeIndex + l) % f == 0){
6938                         r[++ri] = n;
6939                     }
6940                 }
6941
6942                 return r;
6943             },
6944
6945             "only-child" : function(c){
6946                 var r = [], ri = -1;;
6947                 for(var i = 0, ci; ci = c[i]; i++){
6948                     if(!prev(ci) && !next(ci)){
6949                         r[++ri] = ci;
6950                     }
6951                 }
6952                 return r;
6953             },
6954
6955             "empty" : function(c){
6956                 var r = [], ri = -1;
6957                 for(var i = 0, ci; ci = c[i]; i++){
6958                     var cns = ci.childNodes, j = 0, cn, empty = true;
6959                     while(cn = cns[j]){
6960                         ++j;
6961                         if(cn.nodeType == 1 || cn.nodeType == 3){
6962                             empty = false;
6963                             break;
6964                         }
6965                     }
6966                     if(empty){
6967                         r[++ri] = ci;
6968                     }
6969                 }
6970                 return r;
6971             },
6972
6973             "contains" : function(c, v){
6974                 var r = [], ri = -1;
6975                 for(var i = 0, ci; ci = c[i]; i++){
6976                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6977                         r[++ri] = ci;
6978                     }
6979                 }
6980                 return r;
6981             },
6982
6983             "nodeValue" : function(c, v){
6984                 var r = [], ri = -1;
6985                 for(var i = 0, ci; ci = c[i]; i++){
6986                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6987                         r[++ri] = ci;
6988                     }
6989                 }
6990                 return r;
6991             },
6992
6993             "checked" : function(c){
6994                 var r = [], ri = -1;
6995                 for(var i = 0, ci; ci = c[i]; i++){
6996                     if(ci.checked == true){
6997                         r[++ri] = ci;
6998                     }
6999                 }
7000                 return r;
7001             },
7002
7003             "not" : function(c, ss){
7004                 return Roo.DomQuery.filter(c, ss, true);
7005             },
7006
7007             "odd" : function(c){
7008                 return this["nth-child"](c, "odd");
7009             },
7010
7011             "even" : function(c){
7012                 return this["nth-child"](c, "even");
7013             },
7014
7015             "nth" : function(c, a){
7016                 return c[a-1] || [];
7017             },
7018
7019             "first" : function(c){
7020                 return c[0] || [];
7021             },
7022
7023             "last" : function(c){
7024                 return c[c.length-1] || [];
7025             },
7026
7027             "has" : function(c, ss){
7028                 var s = Roo.DomQuery.select;
7029                 var r = [], ri = -1;
7030                 for(var i = 0, ci; ci = c[i]; i++){
7031                     if(s(ss, ci).length > 0){
7032                         r[++ri] = ci;
7033                     }
7034                 }
7035                 return r;
7036             },
7037
7038             "next" : function(c, ss){
7039                 var is = Roo.DomQuery.is;
7040                 var r = [], ri = -1;
7041                 for(var i = 0, ci; ci = c[i]; i++){
7042                     var n = next(ci);
7043                     if(n && is(n, ss)){
7044                         r[++ri] = ci;
7045                     }
7046                 }
7047                 return r;
7048             },
7049
7050             "prev" : function(c, ss){
7051                 var is = Roo.DomQuery.is;
7052                 var r = [], ri = -1;
7053                 for(var i = 0, ci; ci = c[i]; i++){
7054                     var n = prev(ci);
7055                     if(n && is(n, ss)){
7056                         r[++ri] = ci;
7057                     }
7058                 }
7059                 return r;
7060             }
7061         }
7062     };
7063 }();
7064
7065 /**
7066  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
7067  * @param {String} path The selector/xpath query
7068  * @param {Node} root (optional) The start of the query (defaults to document).
7069  * @return {Array}
7070  * @member Roo
7071  * @method query
7072  */
7073 Roo.query = Roo.DomQuery.select;
7074 /*
7075  * Based on:
7076  * Ext JS Library 1.1.1
7077  * Copyright(c) 2006-2007, Ext JS, LLC.
7078  *
7079  * Originally Released Under LGPL - original licence link has changed is not relivant.
7080  *
7081  * Fork - LGPL
7082  * <script type="text/javascript">
7083  */
7084
7085 /**
7086  * @class Roo.util.Observable
7087  * Base class that provides a common interface for publishing events. Subclasses are expected to
7088  * to have a property "events" with all the events defined.<br>
7089  * For example:
7090  * <pre><code>
7091  Employee = function(name){
7092     this.name = name;
7093     this.addEvents({
7094         "fired" : true,
7095         "quit" : true
7096     });
7097  }
7098  Roo.extend(Employee, Roo.util.Observable);
7099 </code></pre>
7100  * @param {Object} config properties to use (incuding events / listeners)
7101  */
7102
7103 Roo.util.Observable = function(cfg){
7104     
7105     cfg = cfg|| {};
7106     this.addEvents(cfg.events || {});
7107     if (cfg.events) {
7108         delete cfg.events; // make sure
7109     }
7110      
7111     Roo.apply(this, cfg);
7112     
7113     if(this.listeners){
7114         this.on(this.listeners);
7115         delete this.listeners;
7116     }
7117 };
7118 Roo.util.Observable.prototype = {
7119     /** 
7120  * @cfg {Object} listeners  list of events and functions to call for this object, 
7121  * For example :
7122  * <pre><code>
7123     listeners :  { 
7124        'click' : function(e) {
7125            ..... 
7126         } ,
7127         .... 
7128     } 
7129   </code></pre>
7130  */
7131     
7132     
7133     /**
7134      * Fires the specified event with the passed parameters (minus the event name).
7135      * @param {String} eventName
7136      * @param {Object...} args Variable number of parameters are passed to handlers
7137      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
7138      */
7139     fireEvent : function(){
7140         var ce = this.events[arguments[0].toLowerCase()];
7141         if(typeof ce == "object"){
7142             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
7143         }else{
7144             return true;
7145         }
7146     },
7147
7148     // private
7149     filterOptRe : /^(?:scope|delay|buffer|single)$/,
7150
7151     /**
7152      * Appends an event handler to this component
7153      * @param {String}   eventName The type of event to listen for
7154      * @param {Function} handler The method the event invokes
7155      * @param {Object}   scope (optional) The scope in which to execute the handler
7156      * function. The handler function's "this" context.
7157      * @param {Object}   options (optional) An object containing handler configuration
7158      * properties. This may contain any of the following properties:<ul>
7159      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7160      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7161      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7162      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7163      * by the specified number of milliseconds. If the event fires again within that time, the original
7164      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7165      * </ul><br>
7166      * <p>
7167      * <b>Combining Options</b><br>
7168      * Using the options argument, it is possible to combine different types of listeners:<br>
7169      * <br>
7170      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
7171                 <pre><code>
7172                 el.on('click', this.onClick, this, {
7173                         single: true,
7174                 delay: 100,
7175                 forumId: 4
7176                 });
7177                 </code></pre>
7178      * <p>
7179      * <b>Attaching multiple handlers in 1 call</b><br>
7180      * The method also allows for a single argument to be passed which is a config object containing properties
7181      * which specify multiple handlers.
7182      * <pre><code>
7183                 el.on({
7184                         'click': {
7185                         fn: this.onClick,
7186                         scope: this,
7187                         delay: 100
7188                 }, 
7189                 'mouseover': {
7190                         fn: this.onMouseOver,
7191                         scope: this
7192                 },
7193                 'mouseout': {
7194                         fn: this.onMouseOut,
7195                         scope: this
7196                 }
7197                 });
7198                 </code></pre>
7199      * <p>
7200      * Or a shorthand syntax which passes the same scope object to all handlers:
7201         <pre><code>
7202                 el.on({
7203                         'click': this.onClick,
7204                 'mouseover': this.onMouseOver,
7205                 'mouseout': this.onMouseOut,
7206                 scope: this
7207                 });
7208                 </code></pre>
7209      */
7210     addListener : function(eventName, fn, scope, o){
7211         if(typeof eventName == "object"){
7212             o = eventName;
7213             for(var e in o){
7214                 if(this.filterOptRe.test(e)){
7215                     continue;
7216                 }
7217                 if(typeof o[e] == "function"){
7218                     // shared options
7219                     this.addListener(e, o[e], o.scope,  o);
7220                 }else{
7221                     // individual options
7222                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
7223                 }
7224             }
7225             return;
7226         }
7227         o = (!o || typeof o == "boolean") ? {} : o;
7228         eventName = eventName.toLowerCase();
7229         var ce = this.events[eventName] || true;
7230         if(typeof ce == "boolean"){
7231             ce = new Roo.util.Event(this, eventName);
7232             this.events[eventName] = ce;
7233         }
7234         ce.addListener(fn, scope, o);
7235     },
7236
7237     /**
7238      * Removes a listener
7239      * @param {String}   eventName     The type of event to listen for
7240      * @param {Function} handler        The handler to remove
7241      * @param {Object}   scope  (optional) The scope (this object) for the handler
7242      */
7243     removeListener : function(eventName, fn, scope){
7244         var ce = this.events[eventName.toLowerCase()];
7245         if(typeof ce == "object"){
7246             ce.removeListener(fn, scope);
7247         }
7248     },
7249
7250     /**
7251      * Removes all listeners for this object
7252      */
7253     purgeListeners : function(){
7254         for(var evt in this.events){
7255             if(typeof this.events[evt] == "object"){
7256                  this.events[evt].clearListeners();
7257             }
7258         }
7259     },
7260
7261     relayEvents : function(o, events){
7262         var createHandler = function(ename){
7263             return function(){
7264                  
7265                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7266             };
7267         };
7268         for(var i = 0, len = events.length; i < len; i++){
7269             var ename = events[i];
7270             if(!this.events[ename]){
7271                 this.events[ename] = true;
7272             };
7273             o.on(ename, createHandler(ename), this);
7274         }
7275     },
7276
7277     /**
7278      * Used to define events on this Observable
7279      * @param {Object} object The object with the events defined
7280      */
7281     addEvents : function(o){
7282         if(!this.events){
7283             this.events = {};
7284         }
7285         Roo.applyIf(this.events, o);
7286     },
7287
7288     /**
7289      * Checks to see if this object has any listeners for a specified event
7290      * @param {String} eventName The name of the event to check for
7291      * @return {Boolean} True if the event is being listened for, else false
7292      */
7293     hasListener : function(eventName){
7294         var e = this.events[eventName];
7295         return typeof e == "object" && e.listeners.length > 0;
7296     }
7297 };
7298 /**
7299  * Appends an event handler to this element (shorthand for addListener)
7300  * @param {String}   eventName     The type of event to listen for
7301  * @param {Function} handler        The method the event invokes
7302  * @param {Object}   scope (optional) The scope in which to execute the handler
7303  * function. The handler function's "this" context.
7304  * @param {Object}   options  (optional)
7305  * @method
7306  */
7307 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7308 /**
7309  * Removes a listener (shorthand for removeListener)
7310  * @param {String}   eventName     The type of event to listen for
7311  * @param {Function} handler        The handler to remove
7312  * @param {Object}   scope  (optional) The scope (this object) for the handler
7313  * @method
7314  */
7315 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7316
7317 /**
7318  * Starts capture on the specified Observable. All events will be passed
7319  * to the supplied function with the event name + standard signature of the event
7320  * <b>before</b> the event is fired. If the supplied function returns false,
7321  * the event will not fire.
7322  * @param {Observable} o The Observable to capture
7323  * @param {Function} fn The function to call
7324  * @param {Object} scope (optional) The scope (this object) for the fn
7325  * @static
7326  */
7327 Roo.util.Observable.capture = function(o, fn, scope){
7328     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7329 };
7330
7331 /**
7332  * Removes <b>all</b> added captures from the Observable.
7333  * @param {Observable} o The Observable to release
7334  * @static
7335  */
7336 Roo.util.Observable.releaseCapture = function(o){
7337     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7338 };
7339
7340 (function(){
7341
7342     var createBuffered = function(h, o, scope){
7343         var task = new Roo.util.DelayedTask();
7344         return function(){
7345             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7346         };
7347     };
7348
7349     var createSingle = function(h, e, fn, scope){
7350         return function(){
7351             e.removeListener(fn, scope);
7352             return h.apply(scope, arguments);
7353         };
7354     };
7355
7356     var createDelayed = function(h, o, scope){
7357         return function(){
7358             var args = Array.prototype.slice.call(arguments, 0);
7359             setTimeout(function(){
7360                 h.apply(scope, args);
7361             }, o.delay || 10);
7362         };
7363     };
7364
7365     Roo.util.Event = function(obj, name){
7366         this.name = name;
7367         this.obj = obj;
7368         this.listeners = [];
7369     };
7370
7371     Roo.util.Event.prototype = {
7372         addListener : function(fn, scope, options){
7373             var o = options || {};
7374             scope = scope || this.obj;
7375             if(!this.isListening(fn, scope)){
7376                 var l = {fn: fn, scope: scope, options: o};
7377                 var h = fn;
7378                 if(o.delay){
7379                     h = createDelayed(h, o, scope);
7380                 }
7381                 if(o.single){
7382                     h = createSingle(h, this, fn, scope);
7383                 }
7384                 if(o.buffer){
7385                     h = createBuffered(h, o, scope);
7386                 }
7387                 l.fireFn = h;
7388                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7389                     this.listeners.push(l);
7390                 }else{
7391                     this.listeners = this.listeners.slice(0);
7392                     this.listeners.push(l);
7393                 }
7394             }
7395         },
7396
7397         findListener : function(fn, scope){
7398             scope = scope || this.obj;
7399             var ls = this.listeners;
7400             for(var i = 0, len = ls.length; i < len; i++){
7401                 var l = ls[i];
7402                 if(l.fn == fn && l.scope == scope){
7403                     return i;
7404                 }
7405             }
7406             return -1;
7407         },
7408
7409         isListening : function(fn, scope){
7410             return this.findListener(fn, scope) != -1;
7411         },
7412
7413         removeListener : function(fn, scope){
7414             var index;
7415             if((index = this.findListener(fn, scope)) != -1){
7416                 if(!this.firing){
7417                     this.listeners.splice(index, 1);
7418                 }else{
7419                     this.listeners = this.listeners.slice(0);
7420                     this.listeners.splice(index, 1);
7421                 }
7422                 return true;
7423             }
7424             return false;
7425         },
7426
7427         clearListeners : function(){
7428             this.listeners = [];
7429         },
7430
7431         fire : function(){
7432             var ls = this.listeners, scope, len = ls.length;
7433             if(len > 0){
7434                 this.firing = true;
7435                 var args = Array.prototype.slice.call(arguments, 0);                
7436                 for(var i = 0; i < len; i++){
7437                     var l = ls[i];
7438                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7439                         this.firing = false;
7440                         return false;
7441                     }
7442                 }
7443                 this.firing = false;
7444             }
7445             return true;
7446         }
7447     };
7448 })();/*
7449  * RooJS Library 
7450  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7451  *
7452  * Licence LGPL 
7453  *
7454  */
7455  
7456 /**
7457  * @class Roo.Document
7458  * @extends Roo.util.Observable
7459  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7460  * 
7461  * @param {Object} config the methods and properties of the 'base' class for the application.
7462  * 
7463  *  Generic Page handler - implement this to start your app..
7464  * 
7465  * eg.
7466  *  MyProject = new Roo.Document({
7467         events : {
7468             'load' : true // your events..
7469         },
7470         listeners : {
7471             'ready' : function() {
7472                 // fired on Roo.onReady()
7473             }
7474         }
7475  * 
7476  */
7477 Roo.Document = function(cfg) {
7478      
7479     this.addEvents({ 
7480         'ready' : true
7481     });
7482     Roo.util.Observable.call(this,cfg);
7483     
7484     var _this = this;
7485     
7486     Roo.onReady(function() {
7487         _this.fireEvent('ready');
7488     },null,false);
7489     
7490     
7491 }
7492
7493 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7494  * Based on:
7495  * Ext JS Library 1.1.1
7496  * Copyright(c) 2006-2007, Ext JS, LLC.
7497  *
7498  * Originally Released Under LGPL - original licence link has changed is not relivant.
7499  *
7500  * Fork - LGPL
7501  * <script type="text/javascript">
7502  */
7503
7504 /**
7505  * @class Roo.EventManager
7506  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7507  * several useful events directly.
7508  * See {@link Roo.EventObject} for more details on normalized event objects.
7509  * @static
7510  */
7511 Roo.EventManager = function(){
7512     var docReadyEvent, docReadyProcId, docReadyState = false;
7513     var resizeEvent, resizeTask, textEvent, textSize;
7514     var E = Roo.lib.Event;
7515     var D = Roo.lib.Dom;
7516
7517     
7518     
7519
7520     var fireDocReady = function(){
7521         if(!docReadyState){
7522             docReadyState = true;
7523             Roo.isReady = true;
7524             if(docReadyProcId){
7525                 clearInterval(docReadyProcId);
7526             }
7527             if(Roo.isGecko || Roo.isOpera) {
7528                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7529             }
7530             if(Roo.isIE){
7531                 var defer = document.getElementById("ie-deferred-loader");
7532                 if(defer){
7533                     defer.onreadystatechange = null;
7534                     defer.parentNode.removeChild(defer);
7535                 }
7536             }
7537             if(docReadyEvent){
7538                 docReadyEvent.fire();
7539                 docReadyEvent.clearListeners();
7540             }
7541         }
7542     };
7543     
7544     var initDocReady = function(){
7545         docReadyEvent = new Roo.util.Event();
7546         if(Roo.isGecko || Roo.isOpera) {
7547             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7548         }else if(Roo.isIE){
7549             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7550             var defer = document.getElementById("ie-deferred-loader");
7551             defer.onreadystatechange = function(){
7552                 if(this.readyState == "complete"){
7553                     fireDocReady();
7554                 }
7555             };
7556         }else if(Roo.isSafari){ 
7557             docReadyProcId = setInterval(function(){
7558                 var rs = document.readyState;
7559                 if(rs == "complete") {
7560                     fireDocReady();     
7561                  }
7562             }, 10);
7563         }
7564         // no matter what, make sure it fires on load
7565         E.on(window, "load", fireDocReady);
7566     };
7567
7568     var createBuffered = function(h, o){
7569         var task = new Roo.util.DelayedTask(h);
7570         return function(e){
7571             // create new event object impl so new events don't wipe out properties
7572             e = new Roo.EventObjectImpl(e);
7573             task.delay(o.buffer, h, null, [e]);
7574         };
7575     };
7576
7577     var createSingle = function(h, el, ename, fn){
7578         return function(e){
7579             Roo.EventManager.removeListener(el, ename, fn);
7580             h(e);
7581         };
7582     };
7583
7584     var createDelayed = function(h, o){
7585         return function(e){
7586             // create new event object impl so new events don't wipe out properties
7587             e = new Roo.EventObjectImpl(e);
7588             setTimeout(function(){
7589                 h(e);
7590             }, o.delay || 10);
7591         };
7592     };
7593     var transitionEndVal = false;
7594     
7595     var transitionEnd = function()
7596     {
7597         if (transitionEndVal) {
7598             return transitionEndVal;
7599         }
7600         var el = document.createElement('div');
7601
7602         var transEndEventNames = {
7603             WebkitTransition : 'webkitTransitionEnd',
7604             MozTransition    : 'transitionend',
7605             OTransition      : 'oTransitionEnd otransitionend',
7606             transition       : 'transitionend'
7607         };
7608     
7609         for (var name in transEndEventNames) {
7610             if (el.style[name] !== undefined) {
7611                 transitionEndVal = transEndEventNames[name];
7612                 return  transitionEndVal ;
7613             }
7614         }
7615     }
7616     
7617   
7618
7619     var listen = function(element, ename, opt, fn, scope)
7620     {
7621         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7622         fn = fn || o.fn; scope = scope || o.scope;
7623         var el = Roo.getDom(element);
7624         
7625         
7626         if(!el){
7627             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7628         }
7629         
7630         if (ename == 'transitionend') {
7631             ename = transitionEnd();
7632         }
7633         var h = function(e){
7634             e = Roo.EventObject.setEvent(e);
7635             var t;
7636             if(o.delegate){
7637                 t = e.getTarget(o.delegate, el);
7638                 if(!t){
7639                     return;
7640                 }
7641             }else{
7642                 t = e.target;
7643             }
7644             if(o.stopEvent === true){
7645                 e.stopEvent();
7646             }
7647             if(o.preventDefault === true){
7648                e.preventDefault();
7649             }
7650             if(o.stopPropagation === true){
7651                 e.stopPropagation();
7652             }
7653
7654             if(o.normalized === false){
7655                 e = e.browserEvent;
7656             }
7657
7658             fn.call(scope || el, e, t, o);
7659         };
7660         if(o.delay){
7661             h = createDelayed(h, o);
7662         }
7663         if(o.single){
7664             h = createSingle(h, el, ename, fn);
7665         }
7666         if(o.buffer){
7667             h = createBuffered(h, o);
7668         }
7669         
7670         fn._handlers = fn._handlers || [];
7671         
7672         
7673         fn._handlers.push([Roo.id(el), ename, h]);
7674         
7675         
7676          
7677         E.on(el, ename, h); // this adds the actuall listener to the object..
7678         
7679         
7680         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7681             el.addEventListener("DOMMouseScroll", h, false);
7682             E.on(window, 'unload', function(){
7683                 el.removeEventListener("DOMMouseScroll", h, false);
7684             });
7685         }
7686         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7687             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7688         }
7689         return h;
7690     };
7691
7692     var stopListening = function(el, ename, fn){
7693         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7694         if(hds){
7695             for(var i = 0, len = hds.length; i < len; i++){
7696                 var h = hds[i];
7697                 if(h[0] == id && h[1] == ename){
7698                     hd = h[2];
7699                     hds.splice(i, 1);
7700                     break;
7701                 }
7702             }
7703         }
7704         E.un(el, ename, hd);
7705         el = Roo.getDom(el);
7706         if(ename == "mousewheel" && el.addEventListener){
7707             el.removeEventListener("DOMMouseScroll", hd, false);
7708         }
7709         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7710             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7711         }
7712     };
7713
7714     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7715     
7716     var pub = {
7717         
7718         
7719         /** 
7720          * Fix for doc tools
7721          * @scope Roo.EventManager
7722          */
7723         
7724         
7725         /** 
7726          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7727          * object with a Roo.EventObject
7728          * @param {Function} fn        The method the event invokes
7729          * @param {Object}   scope    An object that becomes the scope of the handler
7730          * @param {boolean}  override If true, the obj passed in becomes
7731          *                             the execution scope of the listener
7732          * @return {Function} The wrapped function
7733          * @deprecated
7734          */
7735         wrap : function(fn, scope, override){
7736             return function(e){
7737                 Roo.EventObject.setEvent(e);
7738                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7739             };
7740         },
7741         
7742         /**
7743      * Appends an event handler to an element (shorthand for addListener)
7744      * @param {String/HTMLElement}   element        The html element or id to assign the
7745      * @param {String}   eventName The type of event to listen for
7746      * @param {Function} handler The method the event invokes
7747      * @param {Object}   scope (optional) The scope in which to execute the handler
7748      * function. The handler function's "this" context.
7749      * @param {Object}   options (optional) An object containing handler configuration
7750      * properties. This may contain any of the following properties:<ul>
7751      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7752      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7753      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7754      * <li>preventDefault {Boolean} True to prevent the default action</li>
7755      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7756      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7757      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7758      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7759      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7760      * by the specified number of milliseconds. If the event fires again within that time, the original
7761      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7762      * </ul><br>
7763      * <p>
7764      * <b>Combining Options</b><br>
7765      * Using the options argument, it is possible to combine different types of listeners:<br>
7766      * <br>
7767      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7768      * Code:<pre><code>
7769 el.on('click', this.onClick, this, {
7770     single: true,
7771     delay: 100,
7772     stopEvent : true,
7773     forumId: 4
7774 });</code></pre>
7775      * <p>
7776      * <b>Attaching multiple handlers in 1 call</b><br>
7777       * The method also allows for a single argument to be passed which is a config object containing properties
7778      * which specify multiple handlers.
7779      * <p>
7780      * Code:<pre><code>
7781 el.on({
7782     'click' : {
7783         fn: this.onClick
7784         scope: this,
7785         delay: 100
7786     },
7787     'mouseover' : {
7788         fn: this.onMouseOver
7789         scope: this
7790     },
7791     'mouseout' : {
7792         fn: this.onMouseOut
7793         scope: this
7794     }
7795 });</code></pre>
7796      * <p>
7797      * Or a shorthand syntax:<br>
7798      * Code:<pre><code>
7799 el.on({
7800     'click' : this.onClick,
7801     'mouseover' : this.onMouseOver,
7802     'mouseout' : this.onMouseOut
7803     scope: this
7804 });</code></pre>
7805      */
7806         addListener : function(element, eventName, fn, scope, options){
7807             if(typeof eventName == "object"){
7808                 var o = eventName;
7809                 for(var e in o){
7810                     if(propRe.test(e)){
7811                         continue;
7812                     }
7813                     if(typeof o[e] == "function"){
7814                         // shared options
7815                         listen(element, e, o, o[e], o.scope);
7816                     }else{
7817                         // individual options
7818                         listen(element, e, o[e]);
7819                     }
7820                 }
7821                 return;
7822             }
7823             return listen(element, eventName, options, fn, scope);
7824         },
7825         
7826         /**
7827          * Removes an event handler
7828          *
7829          * @param {String/HTMLElement}   element        The id or html element to remove the 
7830          *                             event from
7831          * @param {String}   eventName     The type of event
7832          * @param {Function} fn
7833          * @return {Boolean} True if a listener was actually removed
7834          */
7835         removeListener : function(element, eventName, fn){
7836             return stopListening(element, eventName, fn);
7837         },
7838         
7839         /**
7840          * Fires when the document is ready (before onload and before images are loaded). Can be 
7841          * accessed shorthanded Roo.onReady().
7842          * @param {Function} fn        The method the event invokes
7843          * @param {Object}   scope    An  object that becomes the scope of the handler
7844          * @param {boolean}  options
7845          */
7846         onDocumentReady : function(fn, scope, options){
7847             if(docReadyState){ // if it already fired
7848                 docReadyEvent.addListener(fn, scope, options);
7849                 docReadyEvent.fire();
7850                 docReadyEvent.clearListeners();
7851                 return;
7852             }
7853             if(!docReadyEvent){
7854                 initDocReady();
7855             }
7856             docReadyEvent.addListener(fn, scope, options);
7857         },
7858         
7859         /**
7860          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7861          * @param {Function} fn        The method the event invokes
7862          * @param {Object}   scope    An object that becomes the scope of the handler
7863          * @param {boolean}  options
7864          */
7865         onWindowResize : function(fn, scope, options)
7866         {
7867             if(!resizeEvent){
7868                 resizeEvent = new Roo.util.Event();
7869                 resizeTask = new Roo.util.DelayedTask(function(){
7870                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7871                 });
7872                 E.on(window, "resize", function()
7873                 {
7874                     if (Roo.isIE) {
7875                         resizeTask.delay(50);
7876                     } else {
7877                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7878                     }
7879                 });
7880             }
7881             resizeEvent.addListener(fn, scope, options);
7882         },
7883
7884         /**
7885          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7886          * @param {Function} fn        The method the event invokes
7887          * @param {Object}   scope    An object that becomes the scope of the handler
7888          * @param {boolean}  options
7889          */
7890         onTextResize : function(fn, scope, options){
7891             if(!textEvent){
7892                 textEvent = new Roo.util.Event();
7893                 var textEl = new Roo.Element(document.createElement('div'));
7894                 textEl.dom.className = 'x-text-resize';
7895                 textEl.dom.innerHTML = 'X';
7896                 textEl.appendTo(document.body);
7897                 textSize = textEl.dom.offsetHeight;
7898                 setInterval(function(){
7899                     if(textEl.dom.offsetHeight != textSize){
7900                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7901                     }
7902                 }, this.textResizeInterval);
7903             }
7904             textEvent.addListener(fn, scope, options);
7905         },
7906
7907         /**
7908          * Removes the passed window resize listener.
7909          * @param {Function} fn        The method the event invokes
7910          * @param {Object}   scope    The scope of handler
7911          */
7912         removeResizeListener : function(fn, scope){
7913             if(resizeEvent){
7914                 resizeEvent.removeListener(fn, scope);
7915             }
7916         },
7917
7918         // private
7919         fireResize : function(){
7920             if(resizeEvent){
7921                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7922             }   
7923         },
7924         /**
7925          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7926          */
7927         ieDeferSrc : false,
7928         /**
7929          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7930          */
7931         textResizeInterval : 50
7932     };
7933     
7934     /**
7935      * Fix for doc tools
7936      * @scopeAlias pub=Roo.EventManager
7937      */
7938     
7939      /**
7940      * Appends an event handler to an element (shorthand for addListener)
7941      * @param {String/HTMLElement}   element        The html element or id to assign the
7942      * @param {String}   eventName The type of event to listen for
7943      * @param {Function} handler The method the event invokes
7944      * @param {Object}   scope (optional) The scope in which to execute the handler
7945      * function. The handler function's "this" context.
7946      * @param {Object}   options (optional) An object containing handler configuration
7947      * properties. This may contain any of the following properties:<ul>
7948      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7949      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7950      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7951      * <li>preventDefault {Boolean} True to prevent the default action</li>
7952      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7953      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7954      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7955      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7956      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7957      * by the specified number of milliseconds. If the event fires again within that time, the original
7958      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7959      * </ul><br>
7960      * <p>
7961      * <b>Combining Options</b><br>
7962      * Using the options argument, it is possible to combine different types of listeners:<br>
7963      * <br>
7964      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7965      * Code:<pre><code>
7966 el.on('click', this.onClick, this, {
7967     single: true,
7968     delay: 100,
7969     stopEvent : true,
7970     forumId: 4
7971 });</code></pre>
7972      * <p>
7973      * <b>Attaching multiple handlers in 1 call</b><br>
7974       * The method also allows for a single argument to be passed which is a config object containing properties
7975      * which specify multiple handlers.
7976      * <p>
7977      * Code:<pre><code>
7978 el.on({
7979     'click' : {
7980         fn: this.onClick
7981         scope: this,
7982         delay: 100
7983     },
7984     'mouseover' : {
7985         fn: this.onMouseOver
7986         scope: this
7987     },
7988     'mouseout' : {
7989         fn: this.onMouseOut
7990         scope: this
7991     }
7992 });</code></pre>
7993      * <p>
7994      * Or a shorthand syntax:<br>
7995      * Code:<pre><code>
7996 el.on({
7997     'click' : this.onClick,
7998     'mouseover' : this.onMouseOver,
7999     'mouseout' : this.onMouseOut
8000     scope: this
8001 });</code></pre>
8002      */
8003     pub.on = pub.addListener;
8004     pub.un = pub.removeListener;
8005
8006     pub.stoppedMouseDownEvent = new Roo.util.Event();
8007     return pub;
8008 }();
8009 /**
8010   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
8011   * @param {Function} fn        The method the event invokes
8012   * @param {Object}   scope    An  object that becomes the scope of the handler
8013   * @param {boolean}  override If true, the obj passed in becomes
8014   *                             the execution scope of the listener
8015   * @member Roo
8016   * @method onReady
8017  */
8018 Roo.onReady = Roo.EventManager.onDocumentReady;
8019
8020 Roo.onReady(function(){
8021     var bd = Roo.get(document.body);
8022     if(!bd){ return; }
8023
8024     var cls = [
8025             Roo.isIE ? "roo-ie"
8026             : Roo.isIE11 ? "roo-ie11"
8027             : Roo.isEdge ? "roo-edge"
8028             : Roo.isGecko ? "roo-gecko"
8029             : Roo.isOpera ? "roo-opera"
8030             : Roo.isSafari ? "roo-safari" : ""];
8031
8032     if(Roo.isMac){
8033         cls.push("roo-mac");
8034     }
8035     if(Roo.isLinux){
8036         cls.push("roo-linux");
8037     }
8038     if(Roo.isIOS){
8039         cls.push("roo-ios");
8040     }
8041     if(Roo.isTouch){
8042         cls.push("roo-touch");
8043     }
8044     if(Roo.isBorderBox){
8045         cls.push('roo-border-box');
8046     }
8047     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
8048         var p = bd.dom.parentNode;
8049         if(p){
8050             p.className += ' roo-strict';
8051         }
8052     }
8053     bd.addClass(cls.join(' '));
8054 });
8055
8056 /**
8057  * @class Roo.EventObject
8058  * EventObject exposes the Yahoo! UI Event functionality directly on the object
8059  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
8060  * Example:
8061  * <pre><code>
8062  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
8063     e.preventDefault();
8064     var target = e.getTarget();
8065     ...
8066  }
8067  var myDiv = Roo.get("myDiv");
8068  myDiv.on("click", handleClick);
8069  //or
8070  Roo.EventManager.on("myDiv", 'click', handleClick);
8071  Roo.EventManager.addListener("myDiv", 'click', handleClick);
8072  </code></pre>
8073  * @static
8074  */
8075 Roo.EventObject = function(){
8076     
8077     var E = Roo.lib.Event;
8078     
8079     // safari keypress events for special keys return bad keycodes
8080     var safariKeys = {
8081         63234 : 37, // left
8082         63235 : 39, // right
8083         63232 : 38, // up
8084         63233 : 40, // down
8085         63276 : 33, // page up
8086         63277 : 34, // page down
8087         63272 : 46, // delete
8088         63273 : 36, // home
8089         63275 : 35  // end
8090     };
8091
8092     // normalize button clicks
8093     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
8094                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
8095
8096     Roo.EventObjectImpl = function(e){
8097         if(e){
8098             this.setEvent(e.browserEvent || e);
8099         }
8100     };
8101     Roo.EventObjectImpl.prototype = {
8102         /**
8103          * Used to fix doc tools.
8104          * @scope Roo.EventObject.prototype
8105          */
8106             
8107
8108         
8109         
8110         /** The normal browser event */
8111         browserEvent : null,
8112         /** The button pressed in a mouse event */
8113         button : -1,
8114         /** True if the shift key was down during the event */
8115         shiftKey : false,
8116         /** True if the control key was down during the event */
8117         ctrlKey : false,
8118         /** True if the alt key was down during the event */
8119         altKey : false,
8120
8121         /** Key constant 
8122         * @type Number */
8123         BACKSPACE : 8,
8124         /** Key constant 
8125         * @type Number */
8126         TAB : 9,
8127         /** Key constant 
8128         * @type Number */
8129         RETURN : 13,
8130         /** Key constant 
8131         * @type Number */
8132         ENTER : 13,
8133         /** Key constant 
8134         * @type Number */
8135         SHIFT : 16,
8136         /** Key constant 
8137         * @type Number */
8138         CONTROL : 17,
8139         /** Key constant 
8140         * @type Number */
8141         ESC : 27,
8142         /** Key constant 
8143         * @type Number */
8144         SPACE : 32,
8145         /** Key constant 
8146         * @type Number */
8147         PAGEUP : 33,
8148         /** Key constant 
8149         * @type Number */
8150         PAGEDOWN : 34,
8151         /** Key constant 
8152         * @type Number */
8153         END : 35,
8154         /** Key constant 
8155         * @type Number */
8156         HOME : 36,
8157         /** Key constant 
8158         * @type Number */
8159         LEFT : 37,
8160         /** Key constant 
8161         * @type Number */
8162         UP : 38,
8163         /** Key constant 
8164         * @type Number */
8165         RIGHT : 39,
8166         /** Key constant 
8167         * @type Number */
8168         DOWN : 40,
8169         /** Key constant 
8170         * @type Number */
8171         DELETE : 46,
8172         /** Key constant 
8173         * @type Number */
8174         F5 : 116,
8175
8176            /** @private */
8177         setEvent : function(e){
8178             if(e == this || (e && e.browserEvent)){ // already wrapped
8179                 return e;
8180             }
8181             this.browserEvent = e;
8182             if(e){
8183                 // normalize buttons
8184                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8185                 if(e.type == 'click' && this.button == -1){
8186                     this.button = 0;
8187                 }
8188                 this.type = e.type;
8189                 this.shiftKey = e.shiftKey;
8190                 // mac metaKey behaves like ctrlKey
8191                 this.ctrlKey = e.ctrlKey || e.metaKey;
8192                 this.altKey = e.altKey;
8193                 // in getKey these will be normalized for the mac
8194                 this.keyCode = e.keyCode;
8195                 // keyup warnings on firefox.
8196                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8197                 // cache the target for the delayed and or buffered events
8198                 this.target = E.getTarget(e);
8199                 // same for XY
8200                 this.xy = E.getXY(e);
8201             }else{
8202                 this.button = -1;
8203                 this.shiftKey = false;
8204                 this.ctrlKey = false;
8205                 this.altKey = false;
8206                 this.keyCode = 0;
8207                 this.charCode =0;
8208                 this.target = null;
8209                 this.xy = [0, 0];
8210             }
8211             return this;
8212         },
8213
8214         /**
8215          * Stop the event (preventDefault and stopPropagation)
8216          */
8217         stopEvent : function(){
8218             if(this.browserEvent){
8219                 if(this.browserEvent.type == 'mousedown'){
8220                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8221                 }
8222                 E.stopEvent(this.browserEvent);
8223             }
8224         },
8225
8226         /**
8227          * Prevents the browsers default handling of the event.
8228          */
8229         preventDefault : function(){
8230             if(this.browserEvent){
8231                 E.preventDefault(this.browserEvent);
8232             }
8233         },
8234
8235         /** @private */
8236         isNavKeyPress : function(){
8237             var k = this.keyCode;
8238             k = Roo.isSafari ? (safariKeys[k] || k) : k;
8239             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8240         },
8241
8242         isSpecialKey : function(){
8243             var k = this.keyCode;
8244             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
8245             (k == 16) || (k == 17) ||
8246             (k >= 18 && k <= 20) ||
8247             (k >= 33 && k <= 35) ||
8248             (k >= 36 && k <= 39) ||
8249             (k >= 44 && k <= 45);
8250         },
8251         /**
8252          * Cancels bubbling of the event.
8253          */
8254         stopPropagation : function(){
8255             if(this.browserEvent){
8256                 if(this.type == 'mousedown'){
8257                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8258                 }
8259                 E.stopPropagation(this.browserEvent);
8260             }
8261         },
8262
8263         /**
8264          * Gets the key code for the event.
8265          * @return {Number}
8266          */
8267         getCharCode : function(){
8268             return this.charCode || this.keyCode;
8269         },
8270
8271         /**
8272          * Returns a normalized keyCode for the event.
8273          * @return {Number} The key code
8274          */
8275         getKey : function(){
8276             var k = this.keyCode || this.charCode;
8277             return Roo.isSafari ? (safariKeys[k] || k) : k;
8278         },
8279
8280         /**
8281          * Gets the x coordinate of the event.
8282          * @return {Number}
8283          */
8284         getPageX : function(){
8285             return this.xy[0];
8286         },
8287
8288         /**
8289          * Gets the y coordinate of the event.
8290          * @return {Number}
8291          */
8292         getPageY : function(){
8293             return this.xy[1];
8294         },
8295
8296         /**
8297          * Gets the time of the event.
8298          * @return {Number}
8299          */
8300         getTime : function(){
8301             if(this.browserEvent){
8302                 return E.getTime(this.browserEvent);
8303             }
8304             return null;
8305         },
8306
8307         /**
8308          * Gets the page coordinates of the event.
8309          * @return {Array} The xy values like [x, y]
8310          */
8311         getXY : function(){
8312             return this.xy;
8313         },
8314
8315         /**
8316          * Gets the target for the event.
8317          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8318          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8319                 search as a number or element (defaults to 10 || document.body)
8320          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8321          * @return {HTMLelement}
8322          */
8323         getTarget : function(selector, maxDepth, returnEl){
8324             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8325         },
8326         /**
8327          * Gets the related target.
8328          * @return {HTMLElement}
8329          */
8330         getRelatedTarget : function(){
8331             if(this.browserEvent){
8332                 return E.getRelatedTarget(this.browserEvent);
8333             }
8334             return null;
8335         },
8336
8337         /**
8338          * Normalizes mouse wheel delta across browsers
8339          * @return {Number} The delta
8340          */
8341         getWheelDelta : function(){
8342             var e = this.browserEvent;
8343             var delta = 0;
8344             if(e.wheelDelta){ /* IE/Opera. */
8345                 delta = e.wheelDelta/120;
8346             }else if(e.detail){ /* Mozilla case. */
8347                 delta = -e.detail/3;
8348             }
8349             return delta;
8350         },
8351
8352         /**
8353          * Returns true if the control, meta, shift or alt key was pressed during this event.
8354          * @return {Boolean}
8355          */
8356         hasModifier : function(){
8357             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8358         },
8359
8360         /**
8361          * Returns true if the target of this event equals el or is a child of el
8362          * @param {String/HTMLElement/Element} el
8363          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8364          * @return {Boolean}
8365          */
8366         within : function(el, related){
8367             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8368             return t && Roo.fly(el).contains(t);
8369         },
8370
8371         getPoint : function(){
8372             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8373         }
8374     };
8375
8376     return new Roo.EventObjectImpl();
8377 }();
8378             
8379     /*
8380  * Based on:
8381  * Ext JS Library 1.1.1
8382  * Copyright(c) 2006-2007, Ext JS, LLC.
8383  *
8384  * Originally Released Under LGPL - original licence link has changed is not relivant.
8385  *
8386  * Fork - LGPL
8387  * <script type="text/javascript">
8388  */
8389
8390  
8391 // was in Composite Element!??!?!
8392  
8393 (function(){
8394     var D = Roo.lib.Dom;
8395     var E = Roo.lib.Event;
8396     var A = Roo.lib.Anim;
8397
8398     // local style camelizing for speed
8399     var propCache = {};
8400     var camelRe = /(-[a-z])/gi;
8401     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8402     var view = document.defaultView;
8403
8404 /**
8405  * @class Roo.Element
8406  * Represents an Element in the DOM.<br><br>
8407  * Usage:<br>
8408 <pre><code>
8409 var el = Roo.get("my-div");
8410
8411 // or with getEl
8412 var el = getEl("my-div");
8413
8414 // or with a DOM element
8415 var el = Roo.get(myDivElement);
8416 </code></pre>
8417  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8418  * each call instead of constructing a new one.<br><br>
8419  * <b>Animations</b><br />
8420  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8421  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8422 <pre>
8423 Option    Default   Description
8424 --------- --------  ---------------------------------------------
8425 duration  .35       The duration of the animation in seconds
8426 easing    easeOut   The YUI easing method
8427 callback  none      A function to execute when the anim completes
8428 scope     this      The scope (this) of the callback function
8429 </pre>
8430 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8431 * manipulate the animation. Here's an example:
8432 <pre><code>
8433 var el = Roo.get("my-div");
8434
8435 // no animation
8436 el.setWidth(100);
8437
8438 // default animation
8439 el.setWidth(100, true);
8440
8441 // animation with some options set
8442 el.setWidth(100, {
8443     duration: 1,
8444     callback: this.foo,
8445     scope: this
8446 });
8447
8448 // using the "anim" property to get the Anim object
8449 var opt = {
8450     duration: 1,
8451     callback: this.foo,
8452     scope: this
8453 };
8454 el.setWidth(100, opt);
8455 ...
8456 if(opt.anim.isAnimated()){
8457     opt.anim.stop();
8458 }
8459 </code></pre>
8460 * <b> Composite (Collections of) Elements</b><br />
8461  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8462  * @constructor Create a new Element directly.
8463  * @param {String/HTMLElement} element
8464  * @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).
8465  */
8466     Roo.Element = function(element, forceNew)
8467     {
8468         var dom = typeof element == "string" ?
8469                 document.getElementById(element) : element;
8470         
8471         this.listeners = {};
8472         
8473         if(!dom){ // invalid id/element
8474             return null;
8475         }
8476         var id = dom.id;
8477         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8478             return Roo.Element.cache[id];
8479         }
8480
8481         /**
8482          * The DOM element
8483          * @type HTMLElement
8484          */
8485         this.dom = dom;
8486
8487         /**
8488          * The DOM element ID
8489          * @type String
8490          */
8491         this.id = id || Roo.id(dom);
8492         
8493         return this; // assumed for cctor?
8494     };
8495
8496     var El = Roo.Element;
8497
8498     El.prototype = {
8499         /**
8500          * The element's default display mode  (defaults to "") 
8501          * @type String
8502          */
8503         originalDisplay : "",
8504
8505         
8506         // note this is overridden in BS version..
8507         visibilityMode : 1, 
8508         /**
8509          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8510          * @type String
8511          */
8512         defaultUnit : "px",
8513         
8514         /**
8515          * Sets the element's visibility mode. When setVisible() is called it
8516          * will use this to determine whether to set the visibility or the display property.
8517          * @param visMode Element.VISIBILITY or Element.DISPLAY
8518          * @return {Roo.Element} this
8519          */
8520         setVisibilityMode : function(visMode){
8521             this.visibilityMode = visMode;
8522             return this;
8523         },
8524         /**
8525          * Convenience method for setVisibilityMode(Element.DISPLAY)
8526          * @param {String} display (optional) What to set display to when visible
8527          * @return {Roo.Element} this
8528          */
8529         enableDisplayMode : function(display){
8530             this.setVisibilityMode(El.DISPLAY);
8531             if(typeof display != "undefined") { this.originalDisplay = display; }
8532             return this;
8533         },
8534
8535         /**
8536          * 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)
8537          * @param {String} selector The simple selector to test
8538          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8539                 search as a number or element (defaults to 10 || document.body)
8540          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8541          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8542          */
8543         findParent : function(simpleSelector, maxDepth, returnEl){
8544             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8545             maxDepth = maxDepth || 50;
8546             if(typeof maxDepth != "number"){
8547                 stopEl = Roo.getDom(maxDepth);
8548                 maxDepth = 10;
8549             }
8550             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8551                 if(dq.is(p, simpleSelector)){
8552                     return returnEl ? Roo.get(p) : p;
8553                 }
8554                 depth++;
8555                 p = p.parentNode;
8556             }
8557             return null;
8558         },
8559
8560
8561         /**
8562          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8563          * @param {String} selector The simple selector to test
8564          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8565                 search as a number or element (defaults to 10 || document.body)
8566          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8567          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8568          */
8569         findParentNode : function(simpleSelector, maxDepth, returnEl){
8570             var p = Roo.fly(this.dom.parentNode, '_internal');
8571             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8572         },
8573         
8574         /**
8575          * Looks at  the scrollable parent element
8576          */
8577         findScrollableParent : function()
8578         {
8579             var overflowRegex = /(auto|scroll)/;
8580             
8581             if(this.getStyle('position') === 'fixed'){
8582                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8583             }
8584             
8585             var excludeStaticParent = this.getStyle('position') === "absolute";
8586             
8587             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8588                 
8589                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8590                     continue;
8591                 }
8592                 
8593                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8594                     return parent;
8595                 }
8596                 
8597                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8598                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8599                 }
8600             }
8601             
8602             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8603         },
8604
8605         /**
8606          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8607          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8608          * @param {String} selector The simple selector to test
8609          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8610                 search as a number or element (defaults to 10 || document.body)
8611          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8612          */
8613         up : function(simpleSelector, maxDepth){
8614             return this.findParentNode(simpleSelector, maxDepth, true);
8615         },
8616
8617
8618
8619         /**
8620          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8621          * @param {String} selector The simple selector to test
8622          * @return {Boolean} True if this element matches the selector, else false
8623          */
8624         is : function(simpleSelector){
8625             return Roo.DomQuery.is(this.dom, simpleSelector);
8626         },
8627
8628         /**
8629          * Perform animation on this element.
8630          * @param {Object} args The YUI animation control args
8631          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8632          * @param {Function} onComplete (optional) Function to call when animation completes
8633          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8634          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8635          * @return {Roo.Element} this
8636          */
8637         animate : function(args, duration, onComplete, easing, animType){
8638             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8639             return this;
8640         },
8641
8642         /*
8643          * @private Internal animation call
8644          */
8645         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8646             animType = animType || 'run';
8647             opt = opt || {};
8648             var anim = Roo.lib.Anim[animType](
8649                 this.dom, args,
8650                 (opt.duration || defaultDur) || .35,
8651                 (opt.easing || defaultEase) || 'easeOut',
8652                 function(){
8653                     Roo.callback(cb, this);
8654                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8655                 },
8656                 this
8657             );
8658             opt.anim = anim;
8659             return anim;
8660         },
8661
8662         // private legacy anim prep
8663         preanim : function(a, i){
8664             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8665         },
8666
8667         /**
8668          * Removes worthless text nodes
8669          * @param {Boolean} forceReclean (optional) By default the element
8670          * keeps track if it has been cleaned already so
8671          * you can call this over and over. However, if you update the element and
8672          * need to force a reclean, you can pass true.
8673          */
8674         clean : function(forceReclean){
8675             if(this.isCleaned && forceReclean !== true){
8676                 return this;
8677             }
8678             var ns = /\S/;
8679             var d = this.dom, n = d.firstChild, ni = -1;
8680             while(n){
8681                 var nx = n.nextSibling;
8682                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8683                     d.removeChild(n);
8684                 }else{
8685                     n.nodeIndex = ++ni;
8686                 }
8687                 n = nx;
8688             }
8689             this.isCleaned = true;
8690             return this;
8691         },
8692
8693         // private
8694         calcOffsetsTo : function(el){
8695             el = Roo.get(el);
8696             var d = el.dom;
8697             var restorePos = false;
8698             if(el.getStyle('position') == 'static'){
8699                 el.position('relative');
8700                 restorePos = true;
8701             }
8702             var x = 0, y =0;
8703             var op = this.dom;
8704             while(op && op != d && op.tagName != 'HTML'){
8705                 x+= op.offsetLeft;
8706                 y+= op.offsetTop;
8707                 op = op.offsetParent;
8708             }
8709             if(restorePos){
8710                 el.position('static');
8711             }
8712             return [x, y];
8713         },
8714
8715         /**
8716          * Scrolls this element into view within the passed container.
8717          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8718          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8719          * @return {Roo.Element} this
8720          */
8721         scrollIntoView : function(container, hscroll){
8722             var c = Roo.getDom(container) || document.body;
8723             var el = this.dom;
8724
8725             var o = this.calcOffsetsTo(c),
8726                 l = o[0],
8727                 t = o[1],
8728                 b = t+el.offsetHeight,
8729                 r = l+el.offsetWidth;
8730
8731             var ch = c.clientHeight;
8732             var ct = parseInt(c.scrollTop, 10);
8733             var cl = parseInt(c.scrollLeft, 10);
8734             var cb = ct + ch;
8735             var cr = cl + c.clientWidth;
8736
8737             if(t < ct){
8738                 c.scrollTop = t;
8739             }else if(b > cb){
8740                 c.scrollTop = b-ch;
8741             }
8742
8743             if(hscroll !== false){
8744                 if(l < cl){
8745                     c.scrollLeft = l;
8746                 }else if(r > cr){
8747                     c.scrollLeft = r-c.clientWidth;
8748                 }
8749             }
8750             return this;
8751         },
8752
8753         // private
8754         scrollChildIntoView : function(child, hscroll){
8755             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8756         },
8757
8758         /**
8759          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8760          * the new height may not be available immediately.
8761          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8762          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8763          * @param {Function} onComplete (optional) Function to call when animation completes
8764          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8765          * @return {Roo.Element} this
8766          */
8767         autoHeight : function(animate, duration, onComplete, easing){
8768             var oldHeight = this.getHeight();
8769             this.clip();
8770             this.setHeight(1); // force clipping
8771             setTimeout(function(){
8772                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8773                 if(!animate){
8774                     this.setHeight(height);
8775                     this.unclip();
8776                     if(typeof onComplete == "function"){
8777                         onComplete();
8778                     }
8779                 }else{
8780                     this.setHeight(oldHeight); // restore original height
8781                     this.setHeight(height, animate, duration, function(){
8782                         this.unclip();
8783                         if(typeof onComplete == "function") { onComplete(); }
8784                     }.createDelegate(this), easing);
8785                 }
8786             }.createDelegate(this), 0);
8787             return this;
8788         },
8789
8790         /**
8791          * Returns true if this element is an ancestor of the passed element
8792          * @param {HTMLElement/String} el The element to check
8793          * @return {Boolean} True if this element is an ancestor of el, else false
8794          */
8795         contains : function(el){
8796             if(!el){return false;}
8797             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8798         },
8799
8800         /**
8801          * Checks whether the element is currently visible using both visibility and display properties.
8802          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8803          * @return {Boolean} True if the element is currently visible, else false
8804          */
8805         isVisible : function(deep) {
8806             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8807             if(deep !== true || !vis){
8808                 return vis;
8809             }
8810             var p = this.dom.parentNode;
8811             while(p && p.tagName.toLowerCase() != "body"){
8812                 if(!Roo.fly(p, '_isVisible').isVisible()){
8813                     return false;
8814                 }
8815                 p = p.parentNode;
8816             }
8817             return true;
8818         },
8819
8820         /**
8821          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8822          * @param {String} selector The CSS selector
8823          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8824          * @return {CompositeElement/CompositeElementLite} The composite element
8825          */
8826         select : function(selector, unique){
8827             return El.select(selector, unique, this.dom);
8828         },
8829
8830         /**
8831          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8832          * @param {String} selector The CSS selector
8833          * @return {Array} An array of the matched nodes
8834          */
8835         query : function(selector, unique){
8836             return Roo.DomQuery.select(selector, this.dom);
8837         },
8838
8839         /**
8840          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8841          * @param {String} selector The CSS selector
8842          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8843          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8844          */
8845         child : function(selector, returnDom){
8846             var n = Roo.DomQuery.selectNode(selector, this.dom);
8847             return returnDom ? n : Roo.get(n);
8848         },
8849
8850         /**
8851          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8852          * @param {String} selector The CSS selector
8853          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8854          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8855          */
8856         down : function(selector, returnDom){
8857             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8858             return returnDom ? n : Roo.get(n);
8859         },
8860
8861         /**
8862          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8863          * @param {String} group The group the DD object is member of
8864          * @param {Object} config The DD config object
8865          * @param {Object} overrides An object containing methods to override/implement on the DD object
8866          * @return {Roo.dd.DD} The DD object
8867          */
8868         initDD : function(group, config, overrides){
8869             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8870             return Roo.apply(dd, overrides);
8871         },
8872
8873         /**
8874          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8875          * @param {String} group The group the DDProxy object is member of
8876          * @param {Object} config The DDProxy config object
8877          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8878          * @return {Roo.dd.DDProxy} The DDProxy object
8879          */
8880         initDDProxy : function(group, config, overrides){
8881             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8882             return Roo.apply(dd, overrides);
8883         },
8884
8885         /**
8886          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8887          * @param {String} group The group the DDTarget object is member of
8888          * @param {Object} config The DDTarget config object
8889          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8890          * @return {Roo.dd.DDTarget} The DDTarget object
8891          */
8892         initDDTarget : function(group, config, overrides){
8893             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8894             return Roo.apply(dd, overrides);
8895         },
8896
8897         /**
8898          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8899          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8900          * @param {Boolean} visible Whether the element is visible
8901          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8902          * @return {Roo.Element} this
8903          */
8904          setVisible : function(visible, animate){
8905             if(!animate || !A){
8906                 if(this.visibilityMode == El.DISPLAY){
8907                     this.setDisplayed(visible);
8908                 }else{
8909                     this.fixDisplay();
8910                     this.dom.style.visibility = visible ? "visible" : "hidden";
8911                 }
8912             }else{
8913                 // closure for composites
8914                 var dom = this.dom;
8915                 var visMode = this.visibilityMode;
8916                 if(visible){
8917                     this.setOpacity(.01);
8918                     this.setVisible(true);
8919                 }
8920                 this.anim({opacity: { to: (visible?1:0) }},
8921                       this.preanim(arguments, 1),
8922                       null, .35, 'easeIn', function(){
8923                          if(!visible){
8924                              if(visMode == El.DISPLAY){
8925                                  dom.style.display = "none";
8926                              }else{
8927                                  dom.style.visibility = "hidden";
8928                              }
8929                              Roo.get(dom).setOpacity(1);
8930                          }
8931                      });
8932             }
8933             return this;
8934         },
8935
8936         /**
8937          * Returns true if display is not "none"
8938          * @return {Boolean}
8939          */
8940         isDisplayed : function() {
8941             return this.getStyle("display") != "none";
8942         },
8943
8944         /**
8945          * Toggles the element's visibility or display, depending on visibility mode.
8946          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8947          * @return {Roo.Element} this
8948          */
8949         toggle : function(animate){
8950             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8951             return this;
8952         },
8953
8954         /**
8955          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8956          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8957          * @return {Roo.Element} this
8958          */
8959         setDisplayed : function(value) {
8960             if(typeof value == "boolean"){
8961                value = value ? this.originalDisplay : "none";
8962             }
8963             this.setStyle("display", value);
8964             return this;
8965         },
8966
8967         /**
8968          * Tries to focus the element. Any exceptions are caught and ignored.
8969          * @return {Roo.Element} this
8970          */
8971         focus : function() {
8972             try{
8973                 this.dom.focus();
8974             }catch(e){}
8975             return this;
8976         },
8977
8978         /**
8979          * Tries to blur the element. Any exceptions are caught and ignored.
8980          * @return {Roo.Element} this
8981          */
8982         blur : function() {
8983             try{
8984                 this.dom.blur();
8985             }catch(e){}
8986             return this;
8987         },
8988
8989         /**
8990          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8991          * @param {String/Array} className The CSS class to add, or an array of classes
8992          * @return {Roo.Element} this
8993          */
8994         addClass : function(className){
8995             if(className instanceof Array){
8996                 for(var i = 0, len = className.length; i < len; i++) {
8997                     this.addClass(className[i]);
8998                 }
8999             }else{
9000                 if(className && !this.hasClass(className)){
9001                     if (this.dom instanceof SVGElement) {
9002                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
9003                     } else {
9004                         this.dom.className = this.dom.className + " " + className;
9005                     }
9006                 }
9007             }
9008             return this;
9009         },
9010
9011         /**
9012          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
9013          * @param {String/Array} className The CSS class to add, or an array of classes
9014          * @return {Roo.Element} this
9015          */
9016         radioClass : function(className){
9017             var siblings = this.dom.parentNode.childNodes;
9018             for(var i = 0; i < siblings.length; i++) {
9019                 var s = siblings[i];
9020                 if(s.nodeType == 1){
9021                     Roo.get(s).removeClass(className);
9022                 }
9023             }
9024             this.addClass(className);
9025             return this;
9026         },
9027
9028         /**
9029          * Removes one or more CSS classes from the element.
9030          * @param {String/Array} className The CSS class to remove, or an array of classes
9031          * @return {Roo.Element} this
9032          */
9033         removeClass : function(className){
9034             
9035             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
9036             if(!className || !cn){
9037                 return this;
9038             }
9039             if(className instanceof Array){
9040                 for(var i = 0, len = className.length; i < len; i++) {
9041                     this.removeClass(className[i]);
9042                 }
9043             }else{
9044                 if(this.hasClass(className)){
9045                     var re = this.classReCache[className];
9046                     if (!re) {
9047                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
9048                        this.classReCache[className] = re;
9049                     }
9050                     if (this.dom instanceof SVGElement) {
9051                         this.dom.className.baseVal = cn.replace(re, " ");
9052                     } else {
9053                         this.dom.className = cn.replace(re, " ");
9054                     }
9055                 }
9056             }
9057             return this;
9058         },
9059
9060         // private
9061         classReCache: {},
9062
9063         /**
9064          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
9065          * @param {String} className The CSS class to toggle
9066          * @return {Roo.Element} this
9067          */
9068         toggleClass : function(className){
9069             if(this.hasClass(className)){
9070                 this.removeClass(className);
9071             }else{
9072                 this.addClass(className);
9073             }
9074             return this;
9075         },
9076
9077         /**
9078          * Checks if the specified CSS class exists on this element's DOM node.
9079          * @param {String} className The CSS class to check for
9080          * @return {Boolean} True if the class exists, else false
9081          */
9082         hasClass : function(className){
9083             if (this.dom instanceof SVGElement) {
9084                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
9085             } 
9086             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
9087         },
9088
9089         /**
9090          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
9091          * @param {String} oldClassName The CSS class to replace
9092          * @param {String} newClassName The replacement CSS class
9093          * @return {Roo.Element} this
9094          */
9095         replaceClass : function(oldClassName, newClassName){
9096             this.removeClass(oldClassName);
9097             this.addClass(newClassName);
9098             return this;
9099         },
9100
9101         /**
9102          * Returns an object with properties matching the styles requested.
9103          * For example, el.getStyles('color', 'font-size', 'width') might return
9104          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
9105          * @param {String} style1 A style name
9106          * @param {String} style2 A style name
9107          * @param {String} etc.
9108          * @return {Object} The style object
9109          */
9110         getStyles : function(){
9111             var a = arguments, len = a.length, r = {};
9112             for(var i = 0; i < len; i++){
9113                 r[a[i]] = this.getStyle(a[i]);
9114             }
9115             return r;
9116         },
9117
9118         /**
9119          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
9120          * @param {String} property The style property whose value is returned.
9121          * @return {String} The current value of the style property for this element.
9122          */
9123         getStyle : function(){
9124             return view && view.getComputedStyle ?
9125                 function(prop){
9126                     var el = this.dom, v, cs, camel;
9127                     if(prop == 'float'){
9128                         prop = "cssFloat";
9129                     }
9130                     if(el.style && (v = el.style[prop])){
9131                         return v;
9132                     }
9133                     if(cs = view.getComputedStyle(el, "")){
9134                         if(!(camel = propCache[prop])){
9135                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
9136                         }
9137                         return cs[camel];
9138                     }
9139                     return null;
9140                 } :
9141                 function(prop){
9142                     var el = this.dom, v, cs, camel;
9143                     if(prop == 'opacity'){
9144                         if(typeof el.style.filter == 'string'){
9145                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
9146                             if(m){
9147                                 var fv = parseFloat(m[1]);
9148                                 if(!isNaN(fv)){
9149                                     return fv ? fv / 100 : 0;
9150                                 }
9151                             }
9152                         }
9153                         return 1;
9154                     }else if(prop == 'float'){
9155                         prop = "styleFloat";
9156                     }
9157                     if(!(camel = propCache[prop])){
9158                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
9159                     }
9160                     if(v = el.style[camel]){
9161                         return v;
9162                     }
9163                     if(cs = el.currentStyle){
9164                         return cs[camel];
9165                     }
9166                     return null;
9167                 };
9168         }(),
9169
9170         /**
9171          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
9172          * @param {String/Object} property The style property to be set, or an object of multiple styles.
9173          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
9174          * @return {Roo.Element} this
9175          */
9176         setStyle : function(prop, value){
9177             if(typeof prop == "string"){
9178                 
9179                 if (prop == 'float') {
9180                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
9181                     return this;
9182                 }
9183                 
9184                 var camel;
9185                 if(!(camel = propCache[prop])){
9186                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
9187                 }
9188                 
9189                 if(camel == 'opacity') {
9190                     this.setOpacity(value);
9191                 }else{
9192                     this.dom.style[camel] = value;
9193                 }
9194             }else{
9195                 for(var style in prop){
9196                     if(typeof prop[style] != "function"){
9197                        this.setStyle(style, prop[style]);
9198                     }
9199                 }
9200             }
9201             return this;
9202         },
9203
9204         /**
9205          * More flexible version of {@link #setStyle} for setting style properties.
9206          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9207          * a function which returns such a specification.
9208          * @return {Roo.Element} this
9209          */
9210         applyStyles : function(style){
9211             Roo.DomHelper.applyStyles(this.dom, style);
9212             return this;
9213         },
9214
9215         /**
9216           * 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).
9217           * @return {Number} The X position of the element
9218           */
9219         getX : function(){
9220             return D.getX(this.dom);
9221         },
9222
9223         /**
9224           * 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).
9225           * @return {Number} The Y position of the element
9226           */
9227         getY : function(){
9228             return D.getY(this.dom);
9229         },
9230
9231         /**
9232           * 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).
9233           * @return {Array} The XY position of the element
9234           */
9235         getXY : function(){
9236             return D.getXY(this.dom);
9237         },
9238
9239         /**
9240          * 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).
9241          * @param {Number} The X position of the element
9242          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9243          * @return {Roo.Element} this
9244          */
9245         setX : function(x, animate){
9246             if(!animate || !A){
9247                 D.setX(this.dom, x);
9248             }else{
9249                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9250             }
9251             return this;
9252         },
9253
9254         /**
9255          * 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).
9256          * @param {Number} The Y position of the element
9257          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9258          * @return {Roo.Element} this
9259          */
9260         setY : function(y, animate){
9261             if(!animate || !A){
9262                 D.setY(this.dom, y);
9263             }else{
9264                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9265             }
9266             return this;
9267         },
9268
9269         /**
9270          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9271          * @param {String} left The left CSS property value
9272          * @return {Roo.Element} this
9273          */
9274         setLeft : function(left){
9275             this.setStyle("left", this.addUnits(left));
9276             return this;
9277         },
9278
9279         /**
9280          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9281          * @param {String} top The top CSS property value
9282          * @return {Roo.Element} this
9283          */
9284         setTop : function(top){
9285             this.setStyle("top", this.addUnits(top));
9286             return this;
9287         },
9288
9289         /**
9290          * Sets the element's CSS right style.
9291          * @param {String} right The right CSS property value
9292          * @return {Roo.Element} this
9293          */
9294         setRight : function(right){
9295             this.setStyle("right", this.addUnits(right));
9296             return this;
9297         },
9298
9299         /**
9300          * Sets the element's CSS bottom style.
9301          * @param {String} bottom The bottom CSS property value
9302          * @return {Roo.Element} this
9303          */
9304         setBottom : function(bottom){
9305             this.setStyle("bottom", this.addUnits(bottom));
9306             return this;
9307         },
9308
9309         /**
9310          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9311          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9312          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9313          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9314          * @return {Roo.Element} this
9315          */
9316         setXY : function(pos, animate){
9317             if(!animate || !A){
9318                 D.setXY(this.dom, pos);
9319             }else{
9320                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9321             }
9322             return this;
9323         },
9324
9325         /**
9326          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9327          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9328          * @param {Number} x X value for new position (coordinates are page-based)
9329          * @param {Number} y Y value for new position (coordinates are page-based)
9330          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9331          * @return {Roo.Element} this
9332          */
9333         setLocation : function(x, y, animate){
9334             this.setXY([x, y], this.preanim(arguments, 2));
9335             return this;
9336         },
9337
9338         /**
9339          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9340          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9341          * @param {Number} x X value for new position (coordinates are page-based)
9342          * @param {Number} y Y value for new position (coordinates are page-based)
9343          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9344          * @return {Roo.Element} this
9345          */
9346         moveTo : function(x, y, animate){
9347             this.setXY([x, y], this.preanim(arguments, 2));
9348             return this;
9349         },
9350
9351         /**
9352          * Returns the region of the given element.
9353          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9354          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9355          */
9356         getRegion : function(){
9357             return D.getRegion(this.dom);
9358         },
9359
9360         /**
9361          * Returns the offset height of the element
9362          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9363          * @return {Number} The element's height
9364          */
9365         getHeight : function(contentHeight){
9366             var h = this.dom.offsetHeight || 0;
9367             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9368         },
9369
9370         /**
9371          * Returns the offset width of the element
9372          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9373          * @return {Number} The element's width
9374          */
9375         getWidth : function(contentWidth){
9376             var w = this.dom.offsetWidth || 0;
9377             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9378         },
9379
9380         /**
9381          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9382          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9383          * if a height has not been set using CSS.
9384          * @return {Number}
9385          */
9386         getComputedHeight : function(){
9387             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9388             if(!h){
9389                 h = parseInt(this.getStyle('height'), 10) || 0;
9390                 if(!this.isBorderBox()){
9391                     h += this.getFrameWidth('tb');
9392                 }
9393             }
9394             return h;
9395         },
9396
9397         /**
9398          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9399          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9400          * if a width has not been set using CSS.
9401          * @return {Number}
9402          */
9403         getComputedWidth : function(){
9404             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9405             if(!w){
9406                 w = parseInt(this.getStyle('width'), 10) || 0;
9407                 if(!this.isBorderBox()){
9408                     w += this.getFrameWidth('lr');
9409                 }
9410             }
9411             return w;
9412         },
9413
9414         /**
9415          * Returns the size of the element.
9416          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9417          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9418          */
9419         getSize : function(contentSize){
9420             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9421         },
9422
9423         /**
9424          * Returns the width and height of the viewport.
9425          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9426          */
9427         getViewSize : function(){
9428             var d = this.dom, doc = document, aw = 0, ah = 0;
9429             if(d == doc || d == doc.body){
9430                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9431             }else{
9432                 return {
9433                     width : d.clientWidth,
9434                     height: d.clientHeight
9435                 };
9436             }
9437         },
9438
9439         /**
9440          * Returns the value of the "value" attribute
9441          * @param {Boolean} asNumber true to parse the value as a number
9442          * @return {String/Number}
9443          */
9444         getValue : function(asNumber){
9445             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9446         },
9447
9448         // private
9449         adjustWidth : function(width){
9450             if(typeof width == "number"){
9451                 if(this.autoBoxAdjust && !this.isBorderBox()){
9452                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9453                 }
9454                 if(width < 0){
9455                     width = 0;
9456                 }
9457             }
9458             return width;
9459         },
9460
9461         // private
9462         adjustHeight : function(height){
9463             if(typeof height == "number"){
9464                if(this.autoBoxAdjust && !this.isBorderBox()){
9465                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9466                }
9467                if(height < 0){
9468                    height = 0;
9469                }
9470             }
9471             return height;
9472         },
9473
9474         /**
9475          * Set the width of the element
9476          * @param {Number} width The new width
9477          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9478          * @return {Roo.Element} this
9479          */
9480         setWidth : function(width, animate){
9481             width = this.adjustWidth(width);
9482             if(!animate || !A){
9483                 this.dom.style.width = this.addUnits(width);
9484             }else{
9485                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9486             }
9487             return this;
9488         },
9489
9490         /**
9491          * Set the height of the element
9492          * @param {Number} height The new height
9493          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9494          * @return {Roo.Element} this
9495          */
9496          setHeight : function(height, animate){
9497             height = this.adjustHeight(height);
9498             if(!animate || !A){
9499                 this.dom.style.height = this.addUnits(height);
9500             }else{
9501                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9502             }
9503             return this;
9504         },
9505
9506         /**
9507          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9508          * @param {Number} width The new width
9509          * @param {Number} height The new height
9510          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9511          * @return {Roo.Element} this
9512          */
9513          setSize : function(width, height, animate){
9514             if(typeof width == "object"){ // in case of object from getSize()
9515                 height = width.height; width = width.width;
9516             }
9517             width = this.adjustWidth(width); height = this.adjustHeight(height);
9518             if(!animate || !A){
9519                 this.dom.style.width = this.addUnits(width);
9520                 this.dom.style.height = this.addUnits(height);
9521             }else{
9522                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9523             }
9524             return this;
9525         },
9526
9527         /**
9528          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9529          * @param {Number} x X value for new position (coordinates are page-based)
9530          * @param {Number} y Y value for new position (coordinates are page-based)
9531          * @param {Number} width The new width
9532          * @param {Number} height The new height
9533          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9534          * @return {Roo.Element} this
9535          */
9536         setBounds : function(x, y, width, height, animate){
9537             if(!animate || !A){
9538                 this.setSize(width, height);
9539                 this.setLocation(x, y);
9540             }else{
9541                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9542                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9543                               this.preanim(arguments, 4), 'motion');
9544             }
9545             return this;
9546         },
9547
9548         /**
9549          * 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.
9550          * @param {Roo.lib.Region} region The region to fill
9551          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9552          * @return {Roo.Element} this
9553          */
9554         setRegion : function(region, animate){
9555             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9556             return this;
9557         },
9558
9559         /**
9560          * Appends an event handler
9561          *
9562          * @param {String}   eventName     The type of event to append
9563          * @param {Function} fn        The method the event invokes
9564          * @param {Object} scope       (optional) The scope (this object) of the fn
9565          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9566          */
9567         addListener : function(eventName, fn, scope, options)
9568         {
9569             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9570                 this.addListener('touchstart', this.onTapHandler, this);
9571             }
9572             
9573             // we need to handle a special case where dom element is a svg element.
9574             // in this case we do not actua
9575             if (!this.dom) {
9576                 return;
9577             }
9578             
9579             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9580                 if (typeof(this.listeners[eventName]) == 'undefined') {
9581                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9582                 }
9583                 this.listeners[eventName].addListener(fn, scope, options);
9584                 return;
9585             }
9586             
9587                 
9588             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9589             
9590             
9591         },
9592         tapedTwice : false,
9593         onTapHandler : function(event)
9594         {
9595             if(!this.tapedTwice) {
9596                 this.tapedTwice = true;
9597                 var s = this;
9598                 setTimeout( function() {
9599                     s.tapedTwice = false;
9600                 }, 300 );
9601                 return;
9602             }
9603             event.preventDefault();
9604             var revent = new MouseEvent('dblclick',  {
9605                 view: window,
9606                 bubbles: true,
9607                 cancelable: true
9608             });
9609              
9610             this.dom.dispatchEvent(revent);
9611             //action on double tap goes below
9612              
9613         }, 
9614  
9615         /**
9616          * Removes an event handler from this element
9617          * @param {String} eventName the type of event to remove
9618          * @param {Function} fn the method the event invokes
9619          * @param {Function} scope (needed for svg fake listeners)
9620          * @return {Roo.Element} this
9621          */
9622         removeListener : function(eventName, fn, scope){
9623             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9624             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9625                 return this;
9626             }
9627             this.listeners[eventName].removeListener(fn, scope);
9628             return this;
9629         },
9630
9631         /**
9632          * Removes all previous added listeners from this element
9633          * @return {Roo.Element} this
9634          */
9635         removeAllListeners : function(){
9636             E.purgeElement(this.dom);
9637             this.listeners = {};
9638             return this;
9639         },
9640
9641         relayEvent : function(eventName, observable){
9642             this.on(eventName, function(e){
9643                 observable.fireEvent(eventName, e);
9644             });
9645         },
9646
9647         
9648         /**
9649          * Set the opacity of the element
9650          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9651          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9652          * @return {Roo.Element} this
9653          */
9654          setOpacity : function(opacity, animate){
9655             if(!animate || !A){
9656                 var s = this.dom.style;
9657                 if(Roo.isIE){
9658                     s.zoom = 1;
9659                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9660                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9661                 }else{
9662                     s.opacity = opacity;
9663                 }
9664             }else{
9665                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9666             }
9667             return this;
9668         },
9669
9670         /**
9671          * Gets the left X coordinate
9672          * @param {Boolean} local True to get the local css position instead of page coordinate
9673          * @return {Number}
9674          */
9675         getLeft : function(local){
9676             if(!local){
9677                 return this.getX();
9678             }else{
9679                 return parseInt(this.getStyle("left"), 10) || 0;
9680             }
9681         },
9682
9683         /**
9684          * Gets the right X coordinate of the element (element X position + element width)
9685          * @param {Boolean} local True to get the local css position instead of page coordinate
9686          * @return {Number}
9687          */
9688         getRight : function(local){
9689             if(!local){
9690                 return this.getX() + this.getWidth();
9691             }else{
9692                 return (this.getLeft(true) + this.getWidth()) || 0;
9693             }
9694         },
9695
9696         /**
9697          * Gets the top Y coordinate
9698          * @param {Boolean} local True to get the local css position instead of page coordinate
9699          * @return {Number}
9700          */
9701         getTop : function(local) {
9702             if(!local){
9703                 return this.getY();
9704             }else{
9705                 return parseInt(this.getStyle("top"), 10) || 0;
9706             }
9707         },
9708
9709         /**
9710          * Gets the bottom Y coordinate of the element (element Y position + element height)
9711          * @param {Boolean} local True to get the local css position instead of page coordinate
9712          * @return {Number}
9713          */
9714         getBottom : function(local){
9715             if(!local){
9716                 return this.getY() + this.getHeight();
9717             }else{
9718                 return (this.getTop(true) + this.getHeight()) || 0;
9719             }
9720         },
9721
9722         /**
9723         * Initializes positioning on this element. If a desired position is not passed, it will make the
9724         * the element positioned relative IF it is not already positioned.
9725         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9726         * @param {Number} zIndex (optional) The zIndex to apply
9727         * @param {Number} x (optional) Set the page X position
9728         * @param {Number} y (optional) Set the page Y position
9729         */
9730         position : function(pos, zIndex, x, y){
9731             if(!pos){
9732                if(this.getStyle('position') == 'static'){
9733                    this.setStyle('position', 'relative');
9734                }
9735             }else{
9736                 this.setStyle("position", pos);
9737             }
9738             if(zIndex){
9739                 this.setStyle("z-index", zIndex);
9740             }
9741             if(x !== undefined && y !== undefined){
9742                 this.setXY([x, y]);
9743             }else if(x !== undefined){
9744                 this.setX(x);
9745             }else if(y !== undefined){
9746                 this.setY(y);
9747             }
9748         },
9749
9750         /**
9751         * Clear positioning back to the default when the document was loaded
9752         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9753         * @return {Roo.Element} this
9754          */
9755         clearPositioning : function(value){
9756             value = value ||'';
9757             this.setStyle({
9758                 "left": value,
9759                 "right": value,
9760                 "top": value,
9761                 "bottom": value,
9762                 "z-index": "",
9763                 "position" : "static"
9764             });
9765             return this;
9766         },
9767
9768         /**
9769         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9770         * snapshot before performing an update and then restoring the element.
9771         * @return {Object}
9772         */
9773         getPositioning : function(){
9774             var l = this.getStyle("left");
9775             var t = this.getStyle("top");
9776             return {
9777                 "position" : this.getStyle("position"),
9778                 "left" : l,
9779                 "right" : l ? "" : this.getStyle("right"),
9780                 "top" : t,
9781                 "bottom" : t ? "" : this.getStyle("bottom"),
9782                 "z-index" : this.getStyle("z-index")
9783             };
9784         },
9785
9786         /**
9787          * Gets the width of the border(s) for the specified side(s)
9788          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9789          * passing lr would get the border (l)eft width + the border (r)ight width.
9790          * @return {Number} The width of the sides passed added together
9791          */
9792         getBorderWidth : function(side){
9793             return this.addStyles(side, El.borders);
9794         },
9795
9796         /**
9797          * Gets the width of the padding(s) for the specified side(s)
9798          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9799          * passing lr would get the padding (l)eft + the padding (r)ight.
9800          * @return {Number} The padding of the sides passed added together
9801          */
9802         getPadding : function(side){
9803             return this.addStyles(side, El.paddings);
9804         },
9805
9806         /**
9807         * Set positioning with an object returned by getPositioning().
9808         * @param {Object} posCfg
9809         * @return {Roo.Element} this
9810          */
9811         setPositioning : function(pc){
9812             this.applyStyles(pc);
9813             if(pc.right == "auto"){
9814                 this.dom.style.right = "";
9815             }
9816             if(pc.bottom == "auto"){
9817                 this.dom.style.bottom = "";
9818             }
9819             return this;
9820         },
9821
9822         // private
9823         fixDisplay : function(){
9824             if(this.getStyle("display") == "none"){
9825                 this.setStyle("visibility", "hidden");
9826                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9827                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9828                     this.setStyle("display", "block");
9829                 }
9830             }
9831         },
9832
9833         /**
9834          * Quick set left and top adding default units
9835          * @param {String} left The left CSS property value
9836          * @param {String} top The top CSS property value
9837          * @return {Roo.Element} this
9838          */
9839          setLeftTop : function(left, top){
9840             this.dom.style.left = this.addUnits(left);
9841             this.dom.style.top = this.addUnits(top);
9842             return this;
9843         },
9844
9845         /**
9846          * Move this element relative to its current position.
9847          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9848          * @param {Number} distance How far to move the element in pixels
9849          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9850          * @return {Roo.Element} this
9851          */
9852          move : function(direction, distance, animate){
9853             var xy = this.getXY();
9854             direction = direction.toLowerCase();
9855             switch(direction){
9856                 case "l":
9857                 case "left":
9858                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9859                     break;
9860                case "r":
9861                case "right":
9862                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9863                     break;
9864                case "t":
9865                case "top":
9866                case "up":
9867                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9868                     break;
9869                case "b":
9870                case "bottom":
9871                case "down":
9872                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9873                     break;
9874             }
9875             return this;
9876         },
9877
9878         /**
9879          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9880          * @return {Roo.Element} this
9881          */
9882         clip : function(){
9883             if(!this.isClipped){
9884                this.isClipped = true;
9885                this.originalClip = {
9886                    "o": this.getStyle("overflow"),
9887                    "x": this.getStyle("overflow-x"),
9888                    "y": this.getStyle("overflow-y")
9889                };
9890                this.setStyle("overflow", "hidden");
9891                this.setStyle("overflow-x", "hidden");
9892                this.setStyle("overflow-y", "hidden");
9893             }
9894             return this;
9895         },
9896
9897         /**
9898          *  Return clipping (overflow) to original clipping before clip() was called
9899          * @return {Roo.Element} this
9900          */
9901         unclip : function(){
9902             if(this.isClipped){
9903                 this.isClipped = false;
9904                 var o = this.originalClip;
9905                 if(o.o){this.setStyle("overflow", o.o);}
9906                 if(o.x){this.setStyle("overflow-x", o.x);}
9907                 if(o.y){this.setStyle("overflow-y", o.y);}
9908             }
9909             return this;
9910         },
9911
9912
9913         /**
9914          * Gets the x,y coordinates specified by the anchor position on the element.
9915          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9916          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9917          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9918          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9919          * @return {Array} [x, y] An array containing the element's x and y coordinates
9920          */
9921         getAnchorXY : function(anchor, local, s){
9922             //Passing a different size is useful for pre-calculating anchors,
9923             //especially for anchored animations that change the el size.
9924
9925             var w, h, vp = false;
9926             if(!s){
9927                 var d = this.dom;
9928                 if(d == document.body || d == document){
9929                     vp = true;
9930                     w = D.getViewWidth(); h = D.getViewHeight();
9931                 }else{
9932                     w = this.getWidth(); h = this.getHeight();
9933                 }
9934             }else{
9935                 w = s.width;  h = s.height;
9936             }
9937             var x = 0, y = 0, r = Math.round;
9938             switch((anchor || "tl").toLowerCase()){
9939                 case "c":
9940                     x = r(w*.5);
9941                     y = r(h*.5);
9942                 break;
9943                 case "t":
9944                     x = r(w*.5);
9945                     y = 0;
9946                 break;
9947                 case "l":
9948                     x = 0;
9949                     y = r(h*.5);
9950                 break;
9951                 case "r":
9952                     x = w;
9953                     y = r(h*.5);
9954                 break;
9955                 case "b":
9956                     x = r(w*.5);
9957                     y = h;
9958                 break;
9959                 case "tl":
9960                     x = 0;
9961                     y = 0;
9962                 break;
9963                 case "bl":
9964                     x = 0;
9965                     y = h;
9966                 break;
9967                 case "br":
9968                     x = w;
9969                     y = h;
9970                 break;
9971                 case "tr":
9972                     x = w;
9973                     y = 0;
9974                 break;
9975             }
9976             if(local === true){
9977                 return [x, y];
9978             }
9979             if(vp){
9980                 var sc = this.getScroll();
9981                 return [x + sc.left, y + sc.top];
9982             }
9983             //Add the element's offset xy
9984             var o = this.getXY();
9985             return [x+o[0], y+o[1]];
9986         },
9987
9988         /**
9989          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9990          * supported position values.
9991          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9992          * @param {String} position The position to align to.
9993          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9994          * @return {Array} [x, y]
9995          */
9996         getAlignToXY : function(el, p, o)
9997         {
9998             el = Roo.get(el);
9999             var d = this.dom;
10000             if(!el.dom){
10001                 throw "Element.alignTo with an element that doesn't exist";
10002             }
10003             var c = false; //constrain to viewport
10004             var p1 = "", p2 = "";
10005             o = o || [0,0];
10006
10007             if(!p){
10008                 p = "tl-bl";
10009             }else if(p == "?"){
10010                 p = "tl-bl?";
10011             }else if(p.indexOf("-") == -1){
10012                 p = "tl-" + p;
10013             }
10014             p = p.toLowerCase();
10015             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
10016             if(!m){
10017                throw "Element.alignTo with an invalid alignment " + p;
10018             }
10019             p1 = m[1]; p2 = m[2]; c = !!m[3];
10020
10021             //Subtract the aligned el's internal xy from the target's offset xy
10022             //plus custom offset to get the aligned el's new offset xy
10023             var a1 = this.getAnchorXY(p1, true);
10024             var a2 = el.getAnchorXY(p2, false);
10025             var x = a2[0] - a1[0] + o[0];
10026             var y = a2[1] - a1[1] + o[1];
10027             if(c){
10028                 //constrain the aligned el to viewport if necessary
10029                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
10030                 // 5px of margin for ie
10031                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
10032
10033                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
10034                 //perpendicular to the vp border, allow the aligned el to slide on that border,
10035                 //otherwise swap the aligned el to the opposite border of the target.
10036                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
10037                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
10038                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
10039                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
10040
10041                var doc = document;
10042                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
10043                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
10044
10045                if((x+w) > dw + scrollX){
10046                     x = swapX ? r.left-w : dw+scrollX-w;
10047                 }
10048                if(x < scrollX){
10049                    x = swapX ? r.right : scrollX;
10050                }
10051                if((y+h) > dh + scrollY){
10052                     y = swapY ? r.top-h : dh+scrollY-h;
10053                 }
10054                if (y < scrollY){
10055                    y = swapY ? r.bottom : scrollY;
10056                }
10057             }
10058             return [x,y];
10059         },
10060
10061         // private
10062         getConstrainToXY : function(){
10063             var os = {top:0, left:0, bottom:0, right: 0};
10064
10065             return function(el, local, offsets, proposedXY){
10066                 el = Roo.get(el);
10067                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
10068
10069                 var vw, vh, vx = 0, vy = 0;
10070                 if(el.dom == document.body || el.dom == document){
10071                     vw = Roo.lib.Dom.getViewWidth();
10072                     vh = Roo.lib.Dom.getViewHeight();
10073                 }else{
10074                     vw = el.dom.clientWidth;
10075                     vh = el.dom.clientHeight;
10076                     if(!local){
10077                         var vxy = el.getXY();
10078                         vx = vxy[0];
10079                         vy = vxy[1];
10080                     }
10081                 }
10082
10083                 var s = el.getScroll();
10084
10085                 vx += offsets.left + s.left;
10086                 vy += offsets.top + s.top;
10087
10088                 vw -= offsets.right;
10089                 vh -= offsets.bottom;
10090
10091                 var vr = vx+vw;
10092                 var vb = vy+vh;
10093
10094                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
10095                 var x = xy[0], y = xy[1];
10096                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
10097
10098                 // only move it if it needs it
10099                 var moved = false;
10100
10101                 // first validate right/bottom
10102                 if((x + w) > vr){
10103                     x = vr - w;
10104                     moved = true;
10105                 }
10106                 if((y + h) > vb){
10107                     y = vb - h;
10108                     moved = true;
10109                 }
10110                 // then make sure top/left isn't negative
10111                 if(x < vx){
10112                     x = vx;
10113                     moved = true;
10114                 }
10115                 if(y < vy){
10116                     y = vy;
10117                     moved = true;
10118                 }
10119                 return moved ? [x, y] : false;
10120             };
10121         }(),
10122
10123         // private
10124         adjustForConstraints : function(xy, parent, offsets){
10125             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
10126         },
10127
10128         /**
10129          * Aligns this element with another element relative to the specified anchor points. If the other element is the
10130          * document it aligns it to the viewport.
10131          * The position parameter is optional, and can be specified in any one of the following formats:
10132          * <ul>
10133          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
10134          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
10135          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
10136          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
10137          *   <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
10138          *       element's anchor point, and the second value is used as the target's anchor point.</li>
10139          * </ul>
10140          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
10141          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
10142          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
10143          * that specified in order to enforce the viewport constraints.
10144          * Following are all of the supported anchor positions:
10145     <pre>
10146     Value  Description
10147     -----  -----------------------------
10148     tl     The top left corner (default)
10149     t      The center of the top edge
10150     tr     The top right corner
10151     l      The center of the left edge
10152     c      In the center of the element
10153     r      The center of the right edge
10154     bl     The bottom left corner
10155     b      The center of the bottom edge
10156     br     The bottom right corner
10157     </pre>
10158     Example Usage:
10159     <pre><code>
10160     // align el to other-el using the default positioning ("tl-bl", non-constrained)
10161     el.alignTo("other-el");
10162
10163     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
10164     el.alignTo("other-el", "tr?");
10165
10166     // align the bottom right corner of el with the center left edge of other-el
10167     el.alignTo("other-el", "br-l?");
10168
10169     // align the center of el with the bottom left corner of other-el and
10170     // adjust the x position by -6 pixels (and the y position by 0)
10171     el.alignTo("other-el", "c-bl", [-6, 0]);
10172     </code></pre>
10173          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10174          * @param {String} position The position to align to.
10175          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10176          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10177          * @return {Roo.Element} this
10178          */
10179         alignTo : function(element, position, offsets, animate){
10180             var xy = this.getAlignToXY(element, position, offsets);
10181             this.setXY(xy, this.preanim(arguments, 3));
10182             return this;
10183         },
10184
10185         /**
10186          * Anchors an element to another element and realigns it when the window is resized.
10187          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10188          * @param {String} position The position to align to.
10189          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10190          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10191          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10192          * is a number, it is used as the buffer delay (defaults to 50ms).
10193          * @param {Function} callback The function to call after the animation finishes
10194          * @return {Roo.Element} this
10195          */
10196         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10197             var action = function(){
10198                 this.alignTo(el, alignment, offsets, animate);
10199                 Roo.callback(callback, this);
10200             };
10201             Roo.EventManager.onWindowResize(action, this);
10202             var tm = typeof monitorScroll;
10203             if(tm != 'undefined'){
10204                 Roo.EventManager.on(window, 'scroll', action, this,
10205                     {buffer: tm == 'number' ? monitorScroll : 50});
10206             }
10207             action.call(this); // align immediately
10208             return this;
10209         },
10210         /**
10211          * Clears any opacity settings from this element. Required in some cases for IE.
10212          * @return {Roo.Element} this
10213          */
10214         clearOpacity : function(){
10215             if (window.ActiveXObject) {
10216                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10217                     this.dom.style.filter = "";
10218                 }
10219             } else {
10220                 this.dom.style.opacity = "";
10221                 this.dom.style["-moz-opacity"] = "";
10222                 this.dom.style["-khtml-opacity"] = "";
10223             }
10224             return this;
10225         },
10226
10227         /**
10228          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10229          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10230          * @return {Roo.Element} this
10231          */
10232         hide : function(animate){
10233             this.setVisible(false, this.preanim(arguments, 0));
10234             return this;
10235         },
10236
10237         /**
10238         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10239         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10240          * @return {Roo.Element} this
10241          */
10242         show : function(animate){
10243             this.setVisible(true, this.preanim(arguments, 0));
10244             return this;
10245         },
10246
10247         /**
10248          * @private Test if size has a unit, otherwise appends the default
10249          */
10250         addUnits : function(size){
10251             return Roo.Element.addUnits(size, this.defaultUnit);
10252         },
10253
10254         /**
10255          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10256          * @return {Roo.Element} this
10257          */
10258         beginMeasure : function(){
10259             var el = this.dom;
10260             if(el.offsetWidth || el.offsetHeight){
10261                 return this; // offsets work already
10262             }
10263             var changed = [];
10264             var p = this.dom, b = document.body; // start with this element
10265             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10266                 var pe = Roo.get(p);
10267                 if(pe.getStyle('display') == 'none'){
10268                     changed.push({el: p, visibility: pe.getStyle("visibility")});
10269                     p.style.visibility = "hidden";
10270                     p.style.display = "block";
10271                 }
10272                 p = p.parentNode;
10273             }
10274             this._measureChanged = changed;
10275             return this;
10276
10277         },
10278
10279         /**
10280          * Restores displays to before beginMeasure was called
10281          * @return {Roo.Element} this
10282          */
10283         endMeasure : function(){
10284             var changed = this._measureChanged;
10285             if(changed){
10286                 for(var i = 0, len = changed.length; i < len; i++) {
10287                     var r = changed[i];
10288                     r.el.style.visibility = r.visibility;
10289                     r.el.style.display = "none";
10290                 }
10291                 this._measureChanged = null;
10292             }
10293             return this;
10294         },
10295
10296         /**
10297         * Update the innerHTML of this element, optionally searching for and processing scripts
10298         * @param {String} html The new HTML
10299         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10300         * @param {Function} callback For async script loading you can be noticed when the update completes
10301         * @return {Roo.Element} this
10302          */
10303         update : function(html, loadScripts, callback){
10304             if(typeof html == "undefined"){
10305                 html = "";
10306             }
10307             if(loadScripts !== true){
10308                 this.dom.innerHTML = html;
10309                 if(typeof callback == "function"){
10310                     callback();
10311                 }
10312                 return this;
10313             }
10314             var id = Roo.id();
10315             var dom = this.dom;
10316
10317             html += '<span id="' + id + '"></span>';
10318
10319             E.onAvailable(id, function(){
10320                 var hd = document.getElementsByTagName("head")[0];
10321                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10322                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10323                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10324
10325                 var match;
10326                 while(match = re.exec(html)){
10327                     var attrs = match[1];
10328                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10329                     if(srcMatch && srcMatch[2]){
10330                        var s = document.createElement("script");
10331                        s.src = srcMatch[2];
10332                        var typeMatch = attrs.match(typeRe);
10333                        if(typeMatch && typeMatch[2]){
10334                            s.type = typeMatch[2];
10335                        }
10336                        hd.appendChild(s);
10337                     }else if(match[2] && match[2].length > 0){
10338                         if(window.execScript) {
10339                            window.execScript(match[2]);
10340                         } else {
10341                             /**
10342                              * eval:var:id
10343                              * eval:var:dom
10344                              * eval:var:html
10345                              * 
10346                              */
10347                            window.eval(match[2]);
10348                         }
10349                     }
10350                 }
10351                 var el = document.getElementById(id);
10352                 if(el){el.parentNode.removeChild(el);}
10353                 if(typeof callback == "function"){
10354                     callback();
10355                 }
10356             });
10357             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10358             return this;
10359         },
10360
10361         /**
10362          * Direct access to the UpdateManager update() method (takes the same parameters).
10363          * @param {String/Function} url The url for this request or a function to call to get the url
10364          * @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}
10365          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10366          * @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.
10367          * @return {Roo.Element} this
10368          */
10369         load : function(){
10370             var um = this.getUpdateManager();
10371             um.update.apply(um, arguments);
10372             return this;
10373         },
10374
10375         /**
10376         * Gets this element's UpdateManager
10377         * @return {Roo.UpdateManager} The UpdateManager
10378         */
10379         getUpdateManager : function(){
10380             if(!this.updateManager){
10381                 this.updateManager = new Roo.UpdateManager(this);
10382             }
10383             return this.updateManager;
10384         },
10385
10386         /**
10387          * Disables text selection for this element (normalized across browsers)
10388          * @return {Roo.Element} this
10389          */
10390         unselectable : function(){
10391             this.dom.unselectable = "on";
10392             this.swallowEvent("selectstart", true);
10393             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10394             this.addClass("x-unselectable");
10395             return this;
10396         },
10397
10398         /**
10399         * Calculates the x, y to center this element on the screen
10400         * @return {Array} The x, y values [x, y]
10401         */
10402         getCenterXY : function(){
10403             return this.getAlignToXY(document, 'c-c');
10404         },
10405
10406         /**
10407         * Centers the Element in either the viewport, or another Element.
10408         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10409         */
10410         center : function(centerIn){
10411             this.alignTo(centerIn || document, 'c-c');
10412             return this;
10413         },
10414
10415         /**
10416          * Tests various css rules/browsers to determine if this element uses a border box
10417          * @return {Boolean}
10418          */
10419         isBorderBox : function(){
10420             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10421         },
10422
10423         /**
10424          * Return a box {x, y, width, height} that can be used to set another elements
10425          * size/location to match this element.
10426          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10427          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10428          * @return {Object} box An object in the format {x, y, width, height}
10429          */
10430         getBox : function(contentBox, local){
10431             var xy;
10432             if(!local){
10433                 xy = this.getXY();
10434             }else{
10435                 var left = parseInt(this.getStyle("left"), 10) || 0;
10436                 var top = parseInt(this.getStyle("top"), 10) || 0;
10437                 xy = [left, top];
10438             }
10439             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10440             if(!contentBox){
10441                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10442             }else{
10443                 var l = this.getBorderWidth("l")+this.getPadding("l");
10444                 var r = this.getBorderWidth("r")+this.getPadding("r");
10445                 var t = this.getBorderWidth("t")+this.getPadding("t");
10446                 var b = this.getBorderWidth("b")+this.getPadding("b");
10447                 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)};
10448             }
10449             bx.right = bx.x + bx.width;
10450             bx.bottom = bx.y + bx.height;
10451             return bx;
10452         },
10453
10454         /**
10455          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10456          for more information about the sides.
10457          * @param {String} sides
10458          * @return {Number}
10459          */
10460         getFrameWidth : function(sides, onlyContentBox){
10461             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10462         },
10463
10464         /**
10465          * 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.
10466          * @param {Object} box The box to fill {x, y, width, height}
10467          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10468          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10469          * @return {Roo.Element} this
10470          */
10471         setBox : function(box, adjust, animate){
10472             var w = box.width, h = box.height;
10473             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10474                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10475                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10476             }
10477             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10478             return this;
10479         },
10480
10481         /**
10482          * Forces the browser to repaint this element
10483          * @return {Roo.Element} this
10484          */
10485          repaint : function(){
10486             var dom = this.dom;
10487             this.addClass("x-repaint");
10488             setTimeout(function(){
10489                 Roo.get(dom).removeClass("x-repaint");
10490             }, 1);
10491             return this;
10492         },
10493
10494         /**
10495          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10496          * then it returns the calculated width of the sides (see getPadding)
10497          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10498          * @return {Object/Number}
10499          */
10500         getMargins : function(side){
10501             if(!side){
10502                 return {
10503                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10504                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10505                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10506                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10507                 };
10508             }else{
10509                 return this.addStyles(side, El.margins);
10510              }
10511         },
10512
10513         // private
10514         addStyles : function(sides, styles){
10515             var val = 0, v, w;
10516             for(var i = 0, len = sides.length; i < len; i++){
10517                 v = this.getStyle(styles[sides.charAt(i)]);
10518                 if(v){
10519                      w = parseInt(v, 10);
10520                      if(w){ val += w; }
10521                 }
10522             }
10523             return val;
10524         },
10525
10526         /**
10527          * Creates a proxy element of this element
10528          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10529          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10530          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10531          * @return {Roo.Element} The new proxy element
10532          */
10533         createProxy : function(config, renderTo, matchBox){
10534             if(renderTo){
10535                 renderTo = Roo.getDom(renderTo);
10536             }else{
10537                 renderTo = document.body;
10538             }
10539             config = typeof config == "object" ?
10540                 config : {tag : "div", cls: config};
10541             var proxy = Roo.DomHelper.append(renderTo, config, true);
10542             if(matchBox){
10543                proxy.setBox(this.getBox());
10544             }
10545             return proxy;
10546         },
10547
10548         /**
10549          * Puts a mask over this element to disable user interaction. Requires core.css.
10550          * This method can only be applied to elements which accept child nodes.
10551          * @param {String} msg (optional) A message to display in the mask
10552          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10553          * @return {Element} The mask  element
10554          */
10555         mask : function(msg, msgCls)
10556         {
10557             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10558                 this.setStyle("position", "relative");
10559             }
10560             if(!this._mask){
10561                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10562             }
10563             
10564             this.addClass("x-masked");
10565             this._mask.setDisplayed(true);
10566             
10567             // we wander
10568             var z = 0;
10569             var dom = this.dom;
10570             while (dom && dom.style) {
10571                 if (!isNaN(parseInt(dom.style.zIndex))) {
10572                     z = Math.max(z, parseInt(dom.style.zIndex));
10573                 }
10574                 dom = dom.parentNode;
10575             }
10576             // if we are masking the body - then it hides everything..
10577             if (this.dom == document.body) {
10578                 z = 1000000;
10579                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10580                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10581             }
10582            
10583             if(typeof msg == 'string'){
10584                 if(!this._maskMsg){
10585                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10586                         cls: "roo-el-mask-msg", 
10587                         cn: [
10588                             {
10589                                 tag: 'i',
10590                                 cls: 'fa fa-spinner fa-spin'
10591                             },
10592                             {
10593                                 tag: 'div'
10594                             }   
10595                         ]
10596                     }, true);
10597                 }
10598                 var mm = this._maskMsg;
10599                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10600                 if (mm.dom.lastChild) { // weird IE issue?
10601                     mm.dom.lastChild.innerHTML = msg;
10602                 }
10603                 mm.setDisplayed(true);
10604                 mm.center(this);
10605                 mm.setStyle('z-index', z + 102);
10606             }
10607             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10608                 this._mask.setHeight(this.getHeight());
10609             }
10610             this._mask.setStyle('z-index', z + 100);
10611             
10612             return this._mask;
10613         },
10614
10615         /**
10616          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10617          * it is cached for reuse.
10618          */
10619         unmask : function(removeEl){
10620             if(this._mask){
10621                 if(removeEl === true){
10622                     this._mask.remove();
10623                     delete this._mask;
10624                     if(this._maskMsg){
10625                         this._maskMsg.remove();
10626                         delete this._maskMsg;
10627                     }
10628                 }else{
10629                     this._mask.setDisplayed(false);
10630                     if(this._maskMsg){
10631                         this._maskMsg.setDisplayed(false);
10632                     }
10633                 }
10634             }
10635             this.removeClass("x-masked");
10636         },
10637
10638         /**
10639          * Returns true if this element is masked
10640          * @return {Boolean}
10641          */
10642         isMasked : function(){
10643             return this._mask && this._mask.isVisible();
10644         },
10645
10646         /**
10647          * Creates an iframe shim for this element to keep selects and other windowed objects from
10648          * showing through.
10649          * @return {Roo.Element} The new shim element
10650          */
10651         createShim : function(){
10652             var el = document.createElement('iframe');
10653             el.frameBorder = 'no';
10654             el.className = 'roo-shim';
10655             if(Roo.isIE && Roo.isSecure){
10656                 el.src = Roo.SSL_SECURE_URL;
10657             }
10658             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10659             shim.autoBoxAdjust = false;
10660             return shim;
10661         },
10662
10663         /**
10664          * Removes this element from the DOM and deletes it from the cache
10665          */
10666         remove : function(){
10667             if(this.dom.parentNode){
10668                 this.dom.parentNode.removeChild(this.dom);
10669             }
10670             delete El.cache[this.dom.id];
10671         },
10672
10673         /**
10674          * Sets up event handlers to add and remove a css class when the mouse is over this element
10675          * @param {String} className
10676          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10677          * mouseout events for children elements
10678          * @return {Roo.Element} this
10679          */
10680         addClassOnOver : function(className, preventFlicker){
10681             this.on("mouseover", function(){
10682                 Roo.fly(this, '_internal').addClass(className);
10683             }, this.dom);
10684             var removeFn = function(e){
10685                 if(preventFlicker !== true || !e.within(this, true)){
10686                     Roo.fly(this, '_internal').removeClass(className);
10687                 }
10688             };
10689             this.on("mouseout", removeFn, this.dom);
10690             return this;
10691         },
10692
10693         /**
10694          * Sets up event handlers to add and remove a css class when this element has the focus
10695          * @param {String} className
10696          * @return {Roo.Element} this
10697          */
10698         addClassOnFocus : function(className){
10699             this.on("focus", function(){
10700                 Roo.fly(this, '_internal').addClass(className);
10701             }, this.dom);
10702             this.on("blur", function(){
10703                 Roo.fly(this, '_internal').removeClass(className);
10704             }, this.dom);
10705             return this;
10706         },
10707         /**
10708          * 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)
10709          * @param {String} className
10710          * @return {Roo.Element} this
10711          */
10712         addClassOnClick : function(className){
10713             var dom = this.dom;
10714             this.on("mousedown", function(){
10715                 Roo.fly(dom, '_internal').addClass(className);
10716                 var d = Roo.get(document);
10717                 var fn = function(){
10718                     Roo.fly(dom, '_internal').removeClass(className);
10719                     d.removeListener("mouseup", fn);
10720                 };
10721                 d.on("mouseup", fn);
10722             });
10723             return this;
10724         },
10725
10726         /**
10727          * Stops the specified event from bubbling and optionally prevents the default action
10728          * @param {String} eventName
10729          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10730          * @return {Roo.Element} this
10731          */
10732         swallowEvent : function(eventName, preventDefault){
10733             var fn = function(e){
10734                 e.stopPropagation();
10735                 if(preventDefault){
10736                     e.preventDefault();
10737                 }
10738             };
10739             if(eventName instanceof Array){
10740                 for(var i = 0, len = eventName.length; i < len; i++){
10741                      this.on(eventName[i], fn);
10742                 }
10743                 return this;
10744             }
10745             this.on(eventName, fn);
10746             return this;
10747         },
10748
10749         /**
10750          * @private
10751          */
10752         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10753
10754         /**
10755          * Sizes this element to its parent element's dimensions performing
10756          * neccessary box adjustments.
10757          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10758          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10759          * @return {Roo.Element} this
10760          */
10761         fitToParent : function(monitorResize, targetParent) {
10762           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10763           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10764           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10765             return this;
10766           }
10767           var p = Roo.get(targetParent || this.dom.parentNode);
10768           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10769           if (monitorResize === true) {
10770             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10771             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10772           }
10773           return this;
10774         },
10775
10776         /**
10777          * Gets the next sibling, skipping text nodes
10778          * @return {HTMLElement} The next sibling or null
10779          */
10780         getNextSibling : function(){
10781             var n = this.dom.nextSibling;
10782             while(n && n.nodeType != 1){
10783                 n = n.nextSibling;
10784             }
10785             return n;
10786         },
10787
10788         /**
10789          * Gets the previous sibling, skipping text nodes
10790          * @return {HTMLElement} The previous sibling or null
10791          */
10792         getPrevSibling : function(){
10793             var n = this.dom.previousSibling;
10794             while(n && n.nodeType != 1){
10795                 n = n.previousSibling;
10796             }
10797             return n;
10798         },
10799
10800
10801         /**
10802          * Appends the passed element(s) to this element
10803          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10804          * @return {Roo.Element} this
10805          */
10806         appendChild: function(el){
10807             el = Roo.get(el);
10808             el.appendTo(this);
10809             return this;
10810         },
10811
10812         /**
10813          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10814          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10815          * automatically generated with the specified attributes.
10816          * @param {HTMLElement} insertBefore (optional) a child element of this element
10817          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10818          * @return {Roo.Element} The new child element
10819          */
10820         createChild: function(config, insertBefore, returnDom){
10821             config = config || {tag:'div'};
10822             if(insertBefore){
10823                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10824             }
10825             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10826         },
10827
10828         /**
10829          * Appends this element to the passed element
10830          * @param {String/HTMLElement/Element} el The new parent element
10831          * @return {Roo.Element} this
10832          */
10833         appendTo: function(el){
10834             el = Roo.getDom(el);
10835             el.appendChild(this.dom);
10836             return this;
10837         },
10838
10839         /**
10840          * Inserts this element before the passed element in the DOM
10841          * @param {String/HTMLElement/Element} el The element to insert before
10842          * @return {Roo.Element} this
10843          */
10844         insertBefore: function(el){
10845             el = Roo.getDom(el);
10846             el.parentNode.insertBefore(this.dom, el);
10847             return this;
10848         },
10849
10850         /**
10851          * Inserts this element after the passed element in the DOM
10852          * @param {String/HTMLElement/Element} el The element to insert after
10853          * @return {Roo.Element} this
10854          */
10855         insertAfter: function(el){
10856             el = Roo.getDom(el);
10857             el.parentNode.insertBefore(this.dom, el.nextSibling);
10858             return this;
10859         },
10860
10861         /**
10862          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10863          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10864          * @return {Roo.Element} The new child
10865          */
10866         insertFirst: function(el, returnDom){
10867             el = el || {};
10868             if(typeof el == 'object' && !el.nodeType){ // dh config
10869                 return this.createChild(el, this.dom.firstChild, returnDom);
10870             }else{
10871                 el = Roo.getDom(el);
10872                 this.dom.insertBefore(el, this.dom.firstChild);
10873                 return !returnDom ? Roo.get(el) : el;
10874             }
10875         },
10876
10877         /**
10878          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10879          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10880          * @param {String} where (optional) 'before' or 'after' defaults to before
10881          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10882          * @return {Roo.Element} the inserted Element
10883          */
10884         insertSibling: function(el, where, returnDom){
10885             where = where ? where.toLowerCase() : 'before';
10886             el = el || {};
10887             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10888
10889             if(typeof el == 'object' && !el.nodeType){ // dh config
10890                 if(where == 'after' && !this.dom.nextSibling){
10891                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10892                 }else{
10893                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10894                 }
10895
10896             }else{
10897                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10898                             where == 'before' ? this.dom : this.dom.nextSibling);
10899                 if(!returnDom){
10900                     rt = Roo.get(rt);
10901                 }
10902             }
10903             return rt;
10904         },
10905
10906         /**
10907          * Creates and wraps this element with another element
10908          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10909          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10910          * @return {HTMLElement/Element} The newly created wrapper element
10911          */
10912         wrap: function(config, returnDom){
10913             if(!config){
10914                 config = {tag: "div"};
10915             }
10916             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10917             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10918             return newEl;
10919         },
10920
10921         /**
10922          * Replaces the passed element with this element
10923          * @param {String/HTMLElement/Element} el The element to replace
10924          * @return {Roo.Element} this
10925          */
10926         replace: function(el){
10927             el = Roo.get(el);
10928             this.insertBefore(el);
10929             el.remove();
10930             return this;
10931         },
10932
10933         /**
10934          * Inserts an html fragment into this element
10935          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10936          * @param {String} html The HTML fragment
10937          * @param {Boolean} returnEl True to return an Roo.Element
10938          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10939          */
10940         insertHtml : function(where, html, returnEl){
10941             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10942             return returnEl ? Roo.get(el) : el;
10943         },
10944
10945         /**
10946          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10947          * @param {Object} o The object with the attributes
10948          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10949          * @return {Roo.Element} this
10950          */
10951         set : function(o, useSet){
10952             var el = this.dom;
10953             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10954             for(var attr in o){
10955                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10956                 if(attr=="cls"){
10957                     el.className = o["cls"];
10958                 }else{
10959                     if(useSet) {
10960                         el.setAttribute(attr, o[attr]);
10961                     } else {
10962                         el[attr] = o[attr];
10963                     }
10964                 }
10965             }
10966             if(o.style){
10967                 Roo.DomHelper.applyStyles(el, o.style);
10968             }
10969             return this;
10970         },
10971
10972         /**
10973          * Convenience method for constructing a KeyMap
10974          * @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:
10975          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10976          * @param {Function} fn The function to call
10977          * @param {Object} scope (optional) The scope of the function
10978          * @return {Roo.KeyMap} The KeyMap created
10979          */
10980         addKeyListener : function(key, fn, scope){
10981             var config;
10982             if(typeof key != "object" || key instanceof Array){
10983                 config = {
10984                     key: key,
10985                     fn: fn,
10986                     scope: scope
10987                 };
10988             }else{
10989                 config = {
10990                     key : key.key,
10991                     shift : key.shift,
10992                     ctrl : key.ctrl,
10993                     alt : key.alt,
10994                     fn: fn,
10995                     scope: scope
10996                 };
10997             }
10998             return new Roo.KeyMap(this, config);
10999         },
11000
11001         /**
11002          * Creates a KeyMap for this element
11003          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
11004          * @return {Roo.KeyMap} The KeyMap created
11005          */
11006         addKeyMap : function(config){
11007             return new Roo.KeyMap(this, config);
11008         },
11009
11010         /**
11011          * Returns true if this element is scrollable.
11012          * @return {Boolean}
11013          */
11014          isScrollable : function(){
11015             var dom = this.dom;
11016             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
11017         },
11018
11019         /**
11020          * 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().
11021          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
11022          * @param {Number} value The new scroll value
11023          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11024          * @return {Element} this
11025          */
11026
11027         scrollTo : function(side, value, animate){
11028             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
11029             if(!animate || !A){
11030                 this.dom[prop] = value;
11031             }else{
11032                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
11033                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
11034             }
11035             return this;
11036         },
11037
11038         /**
11039          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
11040          * within this element's scrollable range.
11041          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
11042          * @param {Number} distance How far to scroll the element in pixels
11043          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11044          * @return {Boolean} Returns true if a scroll was triggered or false if the element
11045          * was scrolled as far as it could go.
11046          */
11047          scroll : function(direction, distance, animate){
11048              if(!this.isScrollable()){
11049                  return;
11050              }
11051              var el = this.dom;
11052              var l = el.scrollLeft, t = el.scrollTop;
11053              var w = el.scrollWidth, h = el.scrollHeight;
11054              var cw = el.clientWidth, ch = el.clientHeight;
11055              direction = direction.toLowerCase();
11056              var scrolled = false;
11057              var a = this.preanim(arguments, 2);
11058              switch(direction){
11059                  case "l":
11060                  case "left":
11061                      if(w - l > cw){
11062                          var v = Math.min(l + distance, w-cw);
11063                          this.scrollTo("left", v, a);
11064                          scrolled = true;
11065                      }
11066                      break;
11067                 case "r":
11068                 case "right":
11069                      if(l > 0){
11070                          var v = Math.max(l - distance, 0);
11071                          this.scrollTo("left", v, a);
11072                          scrolled = true;
11073                      }
11074                      break;
11075                 case "t":
11076                 case "top":
11077                 case "up":
11078                      if(t > 0){
11079                          var v = Math.max(t - distance, 0);
11080                          this.scrollTo("top", v, a);
11081                          scrolled = true;
11082                      }
11083                      break;
11084                 case "b":
11085                 case "bottom":
11086                 case "down":
11087                      if(h - t > ch){
11088                          var v = Math.min(t + distance, h-ch);
11089                          this.scrollTo("top", v, a);
11090                          scrolled = true;
11091                      }
11092                      break;
11093              }
11094              return scrolled;
11095         },
11096
11097         /**
11098          * Translates the passed page coordinates into left/top css values for this element
11099          * @param {Number/Array} x The page x or an array containing [x, y]
11100          * @param {Number} y The page y
11101          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
11102          */
11103         translatePoints : function(x, y){
11104             if(typeof x == 'object' || x instanceof Array){
11105                 y = x[1]; x = x[0];
11106             }
11107             var p = this.getStyle('position');
11108             var o = this.getXY();
11109
11110             var l = parseInt(this.getStyle('left'), 10);
11111             var t = parseInt(this.getStyle('top'), 10);
11112
11113             if(isNaN(l)){
11114                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
11115             }
11116             if(isNaN(t)){
11117                 t = (p == "relative") ? 0 : this.dom.offsetTop;
11118             }
11119
11120             return {left: (x - o[0] + l), top: (y - o[1] + t)};
11121         },
11122
11123         /**
11124          * Returns the current scroll position of the element.
11125          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
11126          */
11127         getScroll : function(){
11128             var d = this.dom, doc = document;
11129             if(d == doc || d == doc.body){
11130                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
11131                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
11132                 return {left: l, top: t};
11133             }else{
11134                 return {left: d.scrollLeft, top: d.scrollTop};
11135             }
11136         },
11137
11138         /**
11139          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
11140          * are convert to standard 6 digit hex color.
11141          * @param {String} attr The css attribute
11142          * @param {String} defaultValue The default value to use when a valid color isn't found
11143          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
11144          * YUI color anims.
11145          */
11146         getColor : function(attr, defaultValue, prefix){
11147             var v = this.getStyle(attr);
11148             if(!v || v == "transparent" || v == "inherit") {
11149                 return defaultValue;
11150             }
11151             var color = typeof prefix == "undefined" ? "#" : prefix;
11152             if(v.substr(0, 4) == "rgb("){
11153                 var rvs = v.slice(4, v.length -1).split(",");
11154                 for(var i = 0; i < 3; i++){
11155                     var h = parseInt(rvs[i]).toString(16);
11156                     if(h < 16){
11157                         h = "0" + h;
11158                     }
11159                     color += h;
11160                 }
11161             } else {
11162                 if(v.substr(0, 1) == "#"){
11163                     if(v.length == 4) {
11164                         for(var i = 1; i < 4; i++){
11165                             var c = v.charAt(i);
11166                             color +=  c + c;
11167                         }
11168                     }else if(v.length == 7){
11169                         color += v.substr(1);
11170                     }
11171                 }
11172             }
11173             return(color.length > 5 ? color.toLowerCase() : defaultValue);
11174         },
11175
11176         /**
11177          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11178          * gradient background, rounded corners and a 4-way shadow.
11179          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11180          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11181          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11182          * @return {Roo.Element} this
11183          */
11184         boxWrap : function(cls){
11185             cls = cls || 'x-box';
11186             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11187             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11188             return el;
11189         },
11190
11191         /**
11192          * Returns the value of a namespaced attribute from the element's underlying DOM node.
11193          * @param {String} namespace The namespace in which to look for the attribute
11194          * @param {String} name The attribute name
11195          * @return {String} The attribute value
11196          */
11197         getAttributeNS : Roo.isIE ? function(ns, name){
11198             var d = this.dom;
11199             var type = typeof d[ns+":"+name];
11200             if(type != 'undefined' && type != 'unknown'){
11201                 return d[ns+":"+name];
11202             }
11203             return d[name];
11204         } : function(ns, name){
11205             var d = this.dom;
11206             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11207         },
11208         
11209         
11210         /**
11211          * Sets or Returns the value the dom attribute value
11212          * @param {String|Object} name The attribute name (or object to set multiple attributes)
11213          * @param {String} value (optional) The value to set the attribute to
11214          * @return {String} The attribute value
11215          */
11216         attr : function(name){
11217             if (arguments.length > 1) {
11218                 this.dom.setAttribute(name, arguments[1]);
11219                 return arguments[1];
11220             }
11221             if (typeof(name) == 'object') {
11222                 for(var i in name) {
11223                     this.attr(i, name[i]);
11224                 }
11225                 return name;
11226             }
11227             
11228             
11229             if (!this.dom.hasAttribute(name)) {
11230                 return undefined;
11231             }
11232             return this.dom.getAttribute(name);
11233         }
11234         
11235         
11236         
11237     };
11238
11239     var ep = El.prototype;
11240
11241     /**
11242      * Appends an event handler (Shorthand for addListener)
11243      * @param {String}   eventName     The type of event to append
11244      * @param {Function} fn        The method the event invokes
11245      * @param {Object} scope       (optional) The scope (this object) of the fn
11246      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
11247      * @method
11248      */
11249     ep.on = ep.addListener;
11250         // backwards compat
11251     ep.mon = ep.addListener;
11252
11253     /**
11254      * Removes an event handler from this element (shorthand for removeListener)
11255      * @param {String} eventName the type of event to remove
11256      * @param {Function} fn the method the event invokes
11257      * @return {Roo.Element} this
11258      * @method
11259      */
11260     ep.un = ep.removeListener;
11261
11262     /**
11263      * true to automatically adjust width and height settings for box-model issues (default to true)
11264      */
11265     ep.autoBoxAdjust = true;
11266
11267     // private
11268     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11269
11270     // private
11271     El.addUnits = function(v, defaultUnit){
11272         if(v === "" || v == "auto"){
11273             return v;
11274         }
11275         if(v === undefined){
11276             return '';
11277         }
11278         if(typeof v == "number" || !El.unitPattern.test(v)){
11279             return v + (defaultUnit || 'px');
11280         }
11281         return v;
11282     };
11283
11284     // special markup used throughout Roo when box wrapping elements
11285     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>';
11286     /**
11287      * Visibility mode constant - Use visibility to hide element
11288      * @static
11289      * @type Number
11290      */
11291     El.VISIBILITY = 1;
11292     /**
11293      * Visibility mode constant - Use display to hide element
11294      * @static
11295      * @type Number
11296      */
11297     El.DISPLAY = 2;
11298
11299     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11300     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11301     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11302
11303
11304
11305     /**
11306      * @private
11307      */
11308     El.cache = {};
11309
11310     var docEl;
11311
11312     /**
11313      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11314      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11315      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11316      * @return {Element} The Element object
11317      * @static
11318      */
11319     El.get = function(el){
11320         var ex, elm, id;
11321         if(!el){ return null; }
11322         if(typeof el == "string"){ // element id
11323             if(!(elm = document.getElementById(el))){
11324                 return null;
11325             }
11326             if(ex = El.cache[el]){
11327                 ex.dom = elm;
11328             }else{
11329                 ex = El.cache[el] = new El(elm);
11330             }
11331             return ex;
11332         }else if(el.tagName){ // dom element
11333             if(!(id = el.id)){
11334                 id = Roo.id(el);
11335             }
11336             if(ex = El.cache[id]){
11337                 ex.dom = el;
11338             }else{
11339                 ex = El.cache[id] = new El(el);
11340             }
11341             return ex;
11342         }else if(el instanceof El){
11343             if(el != docEl){
11344                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11345                                                               // catch case where it hasn't been appended
11346                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11347             }
11348             return el;
11349         }else if(el.isComposite){
11350             return el;
11351         }else if(el instanceof Array){
11352             return El.select(el);
11353         }else if(el == document){
11354             // create a bogus element object representing the document object
11355             if(!docEl){
11356                 var f = function(){};
11357                 f.prototype = El.prototype;
11358                 docEl = new f();
11359                 docEl.dom = document;
11360             }
11361             return docEl;
11362         }
11363         return null;
11364     };
11365
11366     // private
11367     El.uncache = function(el){
11368         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11369             if(a[i]){
11370                 delete El.cache[a[i].id || a[i]];
11371             }
11372         }
11373     };
11374
11375     // private
11376     // Garbage collection - uncache elements/purge listeners on orphaned elements
11377     // so we don't hold a reference and cause the browser to retain them
11378     El.garbageCollect = function(){
11379         if(!Roo.enableGarbageCollector){
11380             clearInterval(El.collectorThread);
11381             return;
11382         }
11383         for(var eid in El.cache){
11384             var el = El.cache[eid], d = el.dom;
11385             // -------------------------------------------------------
11386             // Determining what is garbage:
11387             // -------------------------------------------------------
11388             // !d
11389             // dom node is null, definitely garbage
11390             // -------------------------------------------------------
11391             // !d.parentNode
11392             // no parentNode == direct orphan, definitely garbage
11393             // -------------------------------------------------------
11394             // !d.offsetParent && !document.getElementById(eid)
11395             // display none elements have no offsetParent so we will
11396             // also try to look it up by it's id. However, check
11397             // offsetParent first so we don't do unneeded lookups.
11398             // This enables collection of elements that are not orphans
11399             // directly, but somewhere up the line they have an orphan
11400             // parent.
11401             // -------------------------------------------------------
11402             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11403                 delete El.cache[eid];
11404                 if(d && Roo.enableListenerCollection){
11405                     E.purgeElement(d);
11406                 }
11407             }
11408         }
11409     }
11410     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11411
11412
11413     // dom is optional
11414     El.Flyweight = function(dom){
11415         this.dom = dom;
11416     };
11417     El.Flyweight.prototype = El.prototype;
11418
11419     El._flyweights = {};
11420     /**
11421      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11422      * the dom node can be overwritten by other code.
11423      * @param {String/HTMLElement} el The dom node or id
11424      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11425      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11426      * @static
11427      * @return {Element} The shared Element object
11428      */
11429     El.fly = function(el, named){
11430         named = named || '_global';
11431         el = Roo.getDom(el);
11432         if(!el){
11433             return null;
11434         }
11435         if(!El._flyweights[named]){
11436             El._flyweights[named] = new El.Flyweight();
11437         }
11438         El._flyweights[named].dom = el;
11439         return El._flyweights[named];
11440     };
11441
11442     /**
11443      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11444      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11445      * Shorthand of {@link Roo.Element#get}
11446      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11447      * @return {Element} The Element object
11448      * @member Roo
11449      * @method get
11450      */
11451     Roo.get = El.get;
11452     /**
11453      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11454      * the dom node can be overwritten by other code.
11455      * Shorthand of {@link Roo.Element#fly}
11456      * @param {String/HTMLElement} el The dom node or id
11457      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11458      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11459      * @static
11460      * @return {Element} The shared Element object
11461      * @member Roo
11462      * @method fly
11463      */
11464     Roo.fly = El.fly;
11465
11466     // speedy lookup for elements never to box adjust
11467     var noBoxAdjust = Roo.isStrict ? {
11468         select:1
11469     } : {
11470         input:1, select:1, textarea:1
11471     };
11472     if(Roo.isIE || Roo.isGecko){
11473         noBoxAdjust['button'] = 1;
11474     }
11475
11476
11477     Roo.EventManager.on(window, 'unload', function(){
11478         delete El.cache;
11479         delete El._flyweights;
11480     });
11481 })();
11482
11483
11484
11485
11486 if(Roo.DomQuery){
11487     Roo.Element.selectorFunction = Roo.DomQuery.select;
11488 }
11489
11490 Roo.Element.select = function(selector, unique, root){
11491     var els;
11492     if(typeof selector == "string"){
11493         els = Roo.Element.selectorFunction(selector, root);
11494     }else if(selector.length !== undefined){
11495         els = selector;
11496     }else{
11497         throw "Invalid selector";
11498     }
11499     if(unique === true){
11500         return new Roo.CompositeElement(els);
11501     }else{
11502         return new Roo.CompositeElementLite(els);
11503     }
11504 };
11505 /**
11506  * Selects elements based on the passed CSS selector to enable working on them as 1.
11507  * @param {String/Array} selector The CSS selector or an array of elements
11508  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11509  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11510  * @return {CompositeElementLite/CompositeElement}
11511  * @member Roo
11512  * @method select
11513  */
11514 Roo.select = Roo.Element.select;
11515
11516
11517
11518
11519
11520
11521
11522
11523
11524
11525
11526
11527
11528
11529 /*
11530  * Based on:
11531  * Ext JS Library 1.1.1
11532  * Copyright(c) 2006-2007, Ext JS, LLC.
11533  *
11534  * Originally Released Under LGPL - original licence link has changed is not relivant.
11535  *
11536  * Fork - LGPL
11537  * <script type="text/javascript">
11538  */
11539
11540
11541
11542 //Notifies Element that fx methods are available
11543 Roo.enableFx = true;
11544
11545 /**
11546  * @class Roo.Fx
11547  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11548  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11549  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11550  * Element effects to work.</p><br/>
11551  *
11552  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11553  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11554  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11555  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11556  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11557  * expected results and should be done with care.</p><br/>
11558  *
11559  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11560  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11561 <pre>
11562 Value  Description
11563 -----  -----------------------------
11564 tl     The top left corner
11565 t      The center of the top edge
11566 tr     The top right corner
11567 l      The center of the left edge
11568 r      The center of the right edge
11569 bl     The bottom left corner
11570 b      The center of the bottom edge
11571 br     The bottom right corner
11572 </pre>
11573  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11574  * below are common options that can be passed to any Fx method.</b>
11575  * @cfg {Function} callback A function called when the effect is finished
11576  * @cfg {Object} scope The scope of the effect function
11577  * @cfg {String} easing A valid Easing value for the effect
11578  * @cfg {String} afterCls A css class to apply after the effect
11579  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11580  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11581  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11582  * effects that end with the element being visually hidden, ignored otherwise)
11583  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11584  * a function which returns such a specification that will be applied to the Element after the effect finishes
11585  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11586  * @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
11587  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11588  */
11589 Roo.Fx = {
11590         /**
11591          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11592          * origin for the slide effect.  This function automatically handles wrapping the element with
11593          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11594          * Usage:
11595          *<pre><code>
11596 // default: slide the element in from the top
11597 el.slideIn();
11598
11599 // custom: slide the element in from the right with a 2-second duration
11600 el.slideIn('r', { duration: 2 });
11601
11602 // common config options shown with default values
11603 el.slideIn('t', {
11604     easing: 'easeOut',
11605     duration: .5
11606 });
11607 </code></pre>
11608          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11609          * @param {Object} options (optional) Object literal with any of the Fx config options
11610          * @return {Roo.Element} The Element
11611          */
11612     slideIn : function(anchor, o){
11613         var el = this.getFxEl();
11614         o = o || {};
11615
11616         el.queueFx(o, function(){
11617
11618             anchor = anchor || "t";
11619
11620             // fix display to visibility
11621             this.fixDisplay();
11622
11623             // restore values after effect
11624             var r = this.getFxRestore();
11625             var b = this.getBox();
11626             // fixed size for slide
11627             this.setSize(b);
11628
11629             // wrap if needed
11630             var wrap = this.fxWrap(r.pos, o, "hidden");
11631
11632             var st = this.dom.style;
11633             st.visibility = "visible";
11634             st.position = "absolute";
11635
11636             // clear out temp styles after slide and unwrap
11637             var after = function(){
11638                 el.fxUnwrap(wrap, r.pos, o);
11639                 st.width = r.width;
11640                 st.height = r.height;
11641                 el.afterFx(o);
11642             };
11643             // time to calc the positions
11644             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11645
11646             switch(anchor.toLowerCase()){
11647                 case "t":
11648                     wrap.setSize(b.width, 0);
11649                     st.left = st.bottom = "0";
11650                     a = {height: bh};
11651                 break;
11652                 case "l":
11653                     wrap.setSize(0, b.height);
11654                     st.right = st.top = "0";
11655                     a = {width: bw};
11656                 break;
11657                 case "r":
11658                     wrap.setSize(0, b.height);
11659                     wrap.setX(b.right);
11660                     st.left = st.top = "0";
11661                     a = {width: bw, points: pt};
11662                 break;
11663                 case "b":
11664                     wrap.setSize(b.width, 0);
11665                     wrap.setY(b.bottom);
11666                     st.left = st.top = "0";
11667                     a = {height: bh, points: pt};
11668                 break;
11669                 case "tl":
11670                     wrap.setSize(0, 0);
11671                     st.right = st.bottom = "0";
11672                     a = {width: bw, height: bh};
11673                 break;
11674                 case "bl":
11675                     wrap.setSize(0, 0);
11676                     wrap.setY(b.y+b.height);
11677                     st.right = st.top = "0";
11678                     a = {width: bw, height: bh, points: pt};
11679                 break;
11680                 case "br":
11681                     wrap.setSize(0, 0);
11682                     wrap.setXY([b.right, b.bottom]);
11683                     st.left = st.top = "0";
11684                     a = {width: bw, height: bh, points: pt};
11685                 break;
11686                 case "tr":
11687                     wrap.setSize(0, 0);
11688                     wrap.setX(b.x+b.width);
11689                     st.left = st.bottom = "0";
11690                     a = {width: bw, height: bh, points: pt};
11691                 break;
11692             }
11693             this.dom.style.visibility = "visible";
11694             wrap.show();
11695
11696             arguments.callee.anim = wrap.fxanim(a,
11697                 o,
11698                 'motion',
11699                 .5,
11700                 'easeOut', after);
11701         });
11702         return this;
11703     },
11704     
11705         /**
11706          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11707          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11708          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11709          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11710          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11711          * Usage:
11712          *<pre><code>
11713 // default: slide the element out to the top
11714 el.slideOut();
11715
11716 // custom: slide the element out to the right with a 2-second duration
11717 el.slideOut('r', { duration: 2 });
11718
11719 // common config options shown with default values
11720 el.slideOut('t', {
11721     easing: 'easeOut',
11722     duration: .5,
11723     remove: false,
11724     useDisplay: false
11725 });
11726 </code></pre>
11727          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11728          * @param {Object} options (optional) Object literal with any of the Fx config options
11729          * @return {Roo.Element} The Element
11730          */
11731     slideOut : function(anchor, o){
11732         var el = this.getFxEl();
11733         o = o || {};
11734
11735         el.queueFx(o, function(){
11736
11737             anchor = anchor || "t";
11738
11739             // restore values after effect
11740             var r = this.getFxRestore();
11741             
11742             var b = this.getBox();
11743             // fixed size for slide
11744             this.setSize(b);
11745
11746             // wrap if needed
11747             var wrap = this.fxWrap(r.pos, o, "visible");
11748
11749             var st = this.dom.style;
11750             st.visibility = "visible";
11751             st.position = "absolute";
11752
11753             wrap.setSize(b);
11754
11755             var after = function(){
11756                 if(o.useDisplay){
11757                     el.setDisplayed(false);
11758                 }else{
11759                     el.hide();
11760                 }
11761
11762                 el.fxUnwrap(wrap, r.pos, o);
11763
11764                 st.width = r.width;
11765                 st.height = r.height;
11766
11767                 el.afterFx(o);
11768             };
11769
11770             var a, zero = {to: 0};
11771             switch(anchor.toLowerCase()){
11772                 case "t":
11773                     st.left = st.bottom = "0";
11774                     a = {height: zero};
11775                 break;
11776                 case "l":
11777                     st.right = st.top = "0";
11778                     a = {width: zero};
11779                 break;
11780                 case "r":
11781                     st.left = st.top = "0";
11782                     a = {width: zero, points: {to:[b.right, b.y]}};
11783                 break;
11784                 case "b":
11785                     st.left = st.top = "0";
11786                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11787                 break;
11788                 case "tl":
11789                     st.right = st.bottom = "0";
11790                     a = {width: zero, height: zero};
11791                 break;
11792                 case "bl":
11793                     st.right = st.top = "0";
11794                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11795                 break;
11796                 case "br":
11797                     st.left = st.top = "0";
11798                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11799                 break;
11800                 case "tr":
11801                     st.left = st.bottom = "0";
11802                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11803                 break;
11804             }
11805
11806             arguments.callee.anim = wrap.fxanim(a,
11807                 o,
11808                 'motion',
11809                 .5,
11810                 "easeOut", after);
11811         });
11812         return this;
11813     },
11814
11815         /**
11816          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11817          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11818          * The element must be removed from the DOM using the 'remove' config option if desired.
11819          * Usage:
11820          *<pre><code>
11821 // default
11822 el.puff();
11823
11824 // common config options shown with default values
11825 el.puff({
11826     easing: 'easeOut',
11827     duration: .5,
11828     remove: false,
11829     useDisplay: false
11830 });
11831 </code></pre>
11832          * @param {Object} options (optional) Object literal with any of the Fx config options
11833          * @return {Roo.Element} The Element
11834          */
11835     puff : function(o){
11836         var el = this.getFxEl();
11837         o = o || {};
11838
11839         el.queueFx(o, function(){
11840             this.clearOpacity();
11841             this.show();
11842
11843             // restore values after effect
11844             var r = this.getFxRestore();
11845             var st = this.dom.style;
11846
11847             var after = function(){
11848                 if(o.useDisplay){
11849                     el.setDisplayed(false);
11850                 }else{
11851                     el.hide();
11852                 }
11853
11854                 el.clearOpacity();
11855
11856                 el.setPositioning(r.pos);
11857                 st.width = r.width;
11858                 st.height = r.height;
11859                 st.fontSize = '';
11860                 el.afterFx(o);
11861             };
11862
11863             var width = this.getWidth();
11864             var height = this.getHeight();
11865
11866             arguments.callee.anim = this.fxanim({
11867                     width : {to: this.adjustWidth(width * 2)},
11868                     height : {to: this.adjustHeight(height * 2)},
11869                     points : {by: [-(width * .5), -(height * .5)]},
11870                     opacity : {to: 0},
11871                     fontSize: {to:200, unit: "%"}
11872                 },
11873                 o,
11874                 'motion',
11875                 .5,
11876                 "easeOut", after);
11877         });
11878         return this;
11879     },
11880
11881         /**
11882          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11883          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11884          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11885          * Usage:
11886          *<pre><code>
11887 // default
11888 el.switchOff();
11889
11890 // all config options shown with default values
11891 el.switchOff({
11892     easing: 'easeIn',
11893     duration: .3,
11894     remove: false,
11895     useDisplay: false
11896 });
11897 </code></pre>
11898          * @param {Object} options (optional) Object literal with any of the Fx config options
11899          * @return {Roo.Element} The Element
11900          */
11901     switchOff : function(o){
11902         var el = this.getFxEl();
11903         o = o || {};
11904
11905         el.queueFx(o, function(){
11906             this.clearOpacity();
11907             this.clip();
11908
11909             // restore values after effect
11910             var r = this.getFxRestore();
11911             var st = this.dom.style;
11912
11913             var after = function(){
11914                 if(o.useDisplay){
11915                     el.setDisplayed(false);
11916                 }else{
11917                     el.hide();
11918                 }
11919
11920                 el.clearOpacity();
11921                 el.setPositioning(r.pos);
11922                 st.width = r.width;
11923                 st.height = r.height;
11924
11925                 el.afterFx(o);
11926             };
11927
11928             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11929                 this.clearOpacity();
11930                 (function(){
11931                     this.fxanim({
11932                         height:{to:1},
11933                         points:{by:[0, this.getHeight() * .5]}
11934                     }, o, 'motion', 0.3, 'easeIn', after);
11935                 }).defer(100, this);
11936             });
11937         });
11938         return this;
11939     },
11940
11941     /**
11942      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11943      * changed using the "attr" config option) and then fading back to the original color. If no original
11944      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11945      * Usage:
11946 <pre><code>
11947 // default: highlight background to yellow
11948 el.highlight();
11949
11950 // custom: highlight foreground text to blue for 2 seconds
11951 el.highlight("0000ff", { attr: 'color', duration: 2 });
11952
11953 // common config options shown with default values
11954 el.highlight("ffff9c", {
11955     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11956     endColor: (current color) or "ffffff",
11957     easing: 'easeIn',
11958     duration: 1
11959 });
11960 </code></pre>
11961      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11962      * @param {Object} options (optional) Object literal with any of the Fx config options
11963      * @return {Roo.Element} The Element
11964      */ 
11965     highlight : function(color, o){
11966         var el = this.getFxEl();
11967         o = o || {};
11968
11969         el.queueFx(o, function(){
11970             color = color || "ffff9c";
11971             attr = o.attr || "backgroundColor";
11972
11973             this.clearOpacity();
11974             this.show();
11975
11976             var origColor = this.getColor(attr);
11977             var restoreColor = this.dom.style[attr];
11978             endColor = (o.endColor || origColor) || "ffffff";
11979
11980             var after = function(){
11981                 el.dom.style[attr] = restoreColor;
11982                 el.afterFx(o);
11983             };
11984
11985             var a = {};
11986             a[attr] = {from: color, to: endColor};
11987             arguments.callee.anim = this.fxanim(a,
11988                 o,
11989                 'color',
11990                 1,
11991                 'easeIn', after);
11992         });
11993         return this;
11994     },
11995
11996    /**
11997     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11998     * Usage:
11999 <pre><code>
12000 // default: a single light blue ripple
12001 el.frame();
12002
12003 // custom: 3 red ripples lasting 3 seconds total
12004 el.frame("ff0000", 3, { duration: 3 });
12005
12006 // common config options shown with default values
12007 el.frame("C3DAF9", 1, {
12008     duration: 1 //duration of entire animation (not each individual ripple)
12009     // Note: Easing is not configurable and will be ignored if included
12010 });
12011 </code></pre>
12012     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
12013     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
12014     * @param {Object} options (optional) Object literal with any of the Fx config options
12015     * @return {Roo.Element} The Element
12016     */
12017     frame : function(color, count, o){
12018         var el = this.getFxEl();
12019         o = o || {};
12020
12021         el.queueFx(o, function(){
12022             color = color || "#C3DAF9";
12023             if(color.length == 6){
12024                 color = "#" + color;
12025             }
12026             count = count || 1;
12027             duration = o.duration || 1;
12028             this.show();
12029
12030             var b = this.getBox();
12031             var animFn = function(){
12032                 var proxy = this.createProxy({
12033
12034                      style:{
12035                         visbility:"hidden",
12036                         position:"absolute",
12037                         "z-index":"35000", // yee haw
12038                         border:"0px solid " + color
12039                      }
12040                   });
12041                 var scale = Roo.isBorderBox ? 2 : 1;
12042                 proxy.animate({
12043                     top:{from:b.y, to:b.y - 20},
12044                     left:{from:b.x, to:b.x - 20},
12045                     borderWidth:{from:0, to:10},
12046                     opacity:{from:1, to:0},
12047                     height:{from:b.height, to:(b.height + (20*scale))},
12048                     width:{from:b.width, to:(b.width + (20*scale))}
12049                 }, duration, function(){
12050                     proxy.remove();
12051                 });
12052                 if(--count > 0){
12053                      animFn.defer((duration/2)*1000, this);
12054                 }else{
12055                     el.afterFx(o);
12056                 }
12057             };
12058             animFn.call(this);
12059         });
12060         return this;
12061     },
12062
12063    /**
12064     * Creates a pause before any subsequent queued effects begin.  If there are
12065     * no effects queued after the pause it will have no effect.
12066     * Usage:
12067 <pre><code>
12068 el.pause(1);
12069 </code></pre>
12070     * @param {Number} seconds The length of time to pause (in seconds)
12071     * @return {Roo.Element} The Element
12072     */
12073     pause : function(seconds){
12074         var el = this.getFxEl();
12075         var o = {};
12076
12077         el.queueFx(o, function(){
12078             setTimeout(function(){
12079                 el.afterFx(o);
12080             }, seconds * 1000);
12081         });
12082         return this;
12083     },
12084
12085    /**
12086     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
12087     * using the "endOpacity" config option.
12088     * Usage:
12089 <pre><code>
12090 // default: fade in from opacity 0 to 100%
12091 el.fadeIn();
12092
12093 // custom: fade in from opacity 0 to 75% over 2 seconds
12094 el.fadeIn({ endOpacity: .75, duration: 2});
12095
12096 // common config options shown with default values
12097 el.fadeIn({
12098     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
12099     easing: 'easeOut',
12100     duration: .5
12101 });
12102 </code></pre>
12103     * @param {Object} options (optional) Object literal with any of the Fx config options
12104     * @return {Roo.Element} The Element
12105     */
12106     fadeIn : function(o){
12107         var el = this.getFxEl();
12108         o = o || {};
12109         el.queueFx(o, function(){
12110             this.setOpacity(0);
12111             this.fixDisplay();
12112             this.dom.style.visibility = 'visible';
12113             var to = o.endOpacity || 1;
12114             arguments.callee.anim = this.fxanim({opacity:{to:to}},
12115                 o, null, .5, "easeOut", function(){
12116                 if(to == 1){
12117                     this.clearOpacity();
12118                 }
12119                 el.afterFx(o);
12120             });
12121         });
12122         return this;
12123     },
12124
12125    /**
12126     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
12127     * using the "endOpacity" config option.
12128     * Usage:
12129 <pre><code>
12130 // default: fade out from the element's current opacity to 0
12131 el.fadeOut();
12132
12133 // custom: fade out from the element's current opacity to 25% over 2 seconds
12134 el.fadeOut({ endOpacity: .25, duration: 2});
12135
12136 // common config options shown with default values
12137 el.fadeOut({
12138     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
12139     easing: 'easeOut',
12140     duration: .5
12141     remove: false,
12142     useDisplay: false
12143 });
12144 </code></pre>
12145     * @param {Object} options (optional) Object literal with any of the Fx config options
12146     * @return {Roo.Element} The Element
12147     */
12148     fadeOut : function(o){
12149         var el = this.getFxEl();
12150         o = o || {};
12151         el.queueFx(o, function(){
12152             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
12153                 o, null, .5, "easeOut", function(){
12154                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
12155                      this.dom.style.display = "none";
12156                 }else{
12157                      this.dom.style.visibility = "hidden";
12158                 }
12159                 this.clearOpacity();
12160                 el.afterFx(o);
12161             });
12162         });
12163         return this;
12164     },
12165
12166    /**
12167     * Animates the transition of an element's dimensions from a starting height/width
12168     * to an ending height/width.
12169     * Usage:
12170 <pre><code>
12171 // change height and width to 100x100 pixels
12172 el.scale(100, 100);
12173
12174 // common config options shown with default values.  The height and width will default to
12175 // the element's existing values if passed as null.
12176 el.scale(
12177     [element's width],
12178     [element's height], {
12179     easing: 'easeOut',
12180     duration: .35
12181 });
12182 </code></pre>
12183     * @param {Number} width  The new width (pass undefined to keep the original width)
12184     * @param {Number} height  The new height (pass undefined to keep the original height)
12185     * @param {Object} options (optional) Object literal with any of the Fx config options
12186     * @return {Roo.Element} The Element
12187     */
12188     scale : function(w, h, o){
12189         this.shift(Roo.apply({}, o, {
12190             width: w,
12191             height: h
12192         }));
12193         return this;
12194     },
12195
12196    /**
12197     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12198     * Any of these properties not specified in the config object will not be changed.  This effect 
12199     * requires that at least one new dimension, position or opacity setting must be passed in on
12200     * the config object in order for the function to have any effect.
12201     * Usage:
12202 <pre><code>
12203 // slide the element horizontally to x position 200 while changing the height and opacity
12204 el.shift({ x: 200, height: 50, opacity: .8 });
12205
12206 // common config options shown with default values.
12207 el.shift({
12208     width: [element's width],
12209     height: [element's height],
12210     x: [element's x position],
12211     y: [element's y position],
12212     opacity: [element's opacity],
12213     easing: 'easeOut',
12214     duration: .35
12215 });
12216 </code></pre>
12217     * @param {Object} options  Object literal with any of the Fx config options
12218     * @return {Roo.Element} The Element
12219     */
12220     shift : function(o){
12221         var el = this.getFxEl();
12222         o = o || {};
12223         el.queueFx(o, function(){
12224             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
12225             if(w !== undefined){
12226                 a.width = {to: this.adjustWidth(w)};
12227             }
12228             if(h !== undefined){
12229                 a.height = {to: this.adjustHeight(h)};
12230             }
12231             if(x !== undefined || y !== undefined){
12232                 a.points = {to: [
12233                     x !== undefined ? x : this.getX(),
12234                     y !== undefined ? y : this.getY()
12235                 ]};
12236             }
12237             if(op !== undefined){
12238                 a.opacity = {to: op};
12239             }
12240             if(o.xy !== undefined){
12241                 a.points = {to: o.xy};
12242             }
12243             arguments.callee.anim = this.fxanim(a,
12244                 o, 'motion', .35, "easeOut", function(){
12245                 el.afterFx(o);
12246             });
12247         });
12248         return this;
12249     },
12250
12251         /**
12252          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
12253          * ending point of the effect.
12254          * Usage:
12255          *<pre><code>
12256 // default: slide the element downward while fading out
12257 el.ghost();
12258
12259 // custom: slide the element out to the right with a 2-second duration
12260 el.ghost('r', { duration: 2 });
12261
12262 // common config options shown with default values
12263 el.ghost('b', {
12264     easing: 'easeOut',
12265     duration: .5
12266     remove: false,
12267     useDisplay: false
12268 });
12269 </code></pre>
12270          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12271          * @param {Object} options (optional) Object literal with any of the Fx config options
12272          * @return {Roo.Element} The Element
12273          */
12274     ghost : function(anchor, o){
12275         var el = this.getFxEl();
12276         o = o || {};
12277
12278         el.queueFx(o, function(){
12279             anchor = anchor || "b";
12280
12281             // restore values after effect
12282             var r = this.getFxRestore();
12283             var w = this.getWidth(),
12284                 h = this.getHeight();
12285
12286             var st = this.dom.style;
12287
12288             var after = function(){
12289                 if(o.useDisplay){
12290                     el.setDisplayed(false);
12291                 }else{
12292                     el.hide();
12293                 }
12294
12295                 el.clearOpacity();
12296                 el.setPositioning(r.pos);
12297                 st.width = r.width;
12298                 st.height = r.height;
12299
12300                 el.afterFx(o);
12301             };
12302
12303             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12304             switch(anchor.toLowerCase()){
12305                 case "t":
12306                     pt.by = [0, -h];
12307                 break;
12308                 case "l":
12309                     pt.by = [-w, 0];
12310                 break;
12311                 case "r":
12312                     pt.by = [w, 0];
12313                 break;
12314                 case "b":
12315                     pt.by = [0, h];
12316                 break;
12317                 case "tl":
12318                     pt.by = [-w, -h];
12319                 break;
12320                 case "bl":
12321                     pt.by = [-w, h];
12322                 break;
12323                 case "br":
12324                     pt.by = [w, h];
12325                 break;
12326                 case "tr":
12327                     pt.by = [w, -h];
12328                 break;
12329             }
12330
12331             arguments.callee.anim = this.fxanim(a,
12332                 o,
12333                 'motion',
12334                 .5,
12335                 "easeOut", after);
12336         });
12337         return this;
12338     },
12339
12340         /**
12341          * Ensures that all effects queued after syncFx is called on the element are
12342          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12343          * @return {Roo.Element} The Element
12344          */
12345     syncFx : function(){
12346         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12347             block : false,
12348             concurrent : true,
12349             stopFx : false
12350         });
12351         return this;
12352     },
12353
12354         /**
12355          * Ensures that all effects queued after sequenceFx is called on the element are
12356          * run in sequence.  This is the opposite of {@link #syncFx}.
12357          * @return {Roo.Element} The Element
12358          */
12359     sequenceFx : function(){
12360         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12361             block : false,
12362             concurrent : false,
12363             stopFx : false
12364         });
12365         return this;
12366     },
12367
12368         /* @private */
12369     nextFx : function(){
12370         var ef = this.fxQueue[0];
12371         if(ef){
12372             ef.call(this);
12373         }
12374     },
12375
12376         /**
12377          * Returns true if the element has any effects actively running or queued, else returns false.
12378          * @return {Boolean} True if element has active effects, else false
12379          */
12380     hasActiveFx : function(){
12381         return this.fxQueue && this.fxQueue[0];
12382     },
12383
12384         /**
12385          * Stops any running effects and clears the element's internal effects queue if it contains
12386          * any additional effects that haven't started yet.
12387          * @return {Roo.Element} The Element
12388          */
12389     stopFx : function(){
12390         if(this.hasActiveFx()){
12391             var cur = this.fxQueue[0];
12392             if(cur && cur.anim && cur.anim.isAnimated()){
12393                 this.fxQueue = [cur]; // clear out others
12394                 cur.anim.stop(true);
12395             }
12396         }
12397         return this;
12398     },
12399
12400         /* @private */
12401     beforeFx : function(o){
12402         if(this.hasActiveFx() && !o.concurrent){
12403            if(o.stopFx){
12404                this.stopFx();
12405                return true;
12406            }
12407            return false;
12408         }
12409         return true;
12410     },
12411
12412         /**
12413          * Returns true if the element is currently blocking so that no other effect can be queued
12414          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12415          * used to ensure that an effect initiated by a user action runs to completion prior to the
12416          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12417          * @return {Boolean} True if blocking, else false
12418          */
12419     hasFxBlock : function(){
12420         var q = this.fxQueue;
12421         return q && q[0] && q[0].block;
12422     },
12423
12424         /* @private */
12425     queueFx : function(o, fn){
12426         if(!this.fxQueue){
12427             this.fxQueue = [];
12428         }
12429         if(!this.hasFxBlock()){
12430             Roo.applyIf(o, this.fxDefaults);
12431             if(!o.concurrent){
12432                 var run = this.beforeFx(o);
12433                 fn.block = o.block;
12434                 this.fxQueue.push(fn);
12435                 if(run){
12436                     this.nextFx();
12437                 }
12438             }else{
12439                 fn.call(this);
12440             }
12441         }
12442         return this;
12443     },
12444
12445         /* @private */
12446     fxWrap : function(pos, o, vis){
12447         var wrap;
12448         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12449             var wrapXY;
12450             if(o.fixPosition){
12451                 wrapXY = this.getXY();
12452             }
12453             var div = document.createElement("div");
12454             div.style.visibility = vis;
12455             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12456             wrap.setPositioning(pos);
12457             if(wrap.getStyle("position") == "static"){
12458                 wrap.position("relative");
12459             }
12460             this.clearPositioning('auto');
12461             wrap.clip();
12462             wrap.dom.appendChild(this.dom);
12463             if(wrapXY){
12464                 wrap.setXY(wrapXY);
12465             }
12466         }
12467         return wrap;
12468     },
12469
12470         /* @private */
12471     fxUnwrap : function(wrap, pos, o){
12472         this.clearPositioning();
12473         this.setPositioning(pos);
12474         if(!o.wrap){
12475             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12476             wrap.remove();
12477         }
12478     },
12479
12480         /* @private */
12481     getFxRestore : function(){
12482         var st = this.dom.style;
12483         return {pos: this.getPositioning(), width: st.width, height : st.height};
12484     },
12485
12486         /* @private */
12487     afterFx : function(o){
12488         if(o.afterStyle){
12489             this.applyStyles(o.afterStyle);
12490         }
12491         if(o.afterCls){
12492             this.addClass(o.afterCls);
12493         }
12494         if(o.remove === true){
12495             this.remove();
12496         }
12497         Roo.callback(o.callback, o.scope, [this]);
12498         if(!o.concurrent){
12499             this.fxQueue.shift();
12500             this.nextFx();
12501         }
12502     },
12503
12504         /* @private */
12505     getFxEl : function(){ // support for composite element fx
12506         return Roo.get(this.dom);
12507     },
12508
12509         /* @private */
12510     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12511         animType = animType || 'run';
12512         opt = opt || {};
12513         var anim = Roo.lib.Anim[animType](
12514             this.dom, args,
12515             (opt.duration || defaultDur) || .35,
12516             (opt.easing || defaultEase) || 'easeOut',
12517             function(){
12518                 Roo.callback(cb, this);
12519             },
12520             this
12521         );
12522         opt.anim = anim;
12523         return anim;
12524     }
12525 };
12526
12527 // backwords compat
12528 Roo.Fx.resize = Roo.Fx.scale;
12529
12530 //When included, Roo.Fx is automatically applied to Element so that all basic
12531 //effects are available directly via the Element API
12532 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12533  * Based on:
12534  * Ext JS Library 1.1.1
12535  * Copyright(c) 2006-2007, Ext JS, LLC.
12536  *
12537  * Originally Released Under LGPL - original licence link has changed is not relivant.
12538  *
12539  * Fork - LGPL
12540  * <script type="text/javascript">
12541  */
12542
12543
12544 /**
12545  * @class Roo.CompositeElement
12546  * Standard composite class. Creates a Roo.Element for every element in the collection.
12547  * <br><br>
12548  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12549  * actions will be performed on all the elements in this collection.</b>
12550  * <br><br>
12551  * All methods return <i>this</i> and can be chained.
12552  <pre><code>
12553  var els = Roo.select("#some-el div.some-class", true);
12554  // or select directly from an existing element
12555  var el = Roo.get('some-el');
12556  el.select('div.some-class', true);
12557
12558  els.setWidth(100); // all elements become 100 width
12559  els.hide(true); // all elements fade out and hide
12560  // or
12561  els.setWidth(100).hide(true);
12562  </code></pre>
12563  */
12564 Roo.CompositeElement = function(els){
12565     this.elements = [];
12566     this.addElements(els);
12567 };
12568 Roo.CompositeElement.prototype = {
12569     isComposite: true,
12570     addElements : function(els){
12571         if(!els) {
12572             return this;
12573         }
12574         if(typeof els == "string"){
12575             els = Roo.Element.selectorFunction(els);
12576         }
12577         var yels = this.elements;
12578         var index = yels.length-1;
12579         for(var i = 0, len = els.length; i < len; i++) {
12580                 yels[++index] = Roo.get(els[i]);
12581         }
12582         return this;
12583     },
12584
12585     /**
12586     * Clears this composite and adds the elements returned by the passed selector.
12587     * @param {String/Array} els A string CSS selector, an array of elements or an element
12588     * @return {CompositeElement} this
12589     */
12590     fill : function(els){
12591         this.elements = [];
12592         this.add(els);
12593         return this;
12594     },
12595
12596     /**
12597     * Filters this composite to only elements that match the passed selector.
12598     * @param {String} selector A string CSS selector
12599     * @param {Boolean} inverse return inverse filter (not matches)
12600     * @return {CompositeElement} this
12601     */
12602     filter : function(selector, inverse){
12603         var els = [];
12604         inverse = inverse || false;
12605         this.each(function(el){
12606             var match = inverse ? !el.is(selector) : el.is(selector);
12607             if(match){
12608                 els[els.length] = el.dom;
12609             }
12610         });
12611         this.fill(els);
12612         return this;
12613     },
12614
12615     invoke : function(fn, args){
12616         var els = this.elements;
12617         for(var i = 0, len = els.length; i < len; i++) {
12618                 Roo.Element.prototype[fn].apply(els[i], args);
12619         }
12620         return this;
12621     },
12622     /**
12623     * Adds elements to this composite.
12624     * @param {String/Array} els A string CSS selector, an array of elements or an element
12625     * @return {CompositeElement} this
12626     */
12627     add : function(els){
12628         if(typeof els == "string"){
12629             this.addElements(Roo.Element.selectorFunction(els));
12630         }else if(els.length !== undefined){
12631             this.addElements(els);
12632         }else{
12633             this.addElements([els]);
12634         }
12635         return this;
12636     },
12637     /**
12638     * Calls the passed function passing (el, this, index) for each element in this composite.
12639     * @param {Function} fn The function to call
12640     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12641     * @return {CompositeElement} this
12642     */
12643     each : function(fn, scope){
12644         var els = this.elements;
12645         for(var i = 0, len = els.length; i < len; i++){
12646             if(fn.call(scope || els[i], els[i], this, i) === false) {
12647                 break;
12648             }
12649         }
12650         return this;
12651     },
12652
12653     /**
12654      * Returns the Element object at the specified index
12655      * @param {Number} index
12656      * @return {Roo.Element}
12657      */
12658     item : function(index){
12659         return this.elements[index] || null;
12660     },
12661
12662     /**
12663      * Returns the first Element
12664      * @return {Roo.Element}
12665      */
12666     first : function(){
12667         return this.item(0);
12668     },
12669
12670     /**
12671      * Returns the last Element
12672      * @return {Roo.Element}
12673      */
12674     last : function(){
12675         return this.item(this.elements.length-1);
12676     },
12677
12678     /**
12679      * Returns the number of elements in this composite
12680      * @return Number
12681      */
12682     getCount : function(){
12683         return this.elements.length;
12684     },
12685
12686     /**
12687      * Returns true if this composite contains the passed element
12688      * @return Boolean
12689      */
12690     contains : function(el){
12691         return this.indexOf(el) !== -1;
12692     },
12693
12694     /**
12695      * Returns true if this composite contains the passed element
12696      * @return Boolean
12697      */
12698     indexOf : function(el){
12699         return this.elements.indexOf(Roo.get(el));
12700     },
12701
12702
12703     /**
12704     * Removes the specified element(s).
12705     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12706     * or an array of any of those.
12707     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12708     * @return {CompositeElement} this
12709     */
12710     removeElement : function(el, removeDom){
12711         if(el instanceof Array){
12712             for(var i = 0, len = el.length; i < len; i++){
12713                 this.removeElement(el[i]);
12714             }
12715             return this;
12716         }
12717         var index = typeof el == 'number' ? el : this.indexOf(el);
12718         if(index !== -1){
12719             if(removeDom){
12720                 var d = this.elements[index];
12721                 if(d.dom){
12722                     d.remove();
12723                 }else{
12724                     d.parentNode.removeChild(d);
12725                 }
12726             }
12727             this.elements.splice(index, 1);
12728         }
12729         return this;
12730     },
12731
12732     /**
12733     * Replaces the specified element with the passed element.
12734     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12735     * to replace.
12736     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12737     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12738     * @return {CompositeElement} this
12739     */
12740     replaceElement : function(el, replacement, domReplace){
12741         var index = typeof el == 'number' ? el : this.indexOf(el);
12742         if(index !== -1){
12743             if(domReplace){
12744                 this.elements[index].replaceWith(replacement);
12745             }else{
12746                 this.elements.splice(index, 1, Roo.get(replacement))
12747             }
12748         }
12749         return this;
12750     },
12751
12752     /**
12753      * Removes all elements.
12754      */
12755     clear : function(){
12756         this.elements = [];
12757     }
12758 };
12759 (function(){
12760     Roo.CompositeElement.createCall = function(proto, fnName){
12761         if(!proto[fnName]){
12762             proto[fnName] = function(){
12763                 return this.invoke(fnName, arguments);
12764             };
12765         }
12766     };
12767     for(var fnName in Roo.Element.prototype){
12768         if(typeof Roo.Element.prototype[fnName] == "function"){
12769             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12770         }
12771     };
12772 })();
12773 /*
12774  * Based on:
12775  * Ext JS Library 1.1.1
12776  * Copyright(c) 2006-2007, Ext JS, LLC.
12777  *
12778  * Originally Released Under LGPL - original licence link has changed is not relivant.
12779  *
12780  * Fork - LGPL
12781  * <script type="text/javascript">
12782  */
12783
12784 /**
12785  * @class Roo.CompositeElementLite
12786  * @extends Roo.CompositeElement
12787  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12788  <pre><code>
12789  var els = Roo.select("#some-el div.some-class");
12790  // or select directly from an existing element
12791  var el = Roo.get('some-el');
12792  el.select('div.some-class');
12793
12794  els.setWidth(100); // all elements become 100 width
12795  els.hide(true); // all elements fade out and hide
12796  // or
12797  els.setWidth(100).hide(true);
12798  </code></pre><br><br>
12799  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12800  * actions will be performed on all the elements in this collection.</b>
12801  */
12802 Roo.CompositeElementLite = function(els){
12803     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12804     this.el = new Roo.Element.Flyweight();
12805 };
12806 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12807     addElements : function(els){
12808         if(els){
12809             if(els instanceof Array){
12810                 this.elements = this.elements.concat(els);
12811             }else{
12812                 var yels = this.elements;
12813                 var index = yels.length-1;
12814                 for(var i = 0, len = els.length; i < len; i++) {
12815                     yels[++index] = els[i];
12816                 }
12817             }
12818         }
12819         return this;
12820     },
12821     invoke : function(fn, args){
12822         var els = this.elements;
12823         var el = this.el;
12824         for(var i = 0, len = els.length; i < len; i++) {
12825             el.dom = els[i];
12826                 Roo.Element.prototype[fn].apply(el, args);
12827         }
12828         return this;
12829     },
12830     /**
12831      * Returns a flyweight Element of the dom element object at the specified index
12832      * @param {Number} index
12833      * @return {Roo.Element}
12834      */
12835     item : function(index){
12836         if(!this.elements[index]){
12837             return null;
12838         }
12839         this.el.dom = this.elements[index];
12840         return this.el;
12841     },
12842
12843     // fixes scope with flyweight
12844     addListener : function(eventName, handler, scope, opt){
12845         var els = this.elements;
12846         for(var i = 0, len = els.length; i < len; i++) {
12847             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12848         }
12849         return this;
12850     },
12851
12852     /**
12853     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12854     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12855     * a reference to the dom node, use el.dom.</b>
12856     * @param {Function} fn The function to call
12857     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12858     * @return {CompositeElement} this
12859     */
12860     each : function(fn, scope){
12861         var els = this.elements;
12862         var el = this.el;
12863         for(var i = 0, len = els.length; i < len; i++){
12864             el.dom = els[i];
12865                 if(fn.call(scope || el, el, this, i) === false){
12866                 break;
12867             }
12868         }
12869         return this;
12870     },
12871
12872     indexOf : function(el){
12873         return this.elements.indexOf(Roo.getDom(el));
12874     },
12875
12876     replaceElement : function(el, replacement, domReplace){
12877         var index = typeof el == 'number' ? el : this.indexOf(el);
12878         if(index !== -1){
12879             replacement = Roo.getDom(replacement);
12880             if(domReplace){
12881                 var d = this.elements[index];
12882                 d.parentNode.insertBefore(replacement, d);
12883                 d.parentNode.removeChild(d);
12884             }
12885             this.elements.splice(index, 1, replacement);
12886         }
12887         return this;
12888     }
12889 });
12890 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12891
12892 /*
12893  * Based on:
12894  * Ext JS Library 1.1.1
12895  * Copyright(c) 2006-2007, Ext JS, LLC.
12896  *
12897  * Originally Released Under LGPL - original licence link has changed is not relivant.
12898  *
12899  * Fork - LGPL
12900  * <script type="text/javascript">
12901  */
12902
12903  
12904
12905 /**
12906  * @class Roo.data.Connection
12907  * @extends Roo.util.Observable
12908  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12909  * either to a configured URL, or to a URL specified at request time. 
12910  * 
12911  * Requests made by this class are asynchronous, and will return immediately. No data from
12912  * the server will be available to the statement immediately following the {@link #request} call.
12913  * To process returned data, use a callback in the request options object, or an event listener.
12914  * 
12915  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12916  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12917  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12918  * property and, if present, the IFRAME's XML document as the responseXML property.
12919  * 
12920  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12921  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12922  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12923  * standard DOM methods.
12924  * @constructor
12925  * @param {Object} config a configuration object.
12926  */
12927 Roo.data.Connection = function(config){
12928     Roo.apply(this, config);
12929     this.addEvents({
12930         /**
12931          * @event beforerequest
12932          * Fires before a network request is made to retrieve a data object.
12933          * @param {Connection} conn This Connection object.
12934          * @param {Object} options The options config object passed to the {@link #request} method.
12935          */
12936         "beforerequest" : true,
12937         /**
12938          * @event requestcomplete
12939          * Fires if the request was successfully completed.
12940          * @param {Connection} conn This Connection object.
12941          * @param {Object} response The XHR object containing the response data.
12942          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12943          * @param {Object} options The options config object passed to the {@link #request} method.
12944          */
12945         "requestcomplete" : true,
12946         /**
12947          * @event requestexception
12948          * Fires if an error HTTP status was returned from the server.
12949          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12950          * @param {Connection} conn This Connection object.
12951          * @param {Object} response The XHR object containing the response data.
12952          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12953          * @param {Object} options The options config object passed to the {@link #request} method.
12954          */
12955         "requestexception" : true
12956     });
12957     Roo.data.Connection.superclass.constructor.call(this);
12958 };
12959
12960 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12961     /**
12962      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12963      */
12964     /**
12965      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12966      * extra parameters to each request made by this object. (defaults to undefined)
12967      */
12968     /**
12969      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12970      *  to each request made by this object. (defaults to undefined)
12971      */
12972     /**
12973      * @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)
12974      */
12975     /**
12976      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12977      */
12978     timeout : 30000,
12979     /**
12980      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12981      * @type Boolean
12982      */
12983     autoAbort:false,
12984
12985     /**
12986      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12987      * @type Boolean
12988      */
12989     disableCaching: true,
12990
12991     /**
12992      * Sends an HTTP request to a remote server.
12993      * @param {Object} options An object which may contain the following properties:<ul>
12994      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12995      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12996      * request, a url encoded string or a function to call to get either.</li>
12997      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12998      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12999      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
13000      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
13001      * <li>options {Object} The parameter to the request call.</li>
13002      * <li>success {Boolean} True if the request succeeded.</li>
13003      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13004      * </ul></li>
13005      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
13006      * The callback is passed the following parameters:<ul>
13007      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13008      * <li>options {Object} The parameter to the request call.</li>
13009      * </ul></li>
13010      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
13011      * The callback is passed the following parameters:<ul>
13012      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13013      * <li>options {Object} The parameter to the request call.</li>
13014      * </ul></li>
13015      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
13016      * for the callback function. Defaults to the browser window.</li>
13017      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
13018      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
13019      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
13020      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
13021      * params for the post data. Any params will be appended to the URL.</li>
13022      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
13023      * </ul>
13024      * @return {Number} transactionId
13025      */
13026     request : function(o){
13027         if(this.fireEvent("beforerequest", this, o) !== false){
13028             var p = o.params;
13029
13030             if(typeof p == "function"){
13031                 p = p.call(o.scope||window, o);
13032             }
13033             if(typeof p == "object"){
13034                 p = Roo.urlEncode(o.params);
13035             }
13036             if(this.extraParams){
13037                 var extras = Roo.urlEncode(this.extraParams);
13038                 p = p ? (p + '&' + extras) : extras;
13039             }
13040
13041             var url = o.url || this.url;
13042             if(typeof url == 'function'){
13043                 url = url.call(o.scope||window, o);
13044             }
13045
13046             if(o.form){
13047                 var form = Roo.getDom(o.form);
13048                 url = url || form.action;
13049
13050                 var enctype = form.getAttribute("enctype");
13051                 
13052                 if (o.formData) {
13053                     return this.doFormDataUpload(o, url);
13054                 }
13055                 
13056                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
13057                     return this.doFormUpload(o, p, url);
13058                 }
13059                 var f = Roo.lib.Ajax.serializeForm(form);
13060                 p = p ? (p + '&' + f) : f;
13061             }
13062             
13063             if (!o.form && o.formData) {
13064                 o.formData = o.formData === true ? new FormData() : o.formData;
13065                 for (var k in o.params) {
13066                     o.formData.append(k,o.params[k]);
13067                 }
13068                     
13069                 return this.doFormDataUpload(o, url);
13070             }
13071             
13072
13073             var hs = o.headers;
13074             if(this.defaultHeaders){
13075                 hs = Roo.apply(hs || {}, this.defaultHeaders);
13076                 if(!o.headers){
13077                     o.headers = hs;
13078                 }
13079             }
13080
13081             var cb = {
13082                 success: this.handleResponse,
13083                 failure: this.handleFailure,
13084                 scope: this,
13085                 argument: {options: o},
13086                 timeout : o.timeout || this.timeout
13087             };
13088
13089             var method = o.method||this.method||(p ? "POST" : "GET");
13090
13091             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
13092                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
13093             }
13094
13095             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13096                 if(o.autoAbort){
13097                     this.abort();
13098                 }
13099             }else if(this.autoAbort !== false){
13100                 this.abort();
13101             }
13102
13103             if((method == 'GET' && p) || o.xmlData){
13104                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
13105                 p = '';
13106             }
13107             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
13108             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
13109             Roo.lib.Ajax.useDefaultHeader == true;
13110             return this.transId;
13111         }else{
13112             Roo.callback(o.callback, o.scope, [o, null, null]);
13113             return null;
13114         }
13115     },
13116
13117     /**
13118      * Determine whether this object has a request outstanding.
13119      * @param {Number} transactionId (Optional) defaults to the last transaction
13120      * @return {Boolean} True if there is an outstanding request.
13121      */
13122     isLoading : function(transId){
13123         if(transId){
13124             return Roo.lib.Ajax.isCallInProgress(transId);
13125         }else{
13126             return this.transId ? true : false;
13127         }
13128     },
13129
13130     /**
13131      * Aborts any outstanding request.
13132      * @param {Number} transactionId (Optional) defaults to the last transaction
13133      */
13134     abort : function(transId){
13135         if(transId || this.isLoading()){
13136             Roo.lib.Ajax.abort(transId || this.transId);
13137         }
13138     },
13139
13140     // private
13141     handleResponse : function(response){
13142         this.transId = false;
13143         var options = response.argument.options;
13144         response.argument = options ? options.argument : null;
13145         this.fireEvent("requestcomplete", this, response, options);
13146         Roo.callback(options.success, options.scope, [response, options]);
13147         Roo.callback(options.callback, options.scope, [options, true, response]);
13148     },
13149
13150     // private
13151     handleFailure : function(response, e){
13152         this.transId = false;
13153         var options = response.argument.options;
13154         response.argument = options ? options.argument : null;
13155         this.fireEvent("requestexception", this, response, options, e);
13156         Roo.callback(options.failure, options.scope, [response, options]);
13157         Roo.callback(options.callback, options.scope, [options, false, response]);
13158     },
13159
13160     // private
13161     doFormUpload : function(o, ps, url){
13162         var id = Roo.id();
13163         var frame = document.createElement('iframe');
13164         frame.id = id;
13165         frame.name = id;
13166         frame.className = 'x-hidden';
13167         if(Roo.isIE){
13168             frame.src = Roo.SSL_SECURE_URL;
13169         }
13170         document.body.appendChild(frame);
13171
13172         if(Roo.isIE){
13173            document.frames[id].name = id;
13174         }
13175
13176         var form = Roo.getDom(o.form);
13177         form.target = id;
13178         form.method = 'POST';
13179         form.enctype = form.encoding = 'multipart/form-data';
13180         if(url){
13181             form.action = url;
13182         }
13183
13184         var hiddens, hd;
13185         if(ps){ // add dynamic params
13186             hiddens = [];
13187             ps = Roo.urlDecode(ps, false);
13188             for(var k in ps){
13189                 if(ps.hasOwnProperty(k)){
13190                     hd = document.createElement('input');
13191                     hd.type = 'hidden';
13192                     hd.name = k;
13193                     hd.value = ps[k];
13194                     form.appendChild(hd);
13195                     hiddens.push(hd);
13196                 }
13197             }
13198         }
13199
13200         function cb(){
13201             var r = {  // bogus response object
13202                 responseText : '',
13203                 responseXML : null
13204             };
13205
13206             r.argument = o ? o.argument : null;
13207
13208             try { //
13209                 var doc;
13210                 if(Roo.isIE){
13211                     doc = frame.contentWindow.document;
13212                 }else {
13213                     doc = (frame.contentDocument || window.frames[id].document);
13214                 }
13215                 if(doc && doc.body){
13216                     r.responseText = doc.body.innerHTML;
13217                 }
13218                 if(doc && doc.XMLDocument){
13219                     r.responseXML = doc.XMLDocument;
13220                 }else {
13221                     r.responseXML = doc;
13222                 }
13223             }
13224             catch(e) {
13225                 // ignore
13226             }
13227
13228             Roo.EventManager.removeListener(frame, 'load', cb, this);
13229
13230             this.fireEvent("requestcomplete", this, r, o);
13231             Roo.callback(o.success, o.scope, [r, o]);
13232             Roo.callback(o.callback, o.scope, [o, true, r]);
13233
13234             setTimeout(function(){document.body.removeChild(frame);}, 100);
13235         }
13236
13237         Roo.EventManager.on(frame, 'load', cb, this);
13238         form.submit();
13239
13240         if(hiddens){ // remove dynamic params
13241             for(var i = 0, len = hiddens.length; i < len; i++){
13242                 form.removeChild(hiddens[i]);
13243             }
13244         }
13245     },
13246     // this is a 'formdata version???'
13247     
13248     
13249     doFormDataUpload : function(o,  url)
13250     {
13251         var formData;
13252         if (o.form) {
13253             var form =  Roo.getDom(o.form);
13254             form.enctype = form.encoding = 'multipart/form-data';
13255             formData = o.formData === true ? new FormData(form) : o.formData;
13256         } else {
13257             formData = o.formData === true ? new FormData() : o.formData;
13258         }
13259         
13260       
13261         var cb = {
13262             success: this.handleResponse,
13263             failure: this.handleFailure,
13264             scope: this,
13265             argument: {options: o},
13266             timeout : o.timeout || this.timeout
13267         };
13268  
13269         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13270             if(o.autoAbort){
13271                 this.abort();
13272             }
13273         }else if(this.autoAbort !== false){
13274             this.abort();
13275         }
13276
13277         //Roo.lib.Ajax.defaultPostHeader = null;
13278         Roo.lib.Ajax.useDefaultHeader = false;
13279         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
13280         Roo.lib.Ajax.useDefaultHeader = true;
13281  
13282          
13283     }
13284     
13285 });
13286 /*
13287  * Based on:
13288  * Ext JS Library 1.1.1
13289  * Copyright(c) 2006-2007, Ext JS, LLC.
13290  *
13291  * Originally Released Under LGPL - original licence link has changed is not relivant.
13292  *
13293  * Fork - LGPL
13294  * <script type="text/javascript">
13295  */
13296  
13297 /**
13298  * Global Ajax request class.
13299  * 
13300  * @class Roo.Ajax
13301  * @extends Roo.data.Connection
13302  * @static
13303  * 
13304  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13305  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13306  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13307  * @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)
13308  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13309  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13310  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13311  */
13312 Roo.Ajax = new Roo.data.Connection({
13313     // fix up the docs
13314     /**
13315      * @scope Roo.Ajax
13316      * @type {Boolear} 
13317      */
13318     autoAbort : false,
13319
13320     /**
13321      * Serialize the passed form into a url encoded string
13322      * @scope Roo.Ajax
13323      * @param {String/HTMLElement} form
13324      * @return {String}
13325      */
13326     serializeForm : function(form){
13327         return Roo.lib.Ajax.serializeForm(form);
13328     }
13329 });/*
13330  * Based on:
13331  * Ext JS Library 1.1.1
13332  * Copyright(c) 2006-2007, Ext JS, LLC.
13333  *
13334  * Originally Released Under LGPL - original licence link has changed is not relivant.
13335  *
13336  * Fork - LGPL
13337  * <script type="text/javascript">
13338  */
13339
13340  
13341 /**
13342  * @class Roo.UpdateManager
13343  * @extends Roo.util.Observable
13344  * Provides AJAX-style update for Element object.<br><br>
13345  * Usage:<br>
13346  * <pre><code>
13347  * // Get it from a Roo.Element object
13348  * var el = Roo.get("foo");
13349  * var mgr = el.getUpdateManager();
13350  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13351  * ...
13352  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13353  * <br>
13354  * // or directly (returns the same UpdateManager instance)
13355  * var mgr = new Roo.UpdateManager("myElementId");
13356  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13357  * mgr.on("update", myFcnNeedsToKnow);
13358  * <br>
13359    // short handed call directly from the element object
13360    Roo.get("foo").load({
13361         url: "bar.php",
13362         scripts:true,
13363         params: "for=bar",
13364         text: "Loading Foo..."
13365    });
13366  * </code></pre>
13367  * @constructor
13368  * Create new UpdateManager directly.
13369  * @param {String/HTMLElement/Roo.Element} el The element to update
13370  * @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).
13371  */
13372 Roo.UpdateManager = function(el, forceNew){
13373     el = Roo.get(el);
13374     if(!forceNew && el.updateManager){
13375         return el.updateManager;
13376     }
13377     /**
13378      * The Element object
13379      * @type Roo.Element
13380      */
13381     this.el = el;
13382     /**
13383      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13384      * @type String
13385      */
13386     this.defaultUrl = null;
13387
13388     this.addEvents({
13389         /**
13390          * @event beforeupdate
13391          * Fired before an update is made, return false from your handler and the update is cancelled.
13392          * @param {Roo.Element} el
13393          * @param {String/Object/Function} url
13394          * @param {String/Object} params
13395          */
13396         "beforeupdate": true,
13397         /**
13398          * @event update
13399          * Fired after successful update is made.
13400          * @param {Roo.Element} el
13401          * @param {Object} oResponseObject The response Object
13402          */
13403         "update": true,
13404         /**
13405          * @event failure
13406          * Fired on update failure.
13407          * @param {Roo.Element} el
13408          * @param {Object} oResponseObject The response Object
13409          */
13410         "failure": true
13411     });
13412     var d = Roo.UpdateManager.defaults;
13413     /**
13414      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13415      * @type String
13416      */
13417     this.sslBlankUrl = d.sslBlankUrl;
13418     /**
13419      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13420      * @type Boolean
13421      */
13422     this.disableCaching = d.disableCaching;
13423     /**
13424      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13425      * @type String
13426      */
13427     this.indicatorText = d.indicatorText;
13428     /**
13429      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13430      * @type String
13431      */
13432     this.showLoadIndicator = d.showLoadIndicator;
13433     /**
13434      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13435      * @type Number
13436      */
13437     this.timeout = d.timeout;
13438
13439     /**
13440      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13441      * @type Boolean
13442      */
13443     this.loadScripts = d.loadScripts;
13444
13445     /**
13446      * Transaction object of current executing transaction
13447      */
13448     this.transaction = null;
13449
13450     /**
13451      * @private
13452      */
13453     this.autoRefreshProcId = null;
13454     /**
13455      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13456      * @type Function
13457      */
13458     this.refreshDelegate = this.refresh.createDelegate(this);
13459     /**
13460      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13461      * @type Function
13462      */
13463     this.updateDelegate = this.update.createDelegate(this);
13464     /**
13465      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13466      * @type Function
13467      */
13468     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13469     /**
13470      * @private
13471      */
13472     this.successDelegate = this.processSuccess.createDelegate(this);
13473     /**
13474      * @private
13475      */
13476     this.failureDelegate = this.processFailure.createDelegate(this);
13477
13478     if(!this.renderer){
13479      /**
13480       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13481       */
13482     this.renderer = new Roo.UpdateManager.BasicRenderer();
13483     }
13484     
13485     Roo.UpdateManager.superclass.constructor.call(this);
13486 };
13487
13488 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13489     /**
13490      * Get the Element this UpdateManager is bound to
13491      * @return {Roo.Element} The element
13492      */
13493     getEl : function(){
13494         return this.el;
13495     },
13496     /**
13497      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13498      * @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:
13499 <pre><code>
13500 um.update({<br/>
13501     url: "your-url.php",<br/>
13502     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13503     callback: yourFunction,<br/>
13504     scope: yourObject, //(optional scope)  <br/>
13505     discardUrl: false, <br/>
13506     nocache: false,<br/>
13507     text: "Loading...",<br/>
13508     timeout: 30,<br/>
13509     scripts: false<br/>
13510 });
13511 </code></pre>
13512      * The only required property is url. The optional properties nocache, text and scripts
13513      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13514      * @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}
13515      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13516      * @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.
13517      */
13518     update : function(url, params, callback, discardUrl){
13519         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13520             var method = this.method,
13521                 cfg;
13522             if(typeof url == "object"){ // must be config object
13523                 cfg = url;
13524                 url = cfg.url;
13525                 params = params || cfg.params;
13526                 callback = callback || cfg.callback;
13527                 discardUrl = discardUrl || cfg.discardUrl;
13528                 if(callback && cfg.scope){
13529                     callback = callback.createDelegate(cfg.scope);
13530                 }
13531                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13532                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13533                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13534                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13535                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13536             }
13537             this.showLoading();
13538             if(!discardUrl){
13539                 this.defaultUrl = url;
13540             }
13541             if(typeof url == "function"){
13542                 url = url.call(this);
13543             }
13544
13545             method = method || (params ? "POST" : "GET");
13546             if(method == "GET"){
13547                 url = this.prepareUrl(url);
13548             }
13549
13550             var o = Roo.apply(cfg ||{}, {
13551                 url : url,
13552                 params: params,
13553                 success: this.successDelegate,
13554                 failure: this.failureDelegate,
13555                 callback: undefined,
13556                 timeout: (this.timeout*1000),
13557                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13558             });
13559             Roo.log("updated manager called with timeout of " + o.timeout);
13560             this.transaction = Roo.Ajax.request(o);
13561         }
13562     },
13563
13564     /**
13565      * 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.
13566      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13567      * @param {String/HTMLElement} form The form Id or form element
13568      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13569      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13570      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13571      */
13572     formUpdate : function(form, url, reset, callback){
13573         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13574             if(typeof url == "function"){
13575                 url = url.call(this);
13576             }
13577             form = Roo.getDom(form);
13578             this.transaction = Roo.Ajax.request({
13579                 form: form,
13580                 url:url,
13581                 success: this.successDelegate,
13582                 failure: this.failureDelegate,
13583                 timeout: (this.timeout*1000),
13584                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13585             });
13586             this.showLoading.defer(1, this);
13587         }
13588     },
13589
13590     /**
13591      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13592      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13593      */
13594     refresh : function(callback){
13595         if(this.defaultUrl == null){
13596             return;
13597         }
13598         this.update(this.defaultUrl, null, callback, true);
13599     },
13600
13601     /**
13602      * Set this element to auto refresh.
13603      * @param {Number} interval How often to update (in seconds).
13604      * @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)
13605      * @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}
13606      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13607      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13608      */
13609     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13610         if(refreshNow){
13611             this.update(url || this.defaultUrl, params, callback, true);
13612         }
13613         if(this.autoRefreshProcId){
13614             clearInterval(this.autoRefreshProcId);
13615         }
13616         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13617     },
13618
13619     /**
13620      * Stop auto refresh on this element.
13621      */
13622      stopAutoRefresh : function(){
13623         if(this.autoRefreshProcId){
13624             clearInterval(this.autoRefreshProcId);
13625             delete this.autoRefreshProcId;
13626         }
13627     },
13628
13629     isAutoRefreshing : function(){
13630        return this.autoRefreshProcId ? true : false;
13631     },
13632     /**
13633      * Called to update the element to "Loading" state. Override to perform custom action.
13634      */
13635     showLoading : function(){
13636         if(this.showLoadIndicator){
13637             this.el.update(this.indicatorText);
13638         }
13639     },
13640
13641     /**
13642      * Adds unique parameter to query string if disableCaching = true
13643      * @private
13644      */
13645     prepareUrl : function(url){
13646         if(this.disableCaching){
13647             var append = "_dc=" + (new Date().getTime());
13648             if(url.indexOf("?") !== -1){
13649                 url += "&" + append;
13650             }else{
13651                 url += "?" + append;
13652             }
13653         }
13654         return url;
13655     },
13656
13657     /**
13658      * @private
13659      */
13660     processSuccess : function(response){
13661         this.transaction = null;
13662         if(response.argument.form && response.argument.reset){
13663             try{ // put in try/catch since some older FF releases had problems with this
13664                 response.argument.form.reset();
13665             }catch(e){}
13666         }
13667         if(this.loadScripts){
13668             this.renderer.render(this.el, response, this,
13669                 this.updateComplete.createDelegate(this, [response]));
13670         }else{
13671             this.renderer.render(this.el, response, this);
13672             this.updateComplete(response);
13673         }
13674     },
13675
13676     updateComplete : function(response){
13677         this.fireEvent("update", this.el, response);
13678         if(typeof response.argument.callback == "function"){
13679             response.argument.callback(this.el, true, response);
13680         }
13681     },
13682
13683     /**
13684      * @private
13685      */
13686     processFailure : function(response){
13687         this.transaction = null;
13688         this.fireEvent("failure", this.el, response);
13689         if(typeof response.argument.callback == "function"){
13690             response.argument.callback(this.el, false, response);
13691         }
13692     },
13693
13694     /**
13695      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13696      * @param {Object} renderer The object implementing the render() method
13697      */
13698     setRenderer : function(renderer){
13699         this.renderer = renderer;
13700     },
13701
13702     getRenderer : function(){
13703        return this.renderer;
13704     },
13705
13706     /**
13707      * Set the defaultUrl used for updates
13708      * @param {String/Function} defaultUrl The url or a function to call to get the url
13709      */
13710     setDefaultUrl : function(defaultUrl){
13711         this.defaultUrl = defaultUrl;
13712     },
13713
13714     /**
13715      * Aborts the executing transaction
13716      */
13717     abort : function(){
13718         if(this.transaction){
13719             Roo.Ajax.abort(this.transaction);
13720         }
13721     },
13722
13723     /**
13724      * Returns true if an update is in progress
13725      * @return {Boolean}
13726      */
13727     isUpdating : function(){
13728         if(this.transaction){
13729             return Roo.Ajax.isLoading(this.transaction);
13730         }
13731         return false;
13732     }
13733 });
13734
13735 /**
13736  * @class Roo.UpdateManager.defaults
13737  * @static (not really - but it helps the doc tool)
13738  * The defaults collection enables customizing the default properties of UpdateManager
13739  */
13740    Roo.UpdateManager.defaults = {
13741        /**
13742          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13743          * @type Number
13744          */
13745          timeout : 30,
13746
13747          /**
13748          * True to process scripts by default (Defaults to false).
13749          * @type Boolean
13750          */
13751         loadScripts : false,
13752
13753         /**
13754         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13755         * @type String
13756         */
13757         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13758         /**
13759          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13760          * @type Boolean
13761          */
13762         disableCaching : false,
13763         /**
13764          * Whether to show indicatorText when loading (Defaults to true).
13765          * @type Boolean
13766          */
13767         showLoadIndicator : true,
13768         /**
13769          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13770          * @type String
13771          */
13772         indicatorText : '<div class="loading-indicator">Loading...</div>'
13773    };
13774
13775 /**
13776  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13777  *Usage:
13778  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13779  * @param {String/HTMLElement/Roo.Element} el The element to update
13780  * @param {String} url The url
13781  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13782  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13783  * @static
13784  * @deprecated
13785  * @member Roo.UpdateManager
13786  */
13787 Roo.UpdateManager.updateElement = function(el, url, params, options){
13788     var um = Roo.get(el, true).getUpdateManager();
13789     Roo.apply(um, options);
13790     um.update(url, params, options ? options.callback : null);
13791 };
13792 // alias for backwards compat
13793 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13794 /**
13795  * @class Roo.UpdateManager.BasicRenderer
13796  * Default Content renderer. Updates the elements innerHTML with the responseText.
13797  */
13798 Roo.UpdateManager.BasicRenderer = function(){};
13799
13800 Roo.UpdateManager.BasicRenderer.prototype = {
13801     /**
13802      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13803      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13804      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13805      * @param {Roo.Element} el The element being rendered
13806      * @param {Object} response The YUI Connect response object
13807      * @param {UpdateManager} updateManager The calling update manager
13808      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13809      */
13810      render : function(el, response, updateManager, callback){
13811         el.update(response.responseText, updateManager.loadScripts, callback);
13812     }
13813 };
13814 /*
13815  * Based on:
13816  * Roo JS
13817  * (c)) Alan Knowles
13818  * Licence : LGPL
13819  */
13820
13821
13822 /**
13823  * @class Roo.DomTemplate
13824  * @extends Roo.Template
13825  * An effort at a dom based template engine..
13826  *
13827  * Similar to XTemplate, except it uses dom parsing to create the template..
13828  *
13829  * Supported features:
13830  *
13831  *  Tags:
13832
13833 <pre><code>
13834       {a_variable} - output encoded.
13835       {a_variable.format:("Y-m-d")} - call a method on the variable
13836       {a_variable:raw} - unencoded output
13837       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13838       {a_variable:this.method_on_template(...)} - call a method on the template object.
13839  
13840 </code></pre>
13841  *  The tpl tag:
13842 <pre><code>
13843         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13844         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13845         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13846         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13847   
13848 </code></pre>
13849  *      
13850  */
13851 Roo.DomTemplate = function()
13852 {
13853      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13854      if (this.html) {
13855         this.compile();
13856      }
13857 };
13858
13859
13860 Roo.extend(Roo.DomTemplate, Roo.Template, {
13861     /**
13862      * id counter for sub templates.
13863      */
13864     id : 0,
13865     /**
13866      * flag to indicate if dom parser is inside a pre,
13867      * it will strip whitespace if not.
13868      */
13869     inPre : false,
13870     
13871     /**
13872      * The various sub templates
13873      */
13874     tpls : false,
13875     
13876     
13877     
13878     /**
13879      *
13880      * basic tag replacing syntax
13881      * WORD:WORD()
13882      *
13883      * // you can fake an object call by doing this
13884      *  x.t:(test,tesT) 
13885      * 
13886      */
13887     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13888     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13889     
13890     iterChild : function (node, method) {
13891         
13892         var oldPre = this.inPre;
13893         if (node.tagName == 'PRE') {
13894             this.inPre = true;
13895         }
13896         for( var i = 0; i < node.childNodes.length; i++) {
13897             method.call(this, node.childNodes[i]);
13898         }
13899         this.inPre = oldPre;
13900     },
13901     
13902     
13903     
13904     /**
13905      * compile the template
13906      *
13907      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13908      *
13909      */
13910     compile: function()
13911     {
13912         var s = this.html;
13913         
13914         // covert the html into DOM...
13915         var doc = false;
13916         var div =false;
13917         try {
13918             doc = document.implementation.createHTMLDocument("");
13919             doc.documentElement.innerHTML =   this.html  ;
13920             div = doc.documentElement;
13921         } catch (e) {
13922             // old IE... - nasty -- it causes all sorts of issues.. with
13923             // images getting pulled from server..
13924             div = document.createElement('div');
13925             div.innerHTML = this.html;
13926         }
13927         //doc.documentElement.innerHTML = htmlBody
13928          
13929         
13930         
13931         this.tpls = [];
13932         var _t = this;
13933         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13934         
13935         var tpls = this.tpls;
13936         
13937         // create a top level template from the snippet..
13938         
13939         //Roo.log(div.innerHTML);
13940         
13941         var tpl = {
13942             uid : 'master',
13943             id : this.id++,
13944             attr : false,
13945             value : false,
13946             body : div.innerHTML,
13947             
13948             forCall : false,
13949             execCall : false,
13950             dom : div,
13951             isTop : true
13952             
13953         };
13954         tpls.unshift(tpl);
13955         
13956         
13957         // compile them...
13958         this.tpls = [];
13959         Roo.each(tpls, function(tp){
13960             this.compileTpl(tp);
13961             this.tpls[tp.id] = tp;
13962         }, this);
13963         
13964         this.master = tpls[0];
13965         return this;
13966         
13967         
13968     },
13969     
13970     compileNode : function(node, istop) {
13971         // test for
13972         //Roo.log(node);
13973         
13974         
13975         // skip anything not a tag..
13976         if (node.nodeType != 1) {
13977             if (node.nodeType == 3 && !this.inPre) {
13978                 // reduce white space..
13979                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13980                 
13981             }
13982             return;
13983         }
13984         
13985         var tpl = {
13986             uid : false,
13987             id : false,
13988             attr : false,
13989             value : false,
13990             body : '',
13991             
13992             forCall : false,
13993             execCall : false,
13994             dom : false,
13995             isTop : istop
13996             
13997             
13998         };
13999         
14000         
14001         switch(true) {
14002             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
14003             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
14004             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
14005             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
14006             // no default..
14007         }
14008         
14009         
14010         if (!tpl.attr) {
14011             // just itterate children..
14012             this.iterChild(node,this.compileNode);
14013             return;
14014         }
14015         tpl.uid = this.id++;
14016         tpl.value = node.getAttribute('roo-' +  tpl.attr);
14017         node.removeAttribute('roo-'+ tpl.attr);
14018         if (tpl.attr != 'name') {
14019             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
14020             node.parentNode.replaceChild(placeholder,  node);
14021         } else {
14022             
14023             var placeholder =  document.createElement('span');
14024             placeholder.className = 'roo-tpl-' + tpl.value;
14025             node.parentNode.replaceChild(placeholder,  node);
14026         }
14027         
14028         // parent now sees '{domtplXXXX}
14029         this.iterChild(node,this.compileNode);
14030         
14031         // we should now have node body...
14032         var div = document.createElement('div');
14033         div.appendChild(node);
14034         tpl.dom = node;
14035         // this has the unfortunate side effect of converting tagged attributes
14036         // eg. href="{...}" into %7C...%7D
14037         // this has been fixed by searching for those combo's although it's a bit hacky..
14038         
14039         
14040         tpl.body = div.innerHTML;
14041         
14042         
14043          
14044         tpl.id = tpl.uid;
14045         switch(tpl.attr) {
14046             case 'for' :
14047                 switch (tpl.value) {
14048                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
14049                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
14050                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
14051                 }
14052                 break;
14053             
14054             case 'exec':
14055                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14056                 break;
14057             
14058             case 'if':     
14059                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14060                 break;
14061             
14062             case 'name':
14063                 tpl.id  = tpl.value; // replace non characters???
14064                 break;
14065             
14066         }
14067         
14068         
14069         this.tpls.push(tpl);
14070         
14071         
14072         
14073     },
14074     
14075     
14076     
14077     
14078     /**
14079      * Compile a segment of the template into a 'sub-template'
14080      *
14081      * 
14082      * 
14083      *
14084      */
14085     compileTpl : function(tpl)
14086     {
14087         var fm = Roo.util.Format;
14088         var useF = this.disableFormats !== true;
14089         
14090         var sep = Roo.isGecko ? "+\n" : ",\n";
14091         
14092         var undef = function(str) {
14093             Roo.debug && Roo.log("Property not found :"  + str);
14094             return '';
14095         };
14096           
14097         //Roo.log(tpl.body);
14098         
14099         
14100         
14101         var fn = function(m, lbrace, name, format, args)
14102         {
14103             //Roo.log("ARGS");
14104             //Roo.log(arguments);
14105             args = args ? args.replace(/\\'/g,"'") : args;
14106             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
14107             if (typeof(format) == 'undefined') {
14108                 format =  'htmlEncode'; 
14109             }
14110             if (format == 'raw' ) {
14111                 format = false;
14112             }
14113             
14114             if(name.substr(0, 6) == 'domtpl'){
14115                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
14116             }
14117             
14118             // build an array of options to determine if value is undefined..
14119             
14120             // basically get 'xxxx.yyyy' then do
14121             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
14122             //    (function () { Roo.log("Property not found"); return ''; })() :
14123             //    ......
14124             
14125             var udef_ar = [];
14126             var lookfor = '';
14127             Roo.each(name.split('.'), function(st) {
14128                 lookfor += (lookfor.length ? '.': '') + st;
14129                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
14130             });
14131             
14132             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
14133             
14134             
14135             if(format && useF){
14136                 
14137                 args = args ? ',' + args : "";
14138                  
14139                 if(format.substr(0, 5) != "this."){
14140                     format = "fm." + format + '(';
14141                 }else{
14142                     format = 'this.call("'+ format.substr(5) + '", ';
14143                     args = ", values";
14144                 }
14145                 
14146                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
14147             }
14148              
14149             if (args && args.length) {
14150                 // called with xxyx.yuu:(test,test)
14151                 // change to ()
14152                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
14153             }
14154             // raw.. - :raw modifier..
14155             return "'"+ sep + udef_st  + name + ")"+sep+"'";
14156             
14157         };
14158         var body;
14159         // branched to use + in gecko and [].join() in others
14160         if(Roo.isGecko){
14161             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
14162                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
14163                     "';};};";
14164         }else{
14165             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
14166             body.push(tpl.body.replace(/(\r\n|\n)/g,
14167                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
14168             body.push("'].join('');};};");
14169             body = body.join('');
14170         }
14171         
14172         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
14173        
14174         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
14175         eval(body);
14176         
14177         return this;
14178     },
14179      
14180     /**
14181      * same as applyTemplate, except it's done to one of the subTemplates
14182      * when using named templates, you can do:
14183      *
14184      * var str = pl.applySubTemplate('your-name', values);
14185      *
14186      * 
14187      * @param {Number} id of the template
14188      * @param {Object} values to apply to template
14189      * @param {Object} parent (normaly the instance of this object)
14190      */
14191     applySubTemplate : function(id, values, parent)
14192     {
14193         
14194         
14195         var t = this.tpls[id];
14196         
14197         
14198         try { 
14199             if(t.ifCall && !t.ifCall.call(this, values, parent)){
14200                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14201                 return '';
14202             }
14203         } catch(e) {
14204             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14205             Roo.log(values);
14206           
14207             return '';
14208         }
14209         try { 
14210             
14211             if(t.execCall && t.execCall.call(this, values, parent)){
14212                 return '';
14213             }
14214         } catch(e) {
14215             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14216             Roo.log(values);
14217             return '';
14218         }
14219         
14220         try {
14221             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14222             parent = t.target ? values : parent;
14223             if(t.forCall && vs instanceof Array){
14224                 var buf = [];
14225                 for(var i = 0, len = vs.length; i < len; i++){
14226                     try {
14227                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
14228                     } catch (e) {
14229                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14230                         Roo.log(e.body);
14231                         //Roo.log(t.compiled);
14232                         Roo.log(vs[i]);
14233                     }   
14234                 }
14235                 return buf.join('');
14236             }
14237         } catch (e) {
14238             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14239             Roo.log(values);
14240             return '';
14241         }
14242         try {
14243             return t.compiled.call(this, vs, parent);
14244         } catch (e) {
14245             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14246             Roo.log(e.body);
14247             //Roo.log(t.compiled);
14248             Roo.log(values);
14249             return '';
14250         }
14251     },
14252
14253    
14254
14255     applyTemplate : function(values){
14256         return this.master.compiled.call(this, values, {});
14257         //var s = this.subs;
14258     },
14259
14260     apply : function(){
14261         return this.applyTemplate.apply(this, arguments);
14262     }
14263
14264  });
14265
14266 Roo.DomTemplate.from = function(el){
14267     el = Roo.getDom(el);
14268     return new Roo.Domtemplate(el.value || el.innerHTML);
14269 };/*
14270  * Based on:
14271  * Ext JS Library 1.1.1
14272  * Copyright(c) 2006-2007, Ext JS, LLC.
14273  *
14274  * Originally Released Under LGPL - original licence link has changed is not relivant.
14275  *
14276  * Fork - LGPL
14277  * <script type="text/javascript">
14278  */
14279
14280 /**
14281  * @class Roo.util.DelayedTask
14282  * Provides a convenient method of performing setTimeout where a new
14283  * timeout cancels the old timeout. An example would be performing validation on a keypress.
14284  * You can use this class to buffer
14285  * the keypress events for a certain number of milliseconds, and perform only if they stop
14286  * for that amount of time.
14287  * @constructor The parameters to this constructor serve as defaults and are not required.
14288  * @param {Function} fn (optional) The default function to timeout
14289  * @param {Object} scope (optional) The default scope of that timeout
14290  * @param {Array} args (optional) The default Array of arguments
14291  */
14292 Roo.util.DelayedTask = function(fn, scope, args){
14293     var id = null, d, t;
14294
14295     var call = function(){
14296         var now = new Date().getTime();
14297         if(now - t >= d){
14298             clearInterval(id);
14299             id = null;
14300             fn.apply(scope, args || []);
14301         }
14302     };
14303     /**
14304      * Cancels any pending timeout and queues a new one
14305      * @param {Number} delay The milliseconds to delay
14306      * @param {Function} newFn (optional) Overrides function passed to constructor
14307      * @param {Object} newScope (optional) Overrides scope passed to constructor
14308      * @param {Array} newArgs (optional) Overrides args passed to constructor
14309      */
14310     this.delay = function(delay, newFn, newScope, newArgs){
14311         if(id && delay != d){
14312             this.cancel();
14313         }
14314         d = delay;
14315         t = new Date().getTime();
14316         fn = newFn || fn;
14317         scope = newScope || scope;
14318         args = newArgs || args;
14319         if(!id){
14320             id = setInterval(call, d);
14321         }
14322     };
14323
14324     /**
14325      * Cancel the last queued timeout
14326      */
14327     this.cancel = function(){
14328         if(id){
14329             clearInterval(id);
14330             id = null;
14331         }
14332     };
14333 };/*
14334  * Based on:
14335  * Ext JS Library 1.1.1
14336  * Copyright(c) 2006-2007, Ext JS, LLC.
14337  *
14338  * Originally Released Under LGPL - original licence link has changed is not relivant.
14339  *
14340  * Fork - LGPL
14341  * <script type="text/javascript">
14342  */
14343 /**
14344  * @class Roo.util.TaskRunner
14345  * Manage background tasks - not sure why this is better that setInterval?
14346  * @static
14347  *
14348  */
14349  
14350 Roo.util.TaskRunner = function(interval){
14351     interval = interval || 10;
14352     var tasks = [], removeQueue = [];
14353     var id = 0;
14354     var running = false;
14355
14356     var stopThread = function(){
14357         running = false;
14358         clearInterval(id);
14359         id = 0;
14360     };
14361
14362     var startThread = function(){
14363         if(!running){
14364             running = true;
14365             id = setInterval(runTasks, interval);
14366         }
14367     };
14368
14369     var removeTask = function(task){
14370         removeQueue.push(task);
14371         if(task.onStop){
14372             task.onStop();
14373         }
14374     };
14375
14376     var runTasks = function(){
14377         if(removeQueue.length > 0){
14378             for(var i = 0, len = removeQueue.length; i < len; i++){
14379                 tasks.remove(removeQueue[i]);
14380             }
14381             removeQueue = [];
14382             if(tasks.length < 1){
14383                 stopThread();
14384                 return;
14385             }
14386         }
14387         var now = new Date().getTime();
14388         for(var i = 0, len = tasks.length; i < len; ++i){
14389             var t = tasks[i];
14390             var itime = now - t.taskRunTime;
14391             if(t.interval <= itime){
14392                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14393                 t.taskRunTime = now;
14394                 if(rt === false || t.taskRunCount === t.repeat){
14395                     removeTask(t);
14396                     return;
14397                 }
14398             }
14399             if(t.duration && t.duration <= (now - t.taskStartTime)){
14400                 removeTask(t);
14401             }
14402         }
14403     };
14404
14405     /**
14406      * Queues a new task.
14407      * @param {Object} task
14408      *
14409      * Task property : interval = how frequent to run.
14410      * Task object should implement
14411      * function run()
14412      * Task object may implement
14413      * function onStop()
14414      */
14415     this.start = function(task){
14416         tasks.push(task);
14417         task.taskStartTime = new Date().getTime();
14418         task.taskRunTime = 0;
14419         task.taskRunCount = 0;
14420         startThread();
14421         return task;
14422     };
14423     /**
14424      * Stop  new task.
14425      * @param {Object} task
14426      */
14427     this.stop = function(task){
14428         removeTask(task);
14429         return task;
14430     };
14431     /**
14432      * Stop all Tasks
14433      */
14434     this.stopAll = function(){
14435         stopThread();
14436         for(var i = 0, len = tasks.length; i < len; i++){
14437             if(tasks[i].onStop){
14438                 tasks[i].onStop();
14439             }
14440         }
14441         tasks = [];
14442         removeQueue = [];
14443     };
14444 };
14445
14446 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14447  * Based on:
14448  * Ext JS Library 1.1.1
14449  * Copyright(c) 2006-2007, Ext JS, LLC.
14450  *
14451  * Originally Released Under LGPL - original licence link has changed is not relivant.
14452  *
14453  * Fork - LGPL
14454  * <script type="text/javascript">
14455  */
14456
14457  
14458 /**
14459  * @class Roo.util.MixedCollection
14460  * @extends Roo.util.Observable
14461  * A Collection class that maintains both numeric indexes and keys and exposes events.
14462  * @constructor
14463  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14464  * collection (defaults to false)
14465  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14466  * and return the key value for that item.  This is used when available to look up the key on items that
14467  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14468  * equivalent to providing an implementation for the {@link #getKey} method.
14469  */
14470 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14471     this.items = [];
14472     this.map = {};
14473     this.keys = [];
14474     this.length = 0;
14475     this.addEvents({
14476         /**
14477          * @event clear
14478          * Fires when the collection is cleared.
14479          */
14480         "clear" : true,
14481         /**
14482          * @event add
14483          * Fires when an item is added to the collection.
14484          * @param {Number} index The index at which the item was added.
14485          * @param {Object} o The item added.
14486          * @param {String} key The key associated with the added item.
14487          */
14488         "add" : true,
14489         /**
14490          * @event replace
14491          * Fires when an item is replaced in the collection.
14492          * @param {String} key he key associated with the new added.
14493          * @param {Object} old The item being replaced.
14494          * @param {Object} new The new item.
14495          */
14496         "replace" : true,
14497         /**
14498          * @event remove
14499          * Fires when an item is removed from the collection.
14500          * @param {Object} o The item being removed.
14501          * @param {String} key (optional) The key associated with the removed item.
14502          */
14503         "remove" : true,
14504         "sort" : true
14505     });
14506     this.allowFunctions = allowFunctions === true;
14507     if(keyFn){
14508         this.getKey = keyFn;
14509     }
14510     Roo.util.MixedCollection.superclass.constructor.call(this);
14511 };
14512
14513 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14514     allowFunctions : false,
14515     
14516 /**
14517  * Adds an item to the collection.
14518  * @param {String} key The key to associate with the item
14519  * @param {Object} o The item to add.
14520  * @return {Object} The item added.
14521  */
14522     add : function(key, o){
14523         if(arguments.length == 1){
14524             o = arguments[0];
14525             key = this.getKey(o);
14526         }
14527         if(typeof key == "undefined" || key === null){
14528             this.length++;
14529             this.items.push(o);
14530             this.keys.push(null);
14531         }else{
14532             var old = this.map[key];
14533             if(old){
14534                 return this.replace(key, o);
14535             }
14536             this.length++;
14537             this.items.push(o);
14538             this.map[key] = o;
14539             this.keys.push(key);
14540         }
14541         this.fireEvent("add", this.length-1, o, key);
14542         return o;
14543     },
14544        
14545 /**
14546   * MixedCollection has a generic way to fetch keys if you implement getKey.
14547 <pre><code>
14548 // normal way
14549 var mc = new Roo.util.MixedCollection();
14550 mc.add(someEl.dom.id, someEl);
14551 mc.add(otherEl.dom.id, otherEl);
14552 //and so on
14553
14554 // using getKey
14555 var mc = new Roo.util.MixedCollection();
14556 mc.getKey = function(el){
14557    return el.dom.id;
14558 };
14559 mc.add(someEl);
14560 mc.add(otherEl);
14561
14562 // or via the constructor
14563 var mc = new Roo.util.MixedCollection(false, function(el){
14564    return el.dom.id;
14565 });
14566 mc.add(someEl);
14567 mc.add(otherEl);
14568 </code></pre>
14569  * @param o {Object} The item for which to find the key.
14570  * @return {Object} The key for the passed item.
14571  */
14572     getKey : function(o){
14573          return o.id; 
14574     },
14575    
14576 /**
14577  * Replaces an item in the collection.
14578  * @param {String} key The key associated with the item to replace, or the item to replace.
14579  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14580  * @return {Object}  The new item.
14581  */
14582     replace : function(key, o){
14583         if(arguments.length == 1){
14584             o = arguments[0];
14585             key = this.getKey(o);
14586         }
14587         var old = this.item(key);
14588         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14589              return this.add(key, o);
14590         }
14591         var index = this.indexOfKey(key);
14592         this.items[index] = o;
14593         this.map[key] = o;
14594         this.fireEvent("replace", key, old, o);
14595         return o;
14596     },
14597    
14598 /**
14599  * Adds all elements of an Array or an Object to the collection.
14600  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14601  * an Array of values, each of which are added to the collection.
14602  */
14603     addAll : function(objs){
14604         if(arguments.length > 1 || objs instanceof Array){
14605             var args = arguments.length > 1 ? arguments : objs;
14606             for(var i = 0, len = args.length; i < len; i++){
14607                 this.add(args[i]);
14608             }
14609         }else{
14610             for(var key in objs){
14611                 if(this.allowFunctions || typeof objs[key] != "function"){
14612                     this.add(key, objs[key]);
14613                 }
14614             }
14615         }
14616     },
14617    
14618 /**
14619  * Executes the specified function once for every item in the collection, passing each
14620  * item as the first and only parameter. returning false from the function will stop the iteration.
14621  * @param {Function} fn The function to execute for each item.
14622  * @param {Object} scope (optional) The scope in which to execute the function.
14623  */
14624     each : function(fn, scope){
14625         var items = [].concat(this.items); // each safe for removal
14626         for(var i = 0, len = items.length; i < len; i++){
14627             if(fn.call(scope || items[i], items[i], i, len) === false){
14628                 break;
14629             }
14630         }
14631     },
14632    
14633 /**
14634  * Executes the specified function once for every key in the collection, passing each
14635  * key, and its associated item as the first two parameters.
14636  * @param {Function} fn The function to execute for each item.
14637  * @param {Object} scope (optional) The scope in which to execute the function.
14638  */
14639     eachKey : function(fn, scope){
14640         for(var i = 0, len = this.keys.length; i < len; i++){
14641             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14642         }
14643     },
14644    
14645 /**
14646  * Returns the first item in the collection which elicits a true return value from the
14647  * passed selection function.
14648  * @param {Function} fn The selection function to execute for each item.
14649  * @param {Object} scope (optional) The scope in which to execute the function.
14650  * @return {Object} The first item in the collection which returned true from the selection function.
14651  */
14652     find : function(fn, scope){
14653         for(var i = 0, len = this.items.length; i < len; i++){
14654             if(fn.call(scope || window, this.items[i], this.keys[i])){
14655                 return this.items[i];
14656             }
14657         }
14658         return null;
14659     },
14660    
14661 /**
14662  * Inserts an item at the specified index in the collection.
14663  * @param {Number} index The index to insert the item at.
14664  * @param {String} key The key to associate with the new item, or the item itself.
14665  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14666  * @return {Object} The item inserted.
14667  */
14668     insert : function(index, key, o){
14669         if(arguments.length == 2){
14670             o = arguments[1];
14671             key = this.getKey(o);
14672         }
14673         if(index >= this.length){
14674             return this.add(key, o);
14675         }
14676         this.length++;
14677         this.items.splice(index, 0, o);
14678         if(typeof key != "undefined" && key != null){
14679             this.map[key] = o;
14680         }
14681         this.keys.splice(index, 0, key);
14682         this.fireEvent("add", index, o, key);
14683         return o;
14684     },
14685    
14686 /**
14687  * Removed an item from the collection.
14688  * @param {Object} o The item to remove.
14689  * @return {Object} The item removed.
14690  */
14691     remove : function(o){
14692         return this.removeAt(this.indexOf(o));
14693     },
14694    
14695 /**
14696  * Remove an item from a specified index in the collection.
14697  * @param {Number} index The index within the collection of the item to remove.
14698  */
14699     removeAt : function(index){
14700         if(index < this.length && index >= 0){
14701             this.length--;
14702             var o = this.items[index];
14703             this.items.splice(index, 1);
14704             var key = this.keys[index];
14705             if(typeof key != "undefined"){
14706                 delete this.map[key];
14707             }
14708             this.keys.splice(index, 1);
14709             this.fireEvent("remove", o, key);
14710         }
14711     },
14712    
14713 /**
14714  * Removed an item associated with the passed key fom the collection.
14715  * @param {String} key The key of the item to remove.
14716  */
14717     removeKey : function(key){
14718         return this.removeAt(this.indexOfKey(key));
14719     },
14720    
14721 /**
14722  * Returns the number of items in the collection.
14723  * @return {Number} the number of items in the collection.
14724  */
14725     getCount : function(){
14726         return this.length; 
14727     },
14728    
14729 /**
14730  * Returns index within the collection of the passed Object.
14731  * @param {Object} o The item to find the index of.
14732  * @return {Number} index of the item.
14733  */
14734     indexOf : function(o){
14735         if(!this.items.indexOf){
14736             for(var i = 0, len = this.items.length; i < len; i++){
14737                 if(this.items[i] == o) {
14738                     return i;
14739                 }
14740             }
14741             return -1;
14742         }else{
14743             return this.items.indexOf(o);
14744         }
14745     },
14746    
14747 /**
14748  * Returns index within the collection of the passed key.
14749  * @param {String} key The key to find the index of.
14750  * @return {Number} index of the key.
14751  */
14752     indexOfKey : function(key){
14753         if(!this.keys.indexOf){
14754             for(var i = 0, len = this.keys.length; i < len; i++){
14755                 if(this.keys[i] == key) {
14756                     return i;
14757                 }
14758             }
14759             return -1;
14760         }else{
14761             return this.keys.indexOf(key);
14762         }
14763     },
14764    
14765 /**
14766  * Returns the item associated with the passed key OR index. Key has priority over index.
14767  * @param {String/Number} key The key or index of the item.
14768  * @return {Object} The item associated with the passed key.
14769  */
14770     item : function(key){
14771         if (key === 'length') {
14772             return null;
14773         }
14774         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14775         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14776     },
14777     
14778 /**
14779  * Returns the item at the specified index.
14780  * @param {Number} index The index of the item.
14781  * @return {Object}
14782  */
14783     itemAt : function(index){
14784         return this.items[index];
14785     },
14786     
14787 /**
14788  * Returns the item associated with the passed key.
14789  * @param {String/Number} key The key of the item.
14790  * @return {Object} The item associated with the passed key.
14791  */
14792     key : function(key){
14793         return this.map[key];
14794     },
14795    
14796 /**
14797  * Returns true if the collection contains the passed Object as an item.
14798  * @param {Object} o  The Object to look for in the collection.
14799  * @return {Boolean} True if the collection contains the Object as an item.
14800  */
14801     contains : function(o){
14802         return this.indexOf(o) != -1;
14803     },
14804    
14805 /**
14806  * Returns true if the collection contains the passed Object as a key.
14807  * @param {String} key The key to look for in the collection.
14808  * @return {Boolean} True if the collection contains the Object as a key.
14809  */
14810     containsKey : function(key){
14811         return typeof this.map[key] != "undefined";
14812     },
14813    
14814 /**
14815  * Removes all items from the collection.
14816  */
14817     clear : function(){
14818         this.length = 0;
14819         this.items = [];
14820         this.keys = [];
14821         this.map = {};
14822         this.fireEvent("clear");
14823     },
14824    
14825 /**
14826  * Returns the first item in the collection.
14827  * @return {Object} the first item in the collection..
14828  */
14829     first : function(){
14830         return this.items[0]; 
14831     },
14832    
14833 /**
14834  * Returns the last item in the collection.
14835  * @return {Object} the last item in the collection..
14836  */
14837     last : function(){
14838         return this.items[this.length-1];   
14839     },
14840     
14841     _sort : function(property, dir, fn){
14842         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14843         fn = fn || function(a, b){
14844             return a-b;
14845         };
14846         var c = [], k = this.keys, items = this.items;
14847         for(var i = 0, len = items.length; i < len; i++){
14848             c[c.length] = {key: k[i], value: items[i], index: i};
14849         }
14850         c.sort(function(a, b){
14851             var v = fn(a[property], b[property]) * dsc;
14852             if(v == 0){
14853                 v = (a.index < b.index ? -1 : 1);
14854             }
14855             return v;
14856         });
14857         for(var i = 0, len = c.length; i < len; i++){
14858             items[i] = c[i].value;
14859             k[i] = c[i].key;
14860         }
14861         this.fireEvent("sort", this);
14862     },
14863     
14864     /**
14865      * Sorts this collection with the passed comparison function
14866      * @param {String} direction (optional) "ASC" or "DESC"
14867      * @param {Function} fn (optional) comparison function
14868      */
14869     sort : function(dir, fn){
14870         this._sort("value", dir, fn);
14871     },
14872     
14873     /**
14874      * Sorts this collection by keys
14875      * @param {String} direction (optional) "ASC" or "DESC"
14876      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14877      */
14878     keySort : function(dir, fn){
14879         this._sort("key", dir, fn || function(a, b){
14880             return String(a).toUpperCase()-String(b).toUpperCase();
14881         });
14882     },
14883     
14884     /**
14885      * Returns a range of items in this collection
14886      * @param {Number} startIndex (optional) defaults to 0
14887      * @param {Number} endIndex (optional) default to the last item
14888      * @return {Array} An array of items
14889      */
14890     getRange : function(start, end){
14891         var items = this.items;
14892         if(items.length < 1){
14893             return [];
14894         }
14895         start = start || 0;
14896         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14897         var r = [];
14898         if(start <= end){
14899             for(var i = start; i <= end; i++) {
14900                     r[r.length] = items[i];
14901             }
14902         }else{
14903             for(var i = start; i >= end; i--) {
14904                     r[r.length] = items[i];
14905             }
14906         }
14907         return r;
14908     },
14909         
14910     /**
14911      * Filter the <i>objects</i> in this collection by a specific property. 
14912      * Returns a new collection that has been filtered.
14913      * @param {String} property A property on your objects
14914      * @param {String/RegExp} value Either string that the property values 
14915      * should start with or a RegExp to test against the property
14916      * @return {MixedCollection} The new filtered collection
14917      */
14918     filter : function(property, value){
14919         if(!value.exec){ // not a regex
14920             value = String(value);
14921             if(value.length == 0){
14922                 return this.clone();
14923             }
14924             value = new RegExp("^" + Roo.escapeRe(value), "i");
14925         }
14926         return this.filterBy(function(o){
14927             return o && value.test(o[property]);
14928         });
14929         },
14930     
14931     /**
14932      * Filter by a function. * Returns a new collection that has been filtered.
14933      * The passed function will be called with each 
14934      * object in the collection. If the function returns true, the value is included 
14935      * otherwise it is filtered.
14936      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14937      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14938      * @return {MixedCollection} The new filtered collection
14939      */
14940     filterBy : function(fn, scope){
14941         var r = new Roo.util.MixedCollection();
14942         r.getKey = this.getKey;
14943         var k = this.keys, it = this.items;
14944         for(var i = 0, len = it.length; i < len; i++){
14945             if(fn.call(scope||this, it[i], k[i])){
14946                                 r.add(k[i], it[i]);
14947                         }
14948         }
14949         return r;
14950     },
14951     
14952     /**
14953      * Creates a duplicate of this collection
14954      * @return {MixedCollection}
14955      */
14956     clone : function(){
14957         var r = new Roo.util.MixedCollection();
14958         var k = this.keys, it = this.items;
14959         for(var i = 0, len = it.length; i < len; i++){
14960             r.add(k[i], it[i]);
14961         }
14962         r.getKey = this.getKey;
14963         return r;
14964     }
14965 });
14966 /**
14967  * Returns the item associated with the passed key or index.
14968  * @method
14969  * @param {String/Number} key The key or index of the item.
14970  * @return {Object} The item associated with the passed key.
14971  */
14972 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14973  * Based on:
14974  * Ext JS Library 1.1.1
14975  * Copyright(c) 2006-2007, Ext JS, LLC.
14976  *
14977  * Originally Released Under LGPL - original licence link has changed is not relivant.
14978  *
14979  * Fork - LGPL
14980  * <script type="text/javascript">
14981  */
14982 /**
14983  * @class Roo.util.JSON
14984  * Modified version of Douglas Crockford"s json.js that doesn"t
14985  * mess with the Object prototype 
14986  * http://www.json.org/js.html
14987  * @static
14988  */
14989 Roo.util.JSON = new (function(){
14990     var useHasOwn = {}.hasOwnProperty ? true : false;
14991     
14992     // crashes Safari in some instances
14993     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14994     
14995     var pad = function(n) {
14996         return n < 10 ? "0" + n : n;
14997     };
14998     
14999     var m = {
15000         "\b": '\\b',
15001         "\t": '\\t',
15002         "\n": '\\n',
15003         "\f": '\\f',
15004         "\r": '\\r',
15005         '"' : '\\"',
15006         "\\": '\\\\'
15007     };
15008
15009     var encodeString = function(s){
15010         if (/["\\\x00-\x1f]/.test(s)) {
15011             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
15012                 var c = m[b];
15013                 if(c){
15014                     return c;
15015                 }
15016                 c = b.charCodeAt();
15017                 return "\\u00" +
15018                     Math.floor(c / 16).toString(16) +
15019                     (c % 16).toString(16);
15020             }) + '"';
15021         }
15022         return '"' + s + '"';
15023     };
15024     
15025     var encodeArray = function(o){
15026         var a = ["["], b, i, l = o.length, v;
15027             for (i = 0; i < l; i += 1) {
15028                 v = o[i];
15029                 switch (typeof v) {
15030                     case "undefined":
15031                     case "function":
15032                     case "unknown":
15033                         break;
15034                     default:
15035                         if (b) {
15036                             a.push(',');
15037                         }
15038                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
15039                         b = true;
15040                 }
15041             }
15042             a.push("]");
15043             return a.join("");
15044     };
15045     
15046     var encodeDate = function(o){
15047         return '"' + o.getFullYear() + "-" +
15048                 pad(o.getMonth() + 1) + "-" +
15049                 pad(o.getDate()) + "T" +
15050                 pad(o.getHours()) + ":" +
15051                 pad(o.getMinutes()) + ":" +
15052                 pad(o.getSeconds()) + '"';
15053     };
15054     
15055     /**
15056      * Encodes an Object, Array or other value
15057      * @param {Mixed} o The variable to encode
15058      * @return {String} The JSON string
15059      */
15060     this.encode = function(o)
15061     {
15062         // should this be extended to fully wrap stringify..
15063         
15064         if(typeof o == "undefined" || o === null){
15065             return "null";
15066         }else if(o instanceof Array){
15067             return encodeArray(o);
15068         }else if(o instanceof Date){
15069             return encodeDate(o);
15070         }else if(typeof o == "string"){
15071             return encodeString(o);
15072         }else if(typeof o == "number"){
15073             return isFinite(o) ? String(o) : "null";
15074         }else if(typeof o == "boolean"){
15075             return String(o);
15076         }else {
15077             var a = ["{"], b, i, v;
15078             for (i in o) {
15079                 if(!useHasOwn || o.hasOwnProperty(i)) {
15080                     v = o[i];
15081                     switch (typeof v) {
15082                     case "undefined":
15083                     case "function":
15084                     case "unknown":
15085                         break;
15086                     default:
15087                         if(b){
15088                             a.push(',');
15089                         }
15090                         a.push(this.encode(i), ":",
15091                                 v === null ? "null" : this.encode(v));
15092                         b = true;
15093                     }
15094                 }
15095             }
15096             a.push("}");
15097             return a.join("");
15098         }
15099     };
15100     
15101     /**
15102      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
15103      * @param {String} json The JSON string
15104      * @return {Object} The resulting object
15105      */
15106     this.decode = function(json){
15107         
15108         return  /** eval:var:json */ eval("(" + json + ')');
15109     };
15110 })();
15111 /** 
15112  * Shorthand for {@link Roo.util.JSON#encode}
15113  * @member Roo encode 
15114  * @method */
15115 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
15116 /** 
15117  * Shorthand for {@link Roo.util.JSON#decode}
15118  * @member Roo decode 
15119  * @method */
15120 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
15121 /*
15122  * Based on:
15123  * Ext JS Library 1.1.1
15124  * Copyright(c) 2006-2007, Ext JS, LLC.
15125  *
15126  * Originally Released Under LGPL - original licence link has changed is not relivant.
15127  *
15128  * Fork - LGPL
15129  * <script type="text/javascript">
15130  */
15131  
15132 /**
15133  * @class Roo.util.Format
15134  * Reusable data formatting functions
15135  * @static
15136  */
15137 Roo.util.Format = function(){
15138     var trimRe = /^\s+|\s+$/g;
15139     return {
15140         /**
15141          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
15142          * @param {String} value The string to truncate
15143          * @param {Number} length The maximum length to allow before truncating
15144          * @return {String} The converted text
15145          */
15146         ellipsis : function(value, len){
15147             if(value && value.length > len){
15148                 return value.substr(0, len-3)+"...";
15149             }
15150             return value;
15151         },
15152
15153         /**
15154          * Checks a reference and converts it to empty string if it is undefined
15155          * @param {Mixed} value Reference to check
15156          * @return {Mixed} Empty string if converted, otherwise the original value
15157          */
15158         undef : function(value){
15159             return typeof value != "undefined" ? value : "";
15160         },
15161
15162         /**
15163          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
15164          * @param {String} value The string to encode
15165          * @return {String} The encoded text
15166          */
15167         htmlEncode : function(value){
15168             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
15169         },
15170
15171         /**
15172          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
15173          * @param {String} value The string to decode
15174          * @return {String} The decoded text
15175          */
15176         htmlDecode : function(value){
15177             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
15178         },
15179
15180         /**
15181          * Trims any whitespace from either side of a string
15182          * @param {String} value The text to trim
15183          * @return {String} The trimmed text
15184          */
15185         trim : function(value){
15186             return String(value).replace(trimRe, "");
15187         },
15188
15189         /**
15190          * Returns a substring from within an original string
15191          * @param {String} value The original text
15192          * @param {Number} start The start index of the substring
15193          * @param {Number} length The length of the substring
15194          * @return {String} The substring
15195          */
15196         substr : function(value, start, length){
15197             return String(value).substr(start, length);
15198         },
15199
15200         /**
15201          * Converts a string to all lower case letters
15202          * @param {String} value The text to convert
15203          * @return {String} The converted text
15204          */
15205         lowercase : function(value){
15206             return String(value).toLowerCase();
15207         },
15208
15209         /**
15210          * Converts a string to all upper case letters
15211          * @param {String} value The text to convert
15212          * @return {String} The converted text
15213          */
15214         uppercase : function(value){
15215             return String(value).toUpperCase();
15216         },
15217
15218         /**
15219          * Converts the first character only of a string to upper case
15220          * @param {String} value The text to convert
15221          * @return {String} The converted text
15222          */
15223         capitalize : function(value){
15224             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15225         },
15226
15227         // private
15228         call : function(value, fn){
15229             if(arguments.length > 2){
15230                 var args = Array.prototype.slice.call(arguments, 2);
15231                 args.unshift(value);
15232                  
15233                 return /** eval:var:value */  eval(fn).apply(window, args);
15234             }else{
15235                 /** eval:var:value */
15236                 return /** eval:var:value */ eval(fn).call(window, value);
15237             }
15238         },
15239
15240        
15241         /**
15242          * safer version of Math.toFixed..??/
15243          * @param {Number/String} value The numeric value to format
15244          * @param {Number/String} value Decimal places 
15245          * @return {String} The formatted currency string
15246          */
15247         toFixed : function(v, n)
15248         {
15249             // why not use to fixed - precision is buggered???
15250             if (!n) {
15251                 return Math.round(v-0);
15252             }
15253             var fact = Math.pow(10,n+1);
15254             v = (Math.round((v-0)*fact))/fact;
15255             var z = (''+fact).substring(2);
15256             if (v == Math.floor(v)) {
15257                 return Math.floor(v) + '.' + z;
15258             }
15259             
15260             // now just padd decimals..
15261             var ps = String(v).split('.');
15262             var fd = (ps[1] + z);
15263             var r = fd.substring(0,n); 
15264             var rm = fd.substring(n); 
15265             if (rm < 5) {
15266                 return ps[0] + '.' + r;
15267             }
15268             r*=1; // turn it into a number;
15269             r++;
15270             if (String(r).length != n) {
15271                 ps[0]*=1;
15272                 ps[0]++;
15273                 r = String(r).substring(1); // chop the end off.
15274             }
15275             
15276             return ps[0] + '.' + r;
15277              
15278         },
15279         
15280         /**
15281          * Format a number as US currency
15282          * @param {Number/String} value The numeric value to format
15283          * @return {String} The formatted currency string
15284          */
15285         usMoney : function(v){
15286             return '$' + Roo.util.Format.number(v);
15287         },
15288         
15289         /**
15290          * Format a number
15291          * eventually this should probably emulate php's number_format
15292          * @param {Number/String} value The numeric value to format
15293          * @param {Number} decimals number of decimal places
15294          * @param {String} delimiter for thousands (default comma)
15295          * @return {String} The formatted currency string
15296          */
15297         number : function(v, decimals, thousandsDelimiter)
15298         {
15299             // multiply and round.
15300             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15301             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15302             
15303             var mul = Math.pow(10, decimals);
15304             var zero = String(mul).substring(1);
15305             v = (Math.round((v-0)*mul))/mul;
15306             
15307             // if it's '0' number.. then
15308             
15309             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15310             v = String(v);
15311             var ps = v.split('.');
15312             var whole = ps[0];
15313             
15314             var r = /(\d+)(\d{3})/;
15315             // add comma's
15316             
15317             if(thousandsDelimiter.length != 0) {
15318                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15319             } 
15320             
15321             var sub = ps[1] ?
15322                     // has decimals..
15323                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15324                     // does not have decimals
15325                     (decimals ? ('.' + zero) : '');
15326             
15327             
15328             return whole + sub ;
15329         },
15330         
15331         /**
15332          * Parse a value into a formatted date using the specified format pattern.
15333          * @param {Mixed} value The value to format
15334          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15335          * @return {String} The formatted date string
15336          */
15337         date : function(v, format){
15338             if(!v){
15339                 return "";
15340             }
15341             if(!(v instanceof Date)){
15342                 v = new Date(Date.parse(v));
15343             }
15344             return v.dateFormat(format || Roo.util.Format.defaults.date);
15345         },
15346
15347         /**
15348          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15349          * @param {String} format Any valid date format string
15350          * @return {Function} The date formatting function
15351          */
15352         dateRenderer : function(format){
15353             return function(v){
15354                 return Roo.util.Format.date(v, format);  
15355             };
15356         },
15357
15358         // private
15359         stripTagsRE : /<\/?[^>]+>/gi,
15360         
15361         /**
15362          * Strips all HTML tags
15363          * @param {Mixed} value The text from which to strip tags
15364          * @return {String} The stripped text
15365          */
15366         stripTags : function(v){
15367             return !v ? v : String(v).replace(this.stripTagsRE, "");
15368         },
15369         
15370         /**
15371          * Size in Mb,Gb etc.
15372          * @param {Number} value The number to be formated
15373          * @param {number} decimals how many decimal places
15374          * @return {String} the formated string
15375          */
15376         size : function(value, decimals)
15377         {
15378             var sizes = ['b', 'k', 'M', 'G', 'T'];
15379             if (value == 0) {
15380                 return 0;
15381             }
15382             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15383             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15384         }
15385         
15386         
15387         
15388     };
15389 }();
15390 Roo.util.Format.defaults = {
15391     date : 'd/M/Y'
15392 };/*
15393  * Based on:
15394  * Ext JS Library 1.1.1
15395  * Copyright(c) 2006-2007, Ext JS, LLC.
15396  *
15397  * Originally Released Under LGPL - original licence link has changed is not relivant.
15398  *
15399  * Fork - LGPL
15400  * <script type="text/javascript">
15401  */
15402
15403
15404  
15405
15406 /**
15407  * @class Roo.MasterTemplate
15408  * @extends Roo.Template
15409  * Provides a template that can have child templates. The syntax is:
15410 <pre><code>
15411 var t = new Roo.MasterTemplate(
15412         '&lt;select name="{name}"&gt;',
15413                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15414         '&lt;/select&gt;'
15415 );
15416 t.add('options', {value: 'foo', text: 'bar'});
15417 // or you can add multiple child elements in one shot
15418 t.addAll('options', [
15419     {value: 'foo', text: 'bar'},
15420     {value: 'foo2', text: 'bar2'},
15421     {value: 'foo3', text: 'bar3'}
15422 ]);
15423 // then append, applying the master template values
15424 t.append('my-form', {name: 'my-select'});
15425 </code></pre>
15426 * A name attribute for the child template is not required if you have only one child
15427 * template or you want to refer to them by index.
15428  */
15429 Roo.MasterTemplate = function(){
15430     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15431     this.originalHtml = this.html;
15432     var st = {};
15433     var m, re = this.subTemplateRe;
15434     re.lastIndex = 0;
15435     var subIndex = 0;
15436     while(m = re.exec(this.html)){
15437         var name = m[1], content = m[2];
15438         st[subIndex] = {
15439             name: name,
15440             index: subIndex,
15441             buffer: [],
15442             tpl : new Roo.Template(content)
15443         };
15444         if(name){
15445             st[name] = st[subIndex];
15446         }
15447         st[subIndex].tpl.compile();
15448         st[subIndex].tpl.call = this.call.createDelegate(this);
15449         subIndex++;
15450     }
15451     this.subCount = subIndex;
15452     this.subs = st;
15453 };
15454 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15455     /**
15456     * The regular expression used to match sub templates
15457     * @type RegExp
15458     * @property
15459     */
15460     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15461
15462     /**
15463      * Applies the passed values to a child template.
15464      * @param {String/Number} name (optional) The name or index of the child template
15465      * @param {Array/Object} values The values to be applied to the template
15466      * @return {MasterTemplate} this
15467      */
15468      add : function(name, values){
15469         if(arguments.length == 1){
15470             values = arguments[0];
15471             name = 0;
15472         }
15473         var s = this.subs[name];
15474         s.buffer[s.buffer.length] = s.tpl.apply(values);
15475         return this;
15476     },
15477
15478     /**
15479      * Applies all the passed values to a child template.
15480      * @param {String/Number} name (optional) The name or index of the child template
15481      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15482      * @param {Boolean} reset (optional) True to reset the template first
15483      * @return {MasterTemplate} this
15484      */
15485     fill : function(name, values, reset){
15486         var a = arguments;
15487         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15488             values = a[0];
15489             name = 0;
15490             reset = a[1];
15491         }
15492         if(reset){
15493             this.reset();
15494         }
15495         for(var i = 0, len = values.length; i < len; i++){
15496             this.add(name, values[i]);
15497         }
15498         return this;
15499     },
15500
15501     /**
15502      * Resets the template for reuse
15503      * @return {MasterTemplate} this
15504      */
15505      reset : function(){
15506         var s = this.subs;
15507         for(var i = 0; i < this.subCount; i++){
15508             s[i].buffer = [];
15509         }
15510         return this;
15511     },
15512
15513     applyTemplate : function(values){
15514         var s = this.subs;
15515         var replaceIndex = -1;
15516         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15517             return s[++replaceIndex].buffer.join("");
15518         });
15519         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15520     },
15521
15522     apply : function(){
15523         return this.applyTemplate.apply(this, arguments);
15524     },
15525
15526     compile : function(){return this;}
15527 });
15528
15529 /**
15530  * Alias for fill().
15531  * @method
15532  */
15533 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15534  /**
15535  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15536  * var tpl = Roo.MasterTemplate.from('element-id');
15537  * @param {String/HTMLElement} el
15538  * @param {Object} config
15539  * @static
15540  */
15541 Roo.MasterTemplate.from = function(el, config){
15542     el = Roo.getDom(el);
15543     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15544 };/*
15545  * Based on:
15546  * Ext JS Library 1.1.1
15547  * Copyright(c) 2006-2007, Ext JS, LLC.
15548  *
15549  * Originally Released Under LGPL - original licence link has changed is not relivant.
15550  *
15551  * Fork - LGPL
15552  * <script type="text/javascript">
15553  */
15554
15555  
15556 /**
15557  * @class Roo.util.CSS
15558  * Utility class for manipulating CSS rules
15559  * @static
15560
15561  */
15562 Roo.util.CSS = function(){
15563         var rules = null;
15564         var doc = document;
15565
15566     var camelRe = /(-[a-z])/gi;
15567     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15568
15569    return {
15570    /**
15571     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15572     * tag and appended to the HEAD of the document.
15573     * @param {String|Object} cssText The text containing the css rules
15574     * @param {String} id An id to add to the stylesheet for later removal
15575     * @return {StyleSheet}
15576     */
15577     createStyleSheet : function(cssText, id){
15578         var ss;
15579         var head = doc.getElementsByTagName("head")[0];
15580         var nrules = doc.createElement("style");
15581         nrules.setAttribute("type", "text/css");
15582         if(id){
15583             nrules.setAttribute("id", id);
15584         }
15585         if (typeof(cssText) != 'string') {
15586             // support object maps..
15587             // not sure if this a good idea.. 
15588             // perhaps it should be merged with the general css handling
15589             // and handle js style props.
15590             var cssTextNew = [];
15591             for(var n in cssText) {
15592                 var citems = [];
15593                 for(var k in cssText[n]) {
15594                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15595                 }
15596                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15597                 
15598             }
15599             cssText = cssTextNew.join("\n");
15600             
15601         }
15602        
15603        
15604        if(Roo.isIE){
15605            head.appendChild(nrules);
15606            ss = nrules.styleSheet;
15607            ss.cssText = cssText;
15608        }else{
15609            try{
15610                 nrules.appendChild(doc.createTextNode(cssText));
15611            }catch(e){
15612                nrules.cssText = cssText; 
15613            }
15614            head.appendChild(nrules);
15615            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15616        }
15617        this.cacheStyleSheet(ss);
15618        return ss;
15619    },
15620
15621    /**
15622     * Removes a style or link tag by id
15623     * @param {String} id The id of the tag
15624     */
15625    removeStyleSheet : function(id){
15626        var existing = doc.getElementById(id);
15627        if(existing){
15628            existing.parentNode.removeChild(existing);
15629        }
15630    },
15631
15632    /**
15633     * Dynamically swaps an existing stylesheet reference for a new one
15634     * @param {String} id The id of an existing link tag to remove
15635     * @param {String} url The href of the new stylesheet to include
15636     */
15637    swapStyleSheet : function(id, url){
15638        this.removeStyleSheet(id);
15639        var ss = doc.createElement("link");
15640        ss.setAttribute("rel", "stylesheet");
15641        ss.setAttribute("type", "text/css");
15642        ss.setAttribute("id", id);
15643        ss.setAttribute("href", url);
15644        doc.getElementsByTagName("head")[0].appendChild(ss);
15645    },
15646    
15647    /**
15648     * Refresh the rule cache if you have dynamically added stylesheets
15649     * @return {Object} An object (hash) of rules indexed by selector
15650     */
15651    refreshCache : function(){
15652        return this.getRules(true);
15653    },
15654
15655    // private
15656    cacheStyleSheet : function(stylesheet){
15657        if(!rules){
15658            rules = {};
15659        }
15660        try{// try catch for cross domain access issue
15661            var ssRules = stylesheet.cssRules || stylesheet.rules;
15662            for(var j = ssRules.length-1; j >= 0; --j){
15663                rules[ssRules[j].selectorText] = ssRules[j];
15664            }
15665        }catch(e){}
15666    },
15667    
15668    /**
15669     * Gets all css rules for the document
15670     * @param {Boolean} refreshCache true to refresh the internal cache
15671     * @return {Object} An object (hash) of rules indexed by selector
15672     */
15673    getRules : function(refreshCache){
15674                 if(rules == null || refreshCache){
15675                         rules = {};
15676                         var ds = doc.styleSheets;
15677                         for(var i =0, len = ds.length; i < len; i++){
15678                             try{
15679                         this.cacheStyleSheet(ds[i]);
15680                     }catch(e){} 
15681                 }
15682                 }
15683                 return rules;
15684         },
15685         
15686         /**
15687     * Gets an an individual CSS rule by selector(s)
15688     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15689     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15690     * @return {CSSRule} The CSS rule or null if one is not found
15691     */
15692    getRule : function(selector, refreshCache){
15693                 var rs = this.getRules(refreshCache);
15694                 if(!(selector instanceof Array)){
15695                     return rs[selector];
15696                 }
15697                 for(var i = 0; i < selector.length; i++){
15698                         if(rs[selector[i]]){
15699                                 return rs[selector[i]];
15700                         }
15701                 }
15702                 return null;
15703         },
15704         
15705         
15706         /**
15707     * Updates a rule property
15708     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15709     * @param {String} property The css property
15710     * @param {String} value The new value for the property
15711     * @return {Boolean} true If a rule was found and updated
15712     */
15713    updateRule : function(selector, property, value){
15714                 if(!(selector instanceof Array)){
15715                         var rule = this.getRule(selector);
15716                         if(rule){
15717                                 rule.style[property.replace(camelRe, camelFn)] = value;
15718                                 return true;
15719                         }
15720                 }else{
15721                         for(var i = 0; i < selector.length; i++){
15722                                 if(this.updateRule(selector[i], property, value)){
15723                                         return true;
15724                                 }
15725                         }
15726                 }
15727                 return false;
15728         }
15729    };   
15730 }();/*
15731  * Based on:
15732  * Ext JS Library 1.1.1
15733  * Copyright(c) 2006-2007, Ext JS, LLC.
15734  *
15735  * Originally Released Under LGPL - original licence link has changed is not relivant.
15736  *
15737  * Fork - LGPL
15738  * <script type="text/javascript">
15739  */
15740
15741  
15742
15743 /**
15744  * @class Roo.util.ClickRepeater
15745  * @extends Roo.util.Observable
15746  * 
15747  * A wrapper class which can be applied to any element. Fires a "click" event while the
15748  * mouse is pressed. The interval between firings may be specified in the config but
15749  * defaults to 10 milliseconds.
15750  * 
15751  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15752  * 
15753  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15754  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15755  * Similar to an autorepeat key delay.
15756  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15757  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15758  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15759  *           "interval" and "delay" are ignored. "immediate" is honored.
15760  * @cfg {Boolean} preventDefault True to prevent the default click event
15761  * @cfg {Boolean} stopDefault True to stop the default click event
15762  * 
15763  * @history
15764  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15765  *     2007-02-02 jvs Renamed to ClickRepeater
15766  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15767  *
15768  *  @constructor
15769  * @param {String/HTMLElement/Element} el The element to listen on
15770  * @param {Object} config
15771  **/
15772 Roo.util.ClickRepeater = function(el, config)
15773 {
15774     this.el = Roo.get(el);
15775     this.el.unselectable();
15776
15777     Roo.apply(this, config);
15778
15779     this.addEvents({
15780     /**
15781      * @event mousedown
15782      * Fires when the mouse button is depressed.
15783      * @param {Roo.util.ClickRepeater} this
15784      */
15785         "mousedown" : true,
15786     /**
15787      * @event click
15788      * Fires on a specified interval during the time the element is pressed.
15789      * @param {Roo.util.ClickRepeater} this
15790      */
15791         "click" : true,
15792     /**
15793      * @event mouseup
15794      * Fires when the mouse key is released.
15795      * @param {Roo.util.ClickRepeater} this
15796      */
15797         "mouseup" : true
15798     });
15799
15800     this.el.on("mousedown", this.handleMouseDown, this);
15801     if(this.preventDefault || this.stopDefault){
15802         this.el.on("click", function(e){
15803             if(this.preventDefault){
15804                 e.preventDefault();
15805             }
15806             if(this.stopDefault){
15807                 e.stopEvent();
15808             }
15809         }, this);
15810     }
15811
15812     // allow inline handler
15813     if(this.handler){
15814         this.on("click", this.handler,  this.scope || this);
15815     }
15816
15817     Roo.util.ClickRepeater.superclass.constructor.call(this);
15818 };
15819
15820 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15821     interval : 20,
15822     delay: 250,
15823     preventDefault : true,
15824     stopDefault : false,
15825     timer : 0,
15826
15827     // private
15828     handleMouseDown : function(){
15829         clearTimeout(this.timer);
15830         this.el.blur();
15831         if(this.pressClass){
15832             this.el.addClass(this.pressClass);
15833         }
15834         this.mousedownTime = new Date();
15835
15836         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15837         this.el.on("mouseout", this.handleMouseOut, this);
15838
15839         this.fireEvent("mousedown", this);
15840         this.fireEvent("click", this);
15841         
15842         this.timer = this.click.defer(this.delay || this.interval, this);
15843     },
15844
15845     // private
15846     click : function(){
15847         this.fireEvent("click", this);
15848         this.timer = this.click.defer(this.getInterval(), this);
15849     },
15850
15851     // private
15852     getInterval: function(){
15853         if(!this.accelerate){
15854             return this.interval;
15855         }
15856         var pressTime = this.mousedownTime.getElapsed();
15857         if(pressTime < 500){
15858             return 400;
15859         }else if(pressTime < 1700){
15860             return 320;
15861         }else if(pressTime < 2600){
15862             return 250;
15863         }else if(pressTime < 3500){
15864             return 180;
15865         }else if(pressTime < 4400){
15866             return 140;
15867         }else if(pressTime < 5300){
15868             return 80;
15869         }else if(pressTime < 6200){
15870             return 50;
15871         }else{
15872             return 10;
15873         }
15874     },
15875
15876     // private
15877     handleMouseOut : function(){
15878         clearTimeout(this.timer);
15879         if(this.pressClass){
15880             this.el.removeClass(this.pressClass);
15881         }
15882         this.el.on("mouseover", this.handleMouseReturn, this);
15883     },
15884
15885     // private
15886     handleMouseReturn : function(){
15887         this.el.un("mouseover", this.handleMouseReturn);
15888         if(this.pressClass){
15889             this.el.addClass(this.pressClass);
15890         }
15891         this.click();
15892     },
15893
15894     // private
15895     handleMouseUp : function(){
15896         clearTimeout(this.timer);
15897         this.el.un("mouseover", this.handleMouseReturn);
15898         this.el.un("mouseout", this.handleMouseOut);
15899         Roo.get(document).un("mouseup", this.handleMouseUp);
15900         this.el.removeClass(this.pressClass);
15901         this.fireEvent("mouseup", this);
15902     }
15903 });/**
15904  * @class Roo.util.Clipboard
15905  * @static
15906  * 
15907  * Clipboard UTILS
15908  * 
15909  **/
15910 Roo.util.Clipboard = {
15911     /**
15912      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15913      * @param {String} text to copy to clipboard
15914      */
15915     write : function(text) {
15916         // navigator clipboard api needs a secure context (https)
15917         if (navigator.clipboard && window.isSecureContext) {
15918             // navigator clipboard api method'
15919             navigator.clipboard.writeText(text);
15920             return ;
15921         } 
15922         // text area method
15923         var ta = document.createElement("textarea");
15924         ta.value = text;
15925         // make the textarea out of viewport
15926         ta.style.position = "fixed";
15927         ta.style.left = "-999999px";
15928         ta.style.top = "-999999px";
15929         document.body.appendChild(ta);
15930         ta.focus();
15931         ta.select();
15932         document.execCommand('copy');
15933         (function() {
15934             ta.remove();
15935         }).defer(100);
15936         
15937     }
15938         
15939 }
15940     /*
15941  * Based on:
15942  * Ext JS Library 1.1.1
15943  * Copyright(c) 2006-2007, Ext JS, LLC.
15944  *
15945  * Originally Released Under LGPL - original licence link has changed is not relivant.
15946  *
15947  * Fork - LGPL
15948  * <script type="text/javascript">
15949  */
15950
15951  
15952 /**
15953  * @class Roo.KeyNav
15954  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15955  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15956  * way to implement custom navigation schemes for any UI component.</p>
15957  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15958  * pageUp, pageDown, del, home, end.  Usage:</p>
15959  <pre><code>
15960 var nav = new Roo.KeyNav("my-element", {
15961     "left" : function(e){
15962         this.moveLeft(e.ctrlKey);
15963     },
15964     "right" : function(e){
15965         this.moveRight(e.ctrlKey);
15966     },
15967     "enter" : function(e){
15968         this.save();
15969     },
15970     scope : this
15971 });
15972 </code></pre>
15973  * @constructor
15974  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15975  * @param {Object} config The config
15976  */
15977 Roo.KeyNav = function(el, config){
15978     this.el = Roo.get(el);
15979     Roo.apply(this, config);
15980     if(!this.disabled){
15981         this.disabled = true;
15982         this.enable();
15983     }
15984 };
15985
15986 Roo.KeyNav.prototype = {
15987     /**
15988      * @cfg {Boolean} disabled
15989      * True to disable this KeyNav instance (defaults to false)
15990      */
15991     disabled : false,
15992     /**
15993      * @cfg {String} defaultEventAction
15994      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15995      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15996      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15997      */
15998     defaultEventAction: "stopEvent",
15999     /**
16000      * @cfg {Boolean} forceKeyDown
16001      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
16002      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
16003      * handle keydown instead of keypress.
16004      */
16005     forceKeyDown : false,
16006
16007     // private
16008     prepareEvent : function(e){
16009         var k = e.getKey();
16010         var h = this.keyToHandler[k];
16011         //if(h && this[h]){
16012         //    e.stopPropagation();
16013         //}
16014         if(Roo.isSafari && h && k >= 37 && k <= 40){
16015             e.stopEvent();
16016         }
16017     },
16018
16019     // private
16020     relay : function(e){
16021         var k = e.getKey();
16022         var h = this.keyToHandler[k];
16023         if(h && this[h]){
16024             if(this.doRelay(e, this[h], h) !== true){
16025                 e[this.defaultEventAction]();
16026             }
16027         }
16028     },
16029
16030     // private
16031     doRelay : function(e, h, hname){
16032         return h.call(this.scope || this, e);
16033     },
16034
16035     // possible handlers
16036     enter : false,
16037     left : false,
16038     right : false,
16039     up : false,
16040     down : false,
16041     tab : false,
16042     esc : false,
16043     pageUp : false,
16044     pageDown : false,
16045     del : false,
16046     home : false,
16047     end : false,
16048
16049     // quick lookup hash
16050     keyToHandler : {
16051         37 : "left",
16052         39 : "right",
16053         38 : "up",
16054         40 : "down",
16055         33 : "pageUp",
16056         34 : "pageDown",
16057         46 : "del",
16058         36 : "home",
16059         35 : "end",
16060         13 : "enter",
16061         27 : "esc",
16062         9  : "tab"
16063     },
16064
16065         /**
16066          * Enable this KeyNav
16067          */
16068         enable: function(){
16069                 if(this.disabled){
16070             // ie won't do special keys on keypress, no one else will repeat keys with keydown
16071             // the EventObject will normalize Safari automatically
16072             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16073                 this.el.on("keydown", this.relay,  this);
16074             }else{
16075                 this.el.on("keydown", this.prepareEvent,  this);
16076                 this.el.on("keypress", this.relay,  this);
16077             }
16078                     this.disabled = false;
16079                 }
16080         },
16081
16082         /**
16083          * Disable this KeyNav
16084          */
16085         disable: function(){
16086                 if(!this.disabled){
16087                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16088                 this.el.un("keydown", this.relay);
16089             }else{
16090                 this.el.un("keydown", this.prepareEvent);
16091                 this.el.un("keypress", this.relay);
16092             }
16093                     this.disabled = true;
16094                 }
16095         }
16096 };/*
16097  * Based on:
16098  * Ext JS Library 1.1.1
16099  * Copyright(c) 2006-2007, Ext JS, LLC.
16100  *
16101  * Originally Released Under LGPL - original licence link has changed is not relivant.
16102  *
16103  * Fork - LGPL
16104  * <script type="text/javascript">
16105  */
16106
16107  
16108 /**
16109  * @class Roo.KeyMap
16110  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
16111  * The constructor accepts the same config object as defined by {@link #addBinding}.
16112  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
16113  * combination it will call the function with this signature (if the match is a multi-key
16114  * combination the callback will still be called only once): (String key, Roo.EventObject e)
16115  * A KeyMap can also handle a string representation of keys.<br />
16116  * Usage:
16117  <pre><code>
16118 // map one key by key code
16119 var map = new Roo.KeyMap("my-element", {
16120     key: 13, // or Roo.EventObject.ENTER
16121     fn: myHandler,
16122     scope: myObject
16123 });
16124
16125 // map multiple keys to one action by string
16126 var map = new Roo.KeyMap("my-element", {
16127     key: "a\r\n\t",
16128     fn: myHandler,
16129     scope: myObject
16130 });
16131
16132 // map multiple keys to multiple actions by strings and array of codes
16133 var map = new Roo.KeyMap("my-element", [
16134     {
16135         key: [10,13],
16136         fn: function(){ alert("Return was pressed"); }
16137     }, {
16138         key: "abc",
16139         fn: function(){ alert('a, b or c was pressed'); }
16140     }, {
16141         key: "\t",
16142         ctrl:true,
16143         shift:true,
16144         fn: function(){ alert('Control + shift + tab was pressed.'); }
16145     }
16146 ]);
16147 </code></pre>
16148  * <b>Note: A KeyMap starts enabled</b>
16149  * @constructor
16150  * @param {String/HTMLElement/Roo.Element} el The element to bind to
16151  * @param {Object} config The config (see {@link #addBinding})
16152  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
16153  */
16154 Roo.KeyMap = function(el, config, eventName){
16155     this.el  = Roo.get(el);
16156     this.eventName = eventName || "keydown";
16157     this.bindings = [];
16158     if(config){
16159         this.addBinding(config);
16160     }
16161     this.enable();
16162 };
16163
16164 Roo.KeyMap.prototype = {
16165     /**
16166      * True to stop the event from bubbling and prevent the default browser action if the
16167      * key was handled by the KeyMap (defaults to false)
16168      * @type Boolean
16169      */
16170     stopEvent : false,
16171
16172     /**
16173      * Add a new binding to this KeyMap. The following config object properties are supported:
16174      * <pre>
16175 Property    Type             Description
16176 ----------  ---------------  ----------------------------------------------------------------------
16177 key         String/Array     A single keycode or an array of keycodes to handle
16178 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
16179 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
16180 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
16181 fn          Function         The function to call when KeyMap finds the expected key combination
16182 scope       Object           The scope of the callback function
16183 </pre>
16184      *
16185      * Usage:
16186      * <pre><code>
16187 // Create a KeyMap
16188 var map = new Roo.KeyMap(document, {
16189     key: Roo.EventObject.ENTER,
16190     fn: handleKey,
16191     scope: this
16192 });
16193
16194 //Add a new binding to the existing KeyMap later
16195 map.addBinding({
16196     key: 'abc',
16197     shift: true,
16198     fn: handleKey,
16199     scope: this
16200 });
16201 </code></pre>
16202      * @param {Object/Array} config A single KeyMap config or an array of configs
16203      */
16204         addBinding : function(config){
16205         if(config instanceof Array){
16206             for(var i = 0, len = config.length; i < len; i++){
16207                 this.addBinding(config[i]);
16208             }
16209             return;
16210         }
16211         var keyCode = config.key,
16212             shift = config.shift, 
16213             ctrl = config.ctrl, 
16214             alt = config.alt,
16215             fn = config.fn,
16216             scope = config.scope;
16217         if(typeof keyCode == "string"){
16218             var ks = [];
16219             var keyString = keyCode.toUpperCase();
16220             for(var j = 0, len = keyString.length; j < len; j++){
16221                 ks.push(keyString.charCodeAt(j));
16222             }
16223             keyCode = ks;
16224         }
16225         var keyArray = keyCode instanceof Array;
16226         var handler = function(e){
16227             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
16228                 var k = e.getKey();
16229                 if(keyArray){
16230                     for(var i = 0, len = keyCode.length; i < len; i++){
16231                         if(keyCode[i] == k){
16232                           if(this.stopEvent){
16233                               e.stopEvent();
16234                           }
16235                           fn.call(scope || window, k, e);
16236                           return;
16237                         }
16238                     }
16239                 }else{
16240                     if(k == keyCode){
16241                         if(this.stopEvent){
16242                            e.stopEvent();
16243                         }
16244                         fn.call(scope || window, k, e);
16245                     }
16246                 }
16247             }
16248         };
16249         this.bindings.push(handler);  
16250         },
16251
16252     /**
16253      * Shorthand for adding a single key listener
16254      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16255      * following options:
16256      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16257      * @param {Function} fn The function to call
16258      * @param {Object} scope (optional) The scope of the function
16259      */
16260     on : function(key, fn, scope){
16261         var keyCode, shift, ctrl, alt;
16262         if(typeof key == "object" && !(key instanceof Array)){
16263             keyCode = key.key;
16264             shift = key.shift;
16265             ctrl = key.ctrl;
16266             alt = key.alt;
16267         }else{
16268             keyCode = key;
16269         }
16270         this.addBinding({
16271             key: keyCode,
16272             shift: shift,
16273             ctrl: ctrl,
16274             alt: alt,
16275             fn: fn,
16276             scope: scope
16277         })
16278     },
16279
16280     // private
16281     handleKeyDown : function(e){
16282             if(this.enabled){ //just in case
16283             var b = this.bindings;
16284             for(var i = 0, len = b.length; i < len; i++){
16285                 b[i].call(this, e);
16286             }
16287             }
16288         },
16289         
16290         /**
16291          * Returns true if this KeyMap is enabled
16292          * @return {Boolean} 
16293          */
16294         isEnabled : function(){
16295             return this.enabled;  
16296         },
16297         
16298         /**
16299          * Enables this KeyMap
16300          */
16301         enable: function(){
16302                 if(!this.enabled){
16303                     this.el.on(this.eventName, this.handleKeyDown, this);
16304                     this.enabled = true;
16305                 }
16306         },
16307
16308         /**
16309          * Disable this KeyMap
16310          */
16311         disable: function(){
16312                 if(this.enabled){
16313                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16314                     this.enabled = false;
16315                 }
16316         }
16317 };/*
16318  * Based on:
16319  * Ext JS Library 1.1.1
16320  * Copyright(c) 2006-2007, Ext JS, LLC.
16321  *
16322  * Originally Released Under LGPL - original licence link has changed is not relivant.
16323  *
16324  * Fork - LGPL
16325  * <script type="text/javascript">
16326  */
16327
16328  
16329 /**
16330  * @class Roo.util.TextMetrics
16331  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16332  * wide, in pixels, a given block of text will be.
16333  * @static
16334  */
16335 Roo.util.TextMetrics = function(){
16336     var shared;
16337     return {
16338         /**
16339          * Measures the size of the specified text
16340          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16341          * that can affect the size of the rendered text
16342          * @param {String} text The text to measure
16343          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16344          * in order to accurately measure the text height
16345          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16346          */
16347         measure : function(el, text, fixedWidth){
16348             if(!shared){
16349                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16350             }
16351             shared.bind(el);
16352             shared.setFixedWidth(fixedWidth || 'auto');
16353             return shared.getSize(text);
16354         },
16355
16356         /**
16357          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16358          * the overhead of multiple calls to initialize the style properties on each measurement.
16359          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16360          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16361          * in order to accurately measure the text height
16362          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16363          */
16364         createInstance : function(el, fixedWidth){
16365             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16366         }
16367     };
16368 }();
16369
16370 /**
16371  * @class Roo.util.TextMetrics.Instance
16372  * Instance of  TextMetrics Calcuation
16373  * @constructor
16374  * Create a new TextMetrics Instance
16375  * @param {Object} bindto
16376  * @param {Boolean} fixedWidth
16377  */
16378
16379 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16380 {
16381     var ml = new Roo.Element(document.createElement('div'));
16382     document.body.appendChild(ml.dom);
16383     ml.position('absolute');
16384     ml.setLeftTop(-1000, -1000);
16385     ml.hide();
16386
16387     if(fixedWidth){
16388         ml.setWidth(fixedWidth);
16389     }
16390      
16391     var instance = {
16392         /**
16393          * Returns the size of the specified text based on the internal element's style and width properties
16394          * @param {String} text The text to measure
16395          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16396          */
16397         getSize : function(text){
16398             ml.update(text);
16399             var s = ml.getSize();
16400             ml.update('');
16401             return s;
16402         },
16403
16404         /**
16405          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16406          * that can affect the size of the rendered text
16407          * @param {String/HTMLElement} el The element, dom node or id
16408          */
16409         bind : function(el){
16410             ml.setStyle(
16411                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16412             );
16413         },
16414
16415         /**
16416          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16417          * to set a fixed width in order to accurately measure the text height.
16418          * @param {Number} width The width to set on the element
16419          */
16420         setFixedWidth : function(width){
16421             ml.setWidth(width);
16422         },
16423
16424         /**
16425          * Returns the measured width of the specified text
16426          * @param {String} text The text to measure
16427          * @return {Number} width The width in pixels
16428          */
16429         getWidth : function(text){
16430             ml.dom.style.width = 'auto';
16431             return this.getSize(text).width;
16432         },
16433
16434         /**
16435          * Returns the measured height of the specified text.  For multiline text, be sure to call
16436          * {@link #setFixedWidth} if necessary.
16437          * @param {String} text The text to measure
16438          * @return {Number} height The height in pixels
16439          */
16440         getHeight : function(text){
16441             return this.getSize(text).height;
16442         }
16443     };
16444
16445     instance.bind(bindTo);
16446
16447     return instance;
16448 };
16449
16450 // backwards compat
16451 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16452  * Based on:
16453  * Ext JS Library 1.1.1
16454  * Copyright(c) 2006-2007, Ext JS, LLC.
16455  *
16456  * Originally Released Under LGPL - original licence link has changed is not relivant.
16457  *
16458  * Fork - LGPL
16459  * <script type="text/javascript">
16460  */
16461
16462 /**
16463  * @class Roo.state.Provider
16464  * Abstract base class for state provider implementations. This class provides methods
16465  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16466  * Provider interface.
16467  */
16468 Roo.state.Provider = function(){
16469     /**
16470      * @event statechange
16471      * Fires when a state change occurs.
16472      * @param {Provider} this This state provider
16473      * @param {String} key The state key which was changed
16474      * @param {String} value The encoded value for the state
16475      */
16476     this.addEvents({
16477         "statechange": true
16478     });
16479     this.state = {};
16480     Roo.state.Provider.superclass.constructor.call(this);
16481 };
16482 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16483     /**
16484      * Returns the current value for a key
16485      * @param {String} name The key name
16486      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16487      * @return {Mixed} The state data
16488      */
16489     get : function(name, defaultValue){
16490         return typeof this.state[name] == "undefined" ?
16491             defaultValue : this.state[name];
16492     },
16493     
16494     /**
16495      * Clears a value from the state
16496      * @param {String} name The key name
16497      */
16498     clear : function(name){
16499         delete this.state[name];
16500         this.fireEvent("statechange", this, name, null);
16501     },
16502     
16503     /**
16504      * Sets the value for a key
16505      * @param {String} name The key name
16506      * @param {Mixed} value The value to set
16507      */
16508     set : function(name, value){
16509         this.state[name] = value;
16510         this.fireEvent("statechange", this, name, value);
16511     },
16512     
16513     /**
16514      * Decodes a string previously encoded with {@link #encodeValue}.
16515      * @param {String} value The value to decode
16516      * @return {Mixed} The decoded value
16517      */
16518     decodeValue : function(cookie){
16519         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16520         var matches = re.exec(unescape(cookie));
16521         if(!matches || !matches[1]) {
16522             return; // non state cookie
16523         }
16524         var type = matches[1];
16525         var v = matches[2];
16526         switch(type){
16527             case "n":
16528                 return parseFloat(v);
16529             case "d":
16530                 return new Date(Date.parse(v));
16531             case "b":
16532                 return (v == "1");
16533             case "a":
16534                 var all = [];
16535                 var values = v.split("^");
16536                 for(var i = 0, len = values.length; i < len; i++){
16537                     all.push(this.decodeValue(values[i]));
16538                 }
16539                 return all;
16540            case "o":
16541                 var all = {};
16542                 var values = v.split("^");
16543                 for(var i = 0, len = values.length; i < len; i++){
16544                     var kv = values[i].split("=");
16545                     all[kv[0]] = this.decodeValue(kv[1]);
16546                 }
16547                 return all;
16548            default:
16549                 return v;
16550         }
16551     },
16552     
16553     /**
16554      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16555      * @param {Mixed} value The value to encode
16556      * @return {String} The encoded value
16557      */
16558     encodeValue : function(v){
16559         var enc;
16560         if(typeof v == "number"){
16561             enc = "n:" + v;
16562         }else if(typeof v == "boolean"){
16563             enc = "b:" + (v ? "1" : "0");
16564         }else if(v instanceof Date){
16565             enc = "d:" + v.toGMTString();
16566         }else if(v instanceof Array){
16567             var flat = "";
16568             for(var i = 0, len = v.length; i < len; i++){
16569                 flat += this.encodeValue(v[i]);
16570                 if(i != len-1) {
16571                     flat += "^";
16572                 }
16573             }
16574             enc = "a:" + flat;
16575         }else if(typeof v == "object"){
16576             var flat = "";
16577             for(var key in v){
16578                 if(typeof v[key] != "function"){
16579                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16580                 }
16581             }
16582             enc = "o:" + flat.substring(0, flat.length-1);
16583         }else{
16584             enc = "s:" + v;
16585         }
16586         return escape(enc);        
16587     }
16588 });
16589
16590 /*
16591  * Based on:
16592  * Ext JS Library 1.1.1
16593  * Copyright(c) 2006-2007, Ext JS, LLC.
16594  *
16595  * Originally Released Under LGPL - original licence link has changed is not relivant.
16596  *
16597  * Fork - LGPL
16598  * <script type="text/javascript">
16599  */
16600 /**
16601  * @class Roo.state.Manager
16602  * This is the global state manager. By default all components that are "state aware" check this class
16603  * for state information if you don't pass them a custom state provider. In order for this class
16604  * to be useful, it must be initialized with a provider when your application initializes.
16605  <pre><code>
16606 // in your initialization function
16607 init : function(){
16608    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16609    ...
16610    // supposed you have a {@link Roo.BorderLayout}
16611    var layout = new Roo.BorderLayout(...);
16612    layout.restoreState();
16613    // or a {Roo.BasicDialog}
16614    var dialog = new Roo.BasicDialog(...);
16615    dialog.restoreState();
16616  </code></pre>
16617  * @static
16618  */
16619 Roo.state.Manager = function(){
16620     var provider = new Roo.state.Provider();
16621     
16622     return {
16623         /**
16624          * Configures the default state provider for your application
16625          * @param {Provider} stateProvider The state provider to set
16626          */
16627         setProvider : function(stateProvider){
16628             provider = stateProvider;
16629         },
16630         
16631         /**
16632          * Returns the current value for a key
16633          * @param {String} name The key name
16634          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16635          * @return {Mixed} The state data
16636          */
16637         get : function(key, defaultValue){
16638             return provider.get(key, defaultValue);
16639         },
16640         
16641         /**
16642          * Sets the value for a key
16643          * @param {String} name The key name
16644          * @param {Mixed} value The state data
16645          */
16646          set : function(key, value){
16647             provider.set(key, value);
16648         },
16649         
16650         /**
16651          * Clears a value from the state
16652          * @param {String} name The key name
16653          */
16654         clear : function(key){
16655             provider.clear(key);
16656         },
16657         
16658         /**
16659          * Gets the currently configured state provider
16660          * @return {Provider} The state provider
16661          */
16662         getProvider : function(){
16663             return provider;
16664         }
16665     };
16666 }();
16667 /*
16668  * Based on:
16669  * Ext JS Library 1.1.1
16670  * Copyright(c) 2006-2007, Ext JS, LLC.
16671  *
16672  * Originally Released Under LGPL - original licence link has changed is not relivant.
16673  *
16674  * Fork - LGPL
16675  * <script type="text/javascript">
16676  */
16677 /**
16678  * @class Roo.state.CookieProvider
16679  * @extends Roo.state.Provider
16680  * The default Provider implementation which saves state via cookies.
16681  * <br />Usage:
16682  <pre><code>
16683    var cp = new Roo.state.CookieProvider({
16684        path: "/cgi-bin/",
16685        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16686        domain: "roojs.com"
16687    })
16688    Roo.state.Manager.setProvider(cp);
16689  </code></pre>
16690  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16691  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16692  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16693  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16694  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16695  * domain the page is running on including the 'www' like 'www.roojs.com')
16696  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16697  * @constructor
16698  * Create a new CookieProvider
16699  * @param {Object} config The configuration object
16700  */
16701 Roo.state.CookieProvider = function(config){
16702     Roo.state.CookieProvider.superclass.constructor.call(this);
16703     this.path = "/";
16704     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16705     this.domain = null;
16706     this.secure = false;
16707     Roo.apply(this, config);
16708     this.state = this.readCookies();
16709 };
16710
16711 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16712     // private
16713     set : function(name, value){
16714         if(typeof value == "undefined" || value === null){
16715             this.clear(name);
16716             return;
16717         }
16718         this.setCookie(name, value);
16719         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16720     },
16721
16722     // private
16723     clear : function(name){
16724         this.clearCookie(name);
16725         Roo.state.CookieProvider.superclass.clear.call(this, name);
16726     },
16727
16728     // private
16729     readCookies : function(){
16730         var cookies = {};
16731         var c = document.cookie + ";";
16732         var re = /\s?(.*?)=(.*?);/g;
16733         var matches;
16734         while((matches = re.exec(c)) != null){
16735             var name = matches[1];
16736             var value = matches[2];
16737             if(name && name.substring(0,3) == "ys-"){
16738                 cookies[name.substr(3)] = this.decodeValue(value);
16739             }
16740         }
16741         return cookies;
16742     },
16743
16744     // private
16745     setCookie : function(name, value){
16746         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16747            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16748            ((this.path == null) ? "" : ("; path=" + this.path)) +
16749            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16750            ((this.secure == true) ? "; secure" : "");
16751     },
16752
16753     // private
16754     clearCookie : function(name){
16755         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16756            ((this.path == null) ? "" : ("; path=" + this.path)) +
16757            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16758            ((this.secure == true) ? "; secure" : "");
16759     }
16760 });/*
16761  * Based on:
16762  * Ext JS Library 1.1.1
16763  * Copyright(c) 2006-2007, Ext JS, LLC.
16764  *
16765  * Originally Released Under LGPL - original licence link has changed is not relivant.
16766  *
16767  * Fork - LGPL
16768  * <script type="text/javascript">
16769  */
16770  
16771
16772 /**
16773  * @class Roo.ComponentMgr
16774  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16775  * @static
16776  */
16777 Roo.ComponentMgr = function(){
16778     var all = new Roo.util.MixedCollection();
16779
16780     return {
16781         /**
16782          * Registers a component.
16783          * @param {Roo.Component} c The component
16784          */
16785         register : function(c){
16786             all.add(c);
16787         },
16788
16789         /**
16790          * Unregisters a component.
16791          * @param {Roo.Component} c The component
16792          */
16793         unregister : function(c){
16794             all.remove(c);
16795         },
16796
16797         /**
16798          * Returns a component by id
16799          * @param {String} id The component id
16800          */
16801         get : function(id){
16802             return all.get(id);
16803         },
16804
16805         /**
16806          * Registers a function that will be called when a specified component is added to ComponentMgr
16807          * @param {String} id The component id
16808          * @param {Funtction} fn The callback function
16809          * @param {Object} scope The scope of the callback
16810          */
16811         onAvailable : function(id, fn, scope){
16812             all.on("add", function(index, o){
16813                 if(o.id == id){
16814                     fn.call(scope || o, o);
16815                     all.un("add", fn, scope);
16816                 }
16817             });
16818         }
16819     };
16820 }();/*
16821  * Based on:
16822  * Ext JS Library 1.1.1
16823  * Copyright(c) 2006-2007, Ext JS, LLC.
16824  *
16825  * Originally Released Under LGPL - original licence link has changed is not relivant.
16826  *
16827  * Fork - LGPL
16828  * <script type="text/javascript">
16829  */
16830  
16831 /**
16832  * @class Roo.Component
16833  * @extends Roo.util.Observable
16834  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16835  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16836  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16837  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16838  * All visual components (widgets) that require rendering into a layout should subclass Component.
16839  * @constructor
16840  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16841  * 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
16842  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16843  */
16844 Roo.Component = function(config){
16845     config = config || {};
16846     if(config.tagName || config.dom || typeof config == "string"){ // element object
16847         config = {el: config, id: config.id || config};
16848     }
16849     this.initialConfig = config;
16850
16851     Roo.apply(this, config);
16852     this.addEvents({
16853         /**
16854          * @event disable
16855          * Fires after the component is disabled.
16856              * @param {Roo.Component} this
16857              */
16858         disable : true,
16859         /**
16860          * @event enable
16861          * Fires after the component is enabled.
16862              * @param {Roo.Component} this
16863              */
16864         enable : true,
16865         /**
16866          * @event beforeshow
16867          * Fires before the component is shown.  Return false to stop the show.
16868              * @param {Roo.Component} this
16869              */
16870         beforeshow : true,
16871         /**
16872          * @event show
16873          * Fires after the component is shown.
16874              * @param {Roo.Component} this
16875              */
16876         show : true,
16877         /**
16878          * @event beforehide
16879          * Fires before the component is hidden. Return false to stop the hide.
16880              * @param {Roo.Component} this
16881              */
16882         beforehide : true,
16883         /**
16884          * @event hide
16885          * Fires after the component is hidden.
16886              * @param {Roo.Component} this
16887              */
16888         hide : true,
16889         /**
16890          * @event beforerender
16891          * Fires before the component is rendered. Return false to stop the render.
16892              * @param {Roo.Component} this
16893              */
16894         beforerender : true,
16895         /**
16896          * @event render
16897          * Fires after the component is rendered.
16898              * @param {Roo.Component} this
16899              */
16900         render : true,
16901         /**
16902          * @event beforedestroy
16903          * Fires before the component is destroyed. Return false to stop the destroy.
16904              * @param {Roo.Component} this
16905              */
16906         beforedestroy : true,
16907         /**
16908          * @event destroy
16909          * Fires after the component is destroyed.
16910              * @param {Roo.Component} this
16911              */
16912         destroy : true
16913     });
16914     if(!this.id){
16915         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16916     }
16917     Roo.ComponentMgr.register(this);
16918     Roo.Component.superclass.constructor.call(this);
16919     this.initComponent();
16920     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16921         this.render(this.renderTo);
16922         delete this.renderTo;
16923     }
16924 };
16925
16926 /** @private */
16927 Roo.Component.AUTO_ID = 1000;
16928
16929 Roo.extend(Roo.Component, Roo.util.Observable, {
16930     /**
16931      * @scope Roo.Component.prototype
16932      * @type {Boolean}
16933      * true if this component is hidden. Read-only.
16934      */
16935     hidden : false,
16936     /**
16937      * @type {Boolean}
16938      * true if this component is disabled. Read-only.
16939      */
16940     disabled : false,
16941     /**
16942      * @type {Boolean}
16943      * true if this component has been rendered. Read-only.
16944      */
16945     rendered : false,
16946     
16947     /** @cfg {String} disableClass
16948      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16949      */
16950     disabledClass : "x-item-disabled",
16951         /** @cfg {Boolean} allowDomMove
16952          * Whether the component can move the Dom node when rendering (defaults to true).
16953          */
16954     allowDomMove : true,
16955     /** @cfg {String} hideMode (display|visibility)
16956      * How this component should hidden. Supported values are
16957      * "visibility" (css visibility), "offsets" (negative offset position) and
16958      * "display" (css display) - defaults to "display".
16959      */
16960     hideMode: 'display',
16961
16962     /** @private */
16963     ctype : "Roo.Component",
16964
16965     /**
16966      * @cfg {String} actionMode 
16967      * which property holds the element that used for  hide() / show() / disable() / enable()
16968      * default is 'el' for forms you probably want to set this to fieldEl 
16969      */
16970     actionMode : "el",
16971
16972     /** @private */
16973     getActionEl : function(){
16974         return this[this.actionMode];
16975     },
16976
16977     initComponent : Roo.emptyFn,
16978     /**
16979      * If this is a lazy rendering component, render it to its container element.
16980      * @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.
16981      */
16982     render : function(container, position){
16983         
16984         if(this.rendered){
16985             return this;
16986         }
16987         
16988         if(this.fireEvent("beforerender", this) === false){
16989             return false;
16990         }
16991         
16992         if(!container && this.el){
16993             this.el = Roo.get(this.el);
16994             container = this.el.dom.parentNode;
16995             this.allowDomMove = false;
16996         }
16997         this.container = Roo.get(container);
16998         this.rendered = true;
16999         if(position !== undefined){
17000             if(typeof position == 'number'){
17001                 position = this.container.dom.childNodes[position];
17002             }else{
17003                 position = Roo.getDom(position);
17004             }
17005         }
17006         this.onRender(this.container, position || null);
17007         if(this.cls){
17008             this.el.addClass(this.cls);
17009             delete this.cls;
17010         }
17011         if(this.style){
17012             this.el.applyStyles(this.style);
17013             delete this.style;
17014         }
17015         this.fireEvent("render", this);
17016         this.afterRender(this.container);
17017         if(this.hidden){
17018             this.hide();
17019         }
17020         if(this.disabled){
17021             this.disable();
17022         }
17023
17024         return this;
17025         
17026     },
17027
17028     /** @private */
17029     // default function is not really useful
17030     onRender : function(ct, position){
17031         if(this.el){
17032             this.el = Roo.get(this.el);
17033             if(this.allowDomMove !== false){
17034                 ct.dom.insertBefore(this.el.dom, position);
17035             }
17036         }
17037     },
17038
17039     /** @private */
17040     getAutoCreate : function(){
17041         var cfg = typeof this.autoCreate == "object" ?
17042                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
17043         if(this.id && !cfg.id){
17044             cfg.id = this.id;
17045         }
17046         return cfg;
17047     },
17048
17049     /** @private */
17050     afterRender : Roo.emptyFn,
17051
17052     /**
17053      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
17054      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
17055      */
17056     destroy : function(){
17057         if(this.fireEvent("beforedestroy", this) !== false){
17058             this.purgeListeners();
17059             this.beforeDestroy();
17060             if(this.rendered){
17061                 this.el.removeAllListeners();
17062                 this.el.remove();
17063                 if(this.actionMode == "container"){
17064                     this.container.remove();
17065                 }
17066             }
17067             this.onDestroy();
17068             Roo.ComponentMgr.unregister(this);
17069             this.fireEvent("destroy", this);
17070         }
17071     },
17072
17073         /** @private */
17074     beforeDestroy : function(){
17075
17076     },
17077
17078         /** @private */
17079         onDestroy : function(){
17080
17081     },
17082
17083     /**
17084      * Returns the underlying {@link Roo.Element}.
17085      * @return {Roo.Element} The element
17086      */
17087     getEl : function(){
17088         return this.el;
17089     },
17090
17091     /**
17092      * Returns the id of this component.
17093      * @return {String}
17094      */
17095     getId : function(){
17096         return this.id;
17097     },
17098
17099     /**
17100      * Try to focus this component.
17101      * @param {Boolean} selectText True to also select the text in this component (if applicable)
17102      * @return {Roo.Component} this
17103      */
17104     focus : function(selectText){
17105         if(this.rendered){
17106             this.el.focus();
17107             if(selectText === true){
17108                 this.el.dom.select();
17109             }
17110         }
17111         return this;
17112     },
17113
17114     /** @private */
17115     blur : function(){
17116         if(this.rendered){
17117             this.el.blur();
17118         }
17119         return this;
17120     },
17121
17122     /**
17123      * Disable this component.
17124      * @return {Roo.Component} this
17125      */
17126     disable : function(){
17127         if(this.rendered){
17128             this.onDisable();
17129         }
17130         this.disabled = true;
17131         this.fireEvent("disable", this);
17132         return this;
17133     },
17134
17135         // private
17136     onDisable : function(){
17137         this.getActionEl().addClass(this.disabledClass);
17138         this.el.dom.disabled = true;
17139     },
17140
17141     /**
17142      * Enable this component.
17143      * @return {Roo.Component} this
17144      */
17145     enable : function(){
17146         if(this.rendered){
17147             this.onEnable();
17148         }
17149         this.disabled = false;
17150         this.fireEvent("enable", this);
17151         return this;
17152     },
17153
17154         // private
17155     onEnable : function(){
17156         this.getActionEl().removeClass(this.disabledClass);
17157         this.el.dom.disabled = false;
17158     },
17159
17160     /**
17161      * Convenience function for setting disabled/enabled by boolean.
17162      * @param {Boolean} disabled
17163      */
17164     setDisabled : function(disabled){
17165         this[disabled ? "disable" : "enable"]();
17166     },
17167
17168     /**
17169      * Show this component.
17170      * @return {Roo.Component} this
17171      */
17172     show: function(){
17173         if(this.fireEvent("beforeshow", this) !== false){
17174             this.hidden = false;
17175             if(this.rendered){
17176                 this.onShow();
17177             }
17178             this.fireEvent("show", this);
17179         }
17180         return this;
17181     },
17182
17183     // private
17184     onShow : function(){
17185         var ae = this.getActionEl();
17186         if(this.hideMode == 'visibility'){
17187             ae.dom.style.visibility = "visible";
17188         }else if(this.hideMode == 'offsets'){
17189             ae.removeClass('x-hidden');
17190         }else{
17191             ae.dom.style.display = "";
17192         }
17193     },
17194
17195     /**
17196      * Hide this component.
17197      * @return {Roo.Component} this
17198      */
17199     hide: function(){
17200         if(this.fireEvent("beforehide", this) !== false){
17201             this.hidden = true;
17202             if(this.rendered){
17203                 this.onHide();
17204             }
17205             this.fireEvent("hide", this);
17206         }
17207         return this;
17208     },
17209
17210     // private
17211     onHide : function(){
17212         var ae = this.getActionEl();
17213         if(this.hideMode == 'visibility'){
17214             ae.dom.style.visibility = "hidden";
17215         }else if(this.hideMode == 'offsets'){
17216             ae.addClass('x-hidden');
17217         }else{
17218             ae.dom.style.display = "none";
17219         }
17220     },
17221
17222     /**
17223      * Convenience function to hide or show this component by boolean.
17224      * @param {Boolean} visible True to show, false to hide
17225      * @return {Roo.Component} this
17226      */
17227     setVisible: function(visible){
17228         if(visible) {
17229             this.show();
17230         }else{
17231             this.hide();
17232         }
17233         return this;
17234     },
17235
17236     /**
17237      * Returns true if this component is visible.
17238      */
17239     isVisible : function(){
17240         return this.getActionEl().isVisible();
17241     },
17242
17243     cloneConfig : function(overrides){
17244         overrides = overrides || {};
17245         var id = overrides.id || Roo.id();
17246         var cfg = Roo.applyIf(overrides, this.initialConfig);
17247         cfg.id = id; // prevent dup id
17248         return new this.constructor(cfg);
17249     }
17250 });/*
17251  * Based on:
17252  * Ext JS Library 1.1.1
17253  * Copyright(c) 2006-2007, Ext JS, LLC.
17254  *
17255  * Originally Released Under LGPL - original licence link has changed is not relivant.
17256  *
17257  * Fork - LGPL
17258  * <script type="text/javascript">
17259  */
17260
17261 /**
17262  * @class Roo.BoxComponent
17263  * @extends Roo.Component
17264  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17265  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17266  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17267  * layout containers.
17268  * @constructor
17269  * @param {Roo.Element/String/Object} config The configuration options.
17270  */
17271 Roo.BoxComponent = function(config){
17272     Roo.Component.call(this, config);
17273     this.addEvents({
17274         /**
17275          * @event resize
17276          * Fires after the component is resized.
17277              * @param {Roo.Component} this
17278              * @param {Number} adjWidth The box-adjusted width that was set
17279              * @param {Number} adjHeight The box-adjusted height that was set
17280              * @param {Number} rawWidth The width that was originally specified
17281              * @param {Number} rawHeight The height that was originally specified
17282              */
17283         resize : true,
17284         /**
17285          * @event move
17286          * Fires after the component is moved.
17287              * @param {Roo.Component} this
17288              * @param {Number} x The new x position
17289              * @param {Number} y The new y position
17290              */
17291         move : true
17292     });
17293 };
17294
17295 Roo.extend(Roo.BoxComponent, Roo.Component, {
17296     // private, set in afterRender to signify that the component has been rendered
17297     boxReady : false,
17298     // private, used to defer height settings to subclasses
17299     deferHeight: false,
17300     /** @cfg {Number} width
17301      * width (optional) size of component
17302      */
17303      /** @cfg {Number} height
17304      * height (optional) size of component
17305      */
17306      
17307     /**
17308      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17309      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17310      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17311      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17312      * @return {Roo.BoxComponent} this
17313      */
17314     setSize : function(w, h){
17315         // support for standard size objects
17316         if(typeof w == 'object'){
17317             h = w.height;
17318             w = w.width;
17319         }
17320         // not rendered
17321         if(!this.boxReady){
17322             this.width = w;
17323             this.height = h;
17324             return this;
17325         }
17326
17327         // prevent recalcs when not needed
17328         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17329             return this;
17330         }
17331         this.lastSize = {width: w, height: h};
17332
17333         var adj = this.adjustSize(w, h);
17334         var aw = adj.width, ah = adj.height;
17335         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17336             var rz = this.getResizeEl();
17337             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17338                 rz.setSize(aw, ah);
17339             }else if(!this.deferHeight && ah !== undefined){
17340                 rz.setHeight(ah);
17341             }else if(aw !== undefined){
17342                 rz.setWidth(aw);
17343             }
17344             this.onResize(aw, ah, w, h);
17345             this.fireEvent('resize', this, aw, ah, w, h);
17346         }
17347         return this;
17348     },
17349
17350     /**
17351      * Gets the current size of the component's underlying element.
17352      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17353      */
17354     getSize : function(){
17355         return this.el.getSize();
17356     },
17357
17358     /**
17359      * Gets the current XY position of the component's underlying element.
17360      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17361      * @return {Array} The XY position of the element (e.g., [100, 200])
17362      */
17363     getPosition : function(local){
17364         if(local === true){
17365             return [this.el.getLeft(true), this.el.getTop(true)];
17366         }
17367         return this.xy || this.el.getXY();
17368     },
17369
17370     /**
17371      * Gets the current box measurements of the component's underlying element.
17372      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17373      * @returns {Object} box An object in the format {x, y, width, height}
17374      */
17375     getBox : function(local){
17376         var s = this.el.getSize();
17377         if(local){
17378             s.x = this.el.getLeft(true);
17379             s.y = this.el.getTop(true);
17380         }else{
17381             var xy = this.xy || this.el.getXY();
17382             s.x = xy[0];
17383             s.y = xy[1];
17384         }
17385         return s;
17386     },
17387
17388     /**
17389      * Sets the current box measurements of the component's underlying element.
17390      * @param {Object} box An object in the format {x, y, width, height}
17391      * @returns {Roo.BoxComponent} this
17392      */
17393     updateBox : function(box){
17394         this.setSize(box.width, box.height);
17395         this.setPagePosition(box.x, box.y);
17396         return this;
17397     },
17398
17399     // protected
17400     getResizeEl : function(){
17401         return this.resizeEl || this.el;
17402     },
17403
17404     // protected
17405     getPositionEl : function(){
17406         return this.positionEl || this.el;
17407     },
17408
17409     /**
17410      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17411      * This method fires the move event.
17412      * @param {Number} left The new left
17413      * @param {Number} top The new top
17414      * @returns {Roo.BoxComponent} this
17415      */
17416     setPosition : function(x, y){
17417         this.x = x;
17418         this.y = y;
17419         if(!this.boxReady){
17420             return this;
17421         }
17422         var adj = this.adjustPosition(x, y);
17423         var ax = adj.x, ay = adj.y;
17424
17425         var el = this.getPositionEl();
17426         if(ax !== undefined || ay !== undefined){
17427             if(ax !== undefined && ay !== undefined){
17428                 el.setLeftTop(ax, ay);
17429             }else if(ax !== undefined){
17430                 el.setLeft(ax);
17431             }else if(ay !== undefined){
17432                 el.setTop(ay);
17433             }
17434             this.onPosition(ax, ay);
17435             this.fireEvent('move', this, ax, ay);
17436         }
17437         return this;
17438     },
17439
17440     /**
17441      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17442      * This method fires the move event.
17443      * @param {Number} x The new x position
17444      * @param {Number} y The new y position
17445      * @returns {Roo.BoxComponent} this
17446      */
17447     setPagePosition : function(x, y){
17448         this.pageX = x;
17449         this.pageY = y;
17450         if(!this.boxReady){
17451             return;
17452         }
17453         if(x === undefined || y === undefined){ // cannot translate undefined points
17454             return;
17455         }
17456         var p = this.el.translatePoints(x, y);
17457         this.setPosition(p.left, p.top);
17458         return this;
17459     },
17460
17461     // private
17462     onRender : function(ct, position){
17463         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17464         if(this.resizeEl){
17465             this.resizeEl = Roo.get(this.resizeEl);
17466         }
17467         if(this.positionEl){
17468             this.positionEl = Roo.get(this.positionEl);
17469         }
17470     },
17471
17472     // private
17473     afterRender : function(){
17474         Roo.BoxComponent.superclass.afterRender.call(this);
17475         this.boxReady = true;
17476         this.setSize(this.width, this.height);
17477         if(this.x || this.y){
17478             this.setPosition(this.x, this.y);
17479         }
17480         if(this.pageX || this.pageY){
17481             this.setPagePosition(this.pageX, this.pageY);
17482         }
17483     },
17484
17485     /**
17486      * Force the component's size to recalculate based on the underlying element's current height and width.
17487      * @returns {Roo.BoxComponent} this
17488      */
17489     syncSize : function(){
17490         delete this.lastSize;
17491         this.setSize(this.el.getWidth(), this.el.getHeight());
17492         return this;
17493     },
17494
17495     /**
17496      * Called after the component is resized, this method is empty by default but can be implemented by any
17497      * subclass that needs to perform custom logic after a resize occurs.
17498      * @param {Number} adjWidth The box-adjusted width that was set
17499      * @param {Number} adjHeight The box-adjusted height that was set
17500      * @param {Number} rawWidth The width that was originally specified
17501      * @param {Number} rawHeight The height that was originally specified
17502      */
17503     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17504
17505     },
17506
17507     /**
17508      * Called after the component is moved, this method is empty by default but can be implemented by any
17509      * subclass that needs to perform custom logic after a move occurs.
17510      * @param {Number} x The new x position
17511      * @param {Number} y The new y position
17512      */
17513     onPosition : function(x, y){
17514
17515     },
17516
17517     // private
17518     adjustSize : function(w, h){
17519         if(this.autoWidth){
17520             w = 'auto';
17521         }
17522         if(this.autoHeight){
17523             h = 'auto';
17524         }
17525         return {width : w, height: h};
17526     },
17527
17528     // private
17529     adjustPosition : function(x, y){
17530         return {x : x, y: y};
17531     }
17532 });/*
17533  * Based on:
17534  * Ext JS Library 1.1.1
17535  * Copyright(c) 2006-2007, Ext JS, LLC.
17536  *
17537  * Originally Released Under LGPL - original licence link has changed is not relivant.
17538  *
17539  * Fork - LGPL
17540  * <script type="text/javascript">
17541  */
17542  (function(){ 
17543 /**
17544  * @class Roo.Layer
17545  * @extends Roo.Element
17546  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17547  * automatic maintaining of shadow/shim positions.
17548  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17549  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17550  * you can pass a string with a CSS class name. False turns off the shadow.
17551  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17552  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17553  * @cfg {String} cls CSS class to add to the element
17554  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17555  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17556  * @constructor
17557  * @param {Object} config An object with config options.
17558  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17559  */
17560
17561 Roo.Layer = function(config, existingEl){
17562     config = config || {};
17563     var dh = Roo.DomHelper;
17564     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17565     if(existingEl){
17566         this.dom = Roo.getDom(existingEl);
17567     }
17568     if(!this.dom){
17569         var o = config.dh || {tag: "div", cls: "x-layer"};
17570         this.dom = dh.append(pel, o);
17571     }
17572     if(config.cls){
17573         this.addClass(config.cls);
17574     }
17575     this.constrain = config.constrain !== false;
17576     this.visibilityMode = Roo.Element.VISIBILITY;
17577     if(config.id){
17578         this.id = this.dom.id = config.id;
17579     }else{
17580         this.id = Roo.id(this.dom);
17581     }
17582     this.zindex = config.zindex || this.getZIndex();
17583     this.position("absolute", this.zindex);
17584     if(config.shadow){
17585         this.shadowOffset = config.shadowOffset || 4;
17586         this.shadow = new Roo.Shadow({
17587             offset : this.shadowOffset,
17588             mode : config.shadow
17589         });
17590     }else{
17591         this.shadowOffset = 0;
17592     }
17593     this.useShim = config.shim !== false && Roo.useShims;
17594     this.useDisplay = config.useDisplay;
17595     this.hide();
17596 };
17597
17598 var supr = Roo.Element.prototype;
17599
17600 // shims are shared among layer to keep from having 100 iframes
17601 var shims = [];
17602
17603 Roo.extend(Roo.Layer, Roo.Element, {
17604
17605     getZIndex : function(){
17606         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17607     },
17608
17609     getShim : function(){
17610         if(!this.useShim){
17611             return null;
17612         }
17613         if(this.shim){
17614             return this.shim;
17615         }
17616         var shim = shims.shift();
17617         if(!shim){
17618             shim = this.createShim();
17619             shim.enableDisplayMode('block');
17620             shim.dom.style.display = 'none';
17621             shim.dom.style.visibility = 'visible';
17622         }
17623         var pn = this.dom.parentNode;
17624         if(shim.dom.parentNode != pn){
17625             pn.insertBefore(shim.dom, this.dom);
17626         }
17627         shim.setStyle('z-index', this.getZIndex()-2);
17628         this.shim = shim;
17629         return shim;
17630     },
17631
17632     hideShim : function(){
17633         if(this.shim){
17634             this.shim.setDisplayed(false);
17635             shims.push(this.shim);
17636             delete this.shim;
17637         }
17638     },
17639
17640     disableShadow : function(){
17641         if(this.shadow){
17642             this.shadowDisabled = true;
17643             this.shadow.hide();
17644             this.lastShadowOffset = this.shadowOffset;
17645             this.shadowOffset = 0;
17646         }
17647     },
17648
17649     enableShadow : function(show){
17650         if(this.shadow){
17651             this.shadowDisabled = false;
17652             this.shadowOffset = this.lastShadowOffset;
17653             delete this.lastShadowOffset;
17654             if(show){
17655                 this.sync(true);
17656             }
17657         }
17658     },
17659
17660     // private
17661     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17662     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17663     sync : function(doShow){
17664         var sw = this.shadow;
17665         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17666             var sh = this.getShim();
17667
17668             var w = this.getWidth(),
17669                 h = this.getHeight();
17670
17671             var l = this.getLeft(true),
17672                 t = this.getTop(true);
17673
17674             if(sw && !this.shadowDisabled){
17675                 if(doShow && !sw.isVisible()){
17676                     sw.show(this);
17677                 }else{
17678                     sw.realign(l, t, w, h);
17679                 }
17680                 if(sh){
17681                     if(doShow){
17682                        sh.show();
17683                     }
17684                     // fit the shim behind the shadow, so it is shimmed too
17685                     var a = sw.adjusts, s = sh.dom.style;
17686                     s.left = (Math.min(l, l+a.l))+"px";
17687                     s.top = (Math.min(t, t+a.t))+"px";
17688                     s.width = (w+a.w)+"px";
17689                     s.height = (h+a.h)+"px";
17690                 }
17691             }else if(sh){
17692                 if(doShow){
17693                    sh.show();
17694                 }
17695                 sh.setSize(w, h);
17696                 sh.setLeftTop(l, t);
17697             }
17698             
17699         }
17700     },
17701
17702     // private
17703     destroy : function(){
17704         this.hideShim();
17705         if(this.shadow){
17706             this.shadow.hide();
17707         }
17708         this.removeAllListeners();
17709         var pn = this.dom.parentNode;
17710         if(pn){
17711             pn.removeChild(this.dom);
17712         }
17713         Roo.Element.uncache(this.id);
17714     },
17715
17716     remove : function(){
17717         this.destroy();
17718     },
17719
17720     // private
17721     beginUpdate : function(){
17722         this.updating = true;
17723     },
17724
17725     // private
17726     endUpdate : function(){
17727         this.updating = false;
17728         this.sync(true);
17729     },
17730
17731     // private
17732     hideUnders : function(negOffset){
17733         if(this.shadow){
17734             this.shadow.hide();
17735         }
17736         this.hideShim();
17737     },
17738
17739     // private
17740     constrainXY : function(){
17741         if(this.constrain){
17742             var vw = Roo.lib.Dom.getViewWidth(),
17743                 vh = Roo.lib.Dom.getViewHeight();
17744             var s = Roo.get(document).getScroll();
17745
17746             var xy = this.getXY();
17747             var x = xy[0], y = xy[1];   
17748             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17749             // only move it if it needs it
17750             var moved = false;
17751             // first validate right/bottom
17752             if((x + w) > vw+s.left){
17753                 x = vw - w - this.shadowOffset;
17754                 moved = true;
17755             }
17756             if((y + h) > vh+s.top){
17757                 y = vh - h - this.shadowOffset;
17758                 moved = true;
17759             }
17760             // then make sure top/left isn't negative
17761             if(x < s.left){
17762                 x = s.left;
17763                 moved = true;
17764             }
17765             if(y < s.top){
17766                 y = s.top;
17767                 moved = true;
17768             }
17769             if(moved){
17770                 if(this.avoidY){
17771                     var ay = this.avoidY;
17772                     if(y <= ay && (y+h) >= ay){
17773                         y = ay-h-5;   
17774                     }
17775                 }
17776                 xy = [x, y];
17777                 this.storeXY(xy);
17778                 supr.setXY.call(this, xy);
17779                 this.sync();
17780             }
17781         }
17782     },
17783
17784     isVisible : function(){
17785         return this.visible;    
17786     },
17787
17788     // private
17789     showAction : function(){
17790         this.visible = true; // track visibility to prevent getStyle calls
17791         if(this.useDisplay === true){
17792             this.setDisplayed("");
17793         }else if(this.lastXY){
17794             supr.setXY.call(this, this.lastXY);
17795         }else if(this.lastLT){
17796             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17797         }
17798     },
17799
17800     // private
17801     hideAction : function(){
17802         this.visible = false;
17803         if(this.useDisplay === true){
17804             this.setDisplayed(false);
17805         }else{
17806             this.setLeftTop(-10000,-10000);
17807         }
17808     },
17809
17810     // overridden Element method
17811     setVisible : function(v, a, d, c, e){
17812         if(v){
17813             this.showAction();
17814         }
17815         if(a && v){
17816             var cb = function(){
17817                 this.sync(true);
17818                 if(c){
17819                     c();
17820                 }
17821             }.createDelegate(this);
17822             supr.setVisible.call(this, true, true, d, cb, e);
17823         }else{
17824             if(!v){
17825                 this.hideUnders(true);
17826             }
17827             var cb = c;
17828             if(a){
17829                 cb = function(){
17830                     this.hideAction();
17831                     if(c){
17832                         c();
17833                     }
17834                 }.createDelegate(this);
17835             }
17836             supr.setVisible.call(this, v, a, d, cb, e);
17837             if(v){
17838                 this.sync(true);
17839             }else if(!a){
17840                 this.hideAction();
17841             }
17842         }
17843     },
17844
17845     storeXY : function(xy){
17846         delete this.lastLT;
17847         this.lastXY = xy;
17848     },
17849
17850     storeLeftTop : function(left, top){
17851         delete this.lastXY;
17852         this.lastLT = [left, top];
17853     },
17854
17855     // private
17856     beforeFx : function(){
17857         this.beforeAction();
17858         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17859     },
17860
17861     // private
17862     afterFx : function(){
17863         Roo.Layer.superclass.afterFx.apply(this, arguments);
17864         this.sync(this.isVisible());
17865     },
17866
17867     // private
17868     beforeAction : function(){
17869         if(!this.updating && this.shadow){
17870             this.shadow.hide();
17871         }
17872     },
17873
17874     // overridden Element method
17875     setLeft : function(left){
17876         this.storeLeftTop(left, this.getTop(true));
17877         supr.setLeft.apply(this, arguments);
17878         this.sync();
17879     },
17880
17881     setTop : function(top){
17882         this.storeLeftTop(this.getLeft(true), top);
17883         supr.setTop.apply(this, arguments);
17884         this.sync();
17885     },
17886
17887     setLeftTop : function(left, top){
17888         this.storeLeftTop(left, top);
17889         supr.setLeftTop.apply(this, arguments);
17890         this.sync();
17891     },
17892
17893     setXY : function(xy, a, d, c, e){
17894         this.fixDisplay();
17895         this.beforeAction();
17896         this.storeXY(xy);
17897         var cb = this.createCB(c);
17898         supr.setXY.call(this, xy, a, d, cb, e);
17899         if(!a){
17900             cb();
17901         }
17902     },
17903
17904     // private
17905     createCB : function(c){
17906         var el = this;
17907         return function(){
17908             el.constrainXY();
17909             el.sync(true);
17910             if(c){
17911                 c();
17912             }
17913         };
17914     },
17915
17916     // overridden Element method
17917     setX : function(x, a, d, c, e){
17918         this.setXY([x, this.getY()], a, d, c, e);
17919     },
17920
17921     // overridden Element method
17922     setY : function(y, a, d, c, e){
17923         this.setXY([this.getX(), y], a, d, c, e);
17924     },
17925
17926     // overridden Element method
17927     setSize : function(w, h, a, d, c, e){
17928         this.beforeAction();
17929         var cb = this.createCB(c);
17930         supr.setSize.call(this, w, h, a, d, cb, e);
17931         if(!a){
17932             cb();
17933         }
17934     },
17935
17936     // overridden Element method
17937     setWidth : function(w, a, d, c, e){
17938         this.beforeAction();
17939         var cb = this.createCB(c);
17940         supr.setWidth.call(this, w, a, d, cb, e);
17941         if(!a){
17942             cb();
17943         }
17944     },
17945
17946     // overridden Element method
17947     setHeight : function(h, a, d, c, e){
17948         this.beforeAction();
17949         var cb = this.createCB(c);
17950         supr.setHeight.call(this, h, a, d, cb, e);
17951         if(!a){
17952             cb();
17953         }
17954     },
17955
17956     // overridden Element method
17957     setBounds : function(x, y, w, h, a, d, c, e){
17958         this.beforeAction();
17959         var cb = this.createCB(c);
17960         if(!a){
17961             this.storeXY([x, y]);
17962             supr.setXY.call(this, [x, y]);
17963             supr.setSize.call(this, w, h, a, d, cb, e);
17964             cb();
17965         }else{
17966             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17967         }
17968         return this;
17969     },
17970     
17971     /**
17972      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17973      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17974      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17975      * @param {Number} zindex The new z-index to set
17976      * @return {this} The Layer
17977      */
17978     setZIndex : function(zindex){
17979         this.zindex = zindex;
17980         this.setStyle("z-index", zindex + 2);
17981         if(this.shadow){
17982             this.shadow.setZIndex(zindex + 1);
17983         }
17984         if(this.shim){
17985             this.shim.setStyle("z-index", zindex);
17986         }
17987     }
17988 });
17989 })();/*
17990  * Original code for Roojs - LGPL
17991  * <script type="text/javascript">
17992  */
17993  
17994 /**
17995  * @class Roo.XComponent
17996  * A delayed Element creator...
17997  * Or a way to group chunks of interface together.
17998  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17999  *  used in conjunction with XComponent.build() it will create an instance of each element,
18000  *  then call addxtype() to build the User interface.
18001  * 
18002  * Mypart.xyx = new Roo.XComponent({
18003
18004     parent : 'Mypart.xyz', // empty == document.element.!!
18005     order : '001',
18006     name : 'xxxx'
18007     region : 'xxxx'
18008     disabled : function() {} 
18009      
18010     tree : function() { // return an tree of xtype declared components
18011         var MODULE = this;
18012         return 
18013         {
18014             xtype : 'NestedLayoutPanel',
18015             // technicall
18016         }
18017      ]
18018  *})
18019  *
18020  *
18021  * It can be used to build a big heiracy, with parent etc.
18022  * or you can just use this to render a single compoent to a dom element
18023  * MYPART.render(Roo.Element | String(id) | dom_element )
18024  *
18025  *
18026  * Usage patterns.
18027  *
18028  * Classic Roo
18029  *
18030  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
18031  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
18032  *
18033  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
18034  *
18035  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
18036  * - if mulitple topModules exist, the last one is defined as the top module.
18037  *
18038  * Embeded Roo
18039  * 
18040  * When the top level or multiple modules are to embedded into a existing HTML page,
18041  * the parent element can container '#id' of the element where the module will be drawn.
18042  *
18043  * Bootstrap Roo
18044  *
18045  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
18046  * it relies more on a include mechanism, where sub modules are included into an outer page.
18047  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
18048  * 
18049  * Bootstrap Roo Included elements
18050  *
18051  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
18052  * hence confusing the component builder as it thinks there are multiple top level elements. 
18053  *
18054  * String Over-ride & Translations
18055  *
18056  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
18057  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
18058  * are needed. @see Roo.XComponent.overlayString  
18059  * 
18060  * 
18061  * 
18062  * @extends Roo.util.Observable
18063  * @constructor
18064  * @param cfg {Object} configuration of component
18065  * 
18066  */
18067 Roo.XComponent = function(cfg) {
18068     Roo.apply(this, cfg);
18069     this.addEvents({ 
18070         /**
18071              * @event built
18072              * Fires when this the componnt is built
18073              * @param {Roo.XComponent} c the component
18074              */
18075         'built' : true
18076         
18077     });
18078     this.region = this.region || 'center'; // default..
18079     Roo.XComponent.register(this);
18080     this.modules = false;
18081     this.el = false; // where the layout goes..
18082     
18083     
18084 }
18085 Roo.extend(Roo.XComponent, Roo.util.Observable, {
18086     /**
18087      * @property el
18088      * The created element (with Roo.factory())
18089      * @type {Roo.Layout}
18090      */
18091     el  : false,
18092     
18093     /**
18094      * @property el
18095      * for BC  - use el in new code
18096      * @type {Roo.Layout}
18097      */
18098     panel : false,
18099     
18100     /**
18101      * @property layout
18102      * for BC  - use el in new code
18103      * @type {Roo.Layout}
18104      */
18105     layout : false,
18106     
18107      /**
18108      * @cfg {Function|boolean} disabled
18109      * If this module is disabled by some rule, return true from the funtion
18110      */
18111     disabled : false,
18112     
18113     /**
18114      * @cfg {String} parent 
18115      * Name of parent element which it get xtype added to..
18116      */
18117     parent: false,
18118     
18119     /**
18120      * @cfg {String} order
18121      * Used to set the order in which elements are created (usefull for multiple tabs)
18122      */
18123     
18124     order : false,
18125     /**
18126      * @cfg {String} name
18127      * String to display while loading.
18128      */
18129     name : false,
18130     /**
18131      * @cfg {String} region
18132      * Region to render component to (defaults to center)
18133      */
18134     region : 'center',
18135     
18136     /**
18137      * @cfg {Array} items
18138      * A single item array - the first element is the root of the tree..
18139      * It's done this way to stay compatible with the Xtype system...
18140      */
18141     items : false,
18142     
18143     /**
18144      * @property _tree
18145      * The method that retuns the tree of parts that make up this compoennt 
18146      * @type {function}
18147      */
18148     _tree  : false,
18149     
18150      /**
18151      * render
18152      * render element to dom or tree
18153      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
18154      */
18155     
18156     render : function(el)
18157     {
18158         
18159         el = el || false;
18160         var hp = this.parent ? 1 : 0;
18161         Roo.debug &&  Roo.log(this);
18162         
18163         var tree = this._tree ? this._tree() : this.tree();
18164
18165         
18166         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18167             // if parent is a '#.....' string, then let's use that..
18168             var ename = this.parent.substr(1);
18169             this.parent = false;
18170             Roo.debug && Roo.log(ename);
18171             switch (ename) {
18172                 case 'bootstrap-body':
18173                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
18174                         // this is the BorderLayout standard?
18175                        this.parent = { el : true };
18176                        break;
18177                     }
18178                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
18179                         // need to insert stuff...
18180                         this.parent =  {
18181                              el : new Roo.bootstrap.layout.Border({
18182                                  el : document.body, 
18183                      
18184                                  center: {
18185                                     titlebar: false,
18186                                     autoScroll:false,
18187                                     closeOnTab: true,
18188                                     tabPosition: 'top',
18189                                       //resizeTabs: true,
18190                                     alwaysShowTabs: true,
18191                                     hideTabs: false
18192                                      //minTabWidth: 140
18193                                  }
18194                              })
18195                         
18196                          };
18197                          break;
18198                     }
18199                          
18200                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18201                         this.parent = { el :  new  Roo.bootstrap.Body() };
18202                         Roo.debug && Roo.log("setting el to doc body");
18203                          
18204                     } else {
18205                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18206                     }
18207                     break;
18208                 case 'bootstrap':
18209                     this.parent = { el : true};
18210                     // fall through
18211                 default:
18212                     el = Roo.get(ename);
18213                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18214                         this.parent = { el : true};
18215                     }
18216                     
18217                     break;
18218             }
18219                 
18220             
18221             if (!el && !this.parent) {
18222                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18223                 return;
18224             }
18225         }
18226         
18227         Roo.debug && Roo.log("EL:");
18228         Roo.debug && Roo.log(el);
18229         Roo.debug && Roo.log("this.parent.el:");
18230         Roo.debug && Roo.log(this.parent.el);
18231         
18232
18233         // altertive root elements ??? - we need a better way to indicate these.
18234         var is_alt = Roo.XComponent.is_alt ||
18235                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18236                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18237                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18238         
18239         
18240         
18241         if (!this.parent && is_alt) {
18242             //el = Roo.get(document.body);
18243             this.parent = { el : true };
18244         }
18245             
18246             
18247         
18248         if (!this.parent) {
18249             
18250             Roo.debug && Roo.log("no parent - creating one");
18251             
18252             el = el ? Roo.get(el) : false;      
18253             
18254             if (typeof(Roo.BorderLayout) == 'undefined' ) {
18255                 
18256                 this.parent =  {
18257                     el : new Roo.bootstrap.layout.Border({
18258                         el: el || document.body,
18259                     
18260                         center: {
18261                             titlebar: false,
18262                             autoScroll:false,
18263                             closeOnTab: true,
18264                             tabPosition: 'top',
18265                              //resizeTabs: true,
18266                             alwaysShowTabs: false,
18267                             hideTabs: true,
18268                             minTabWidth: 140,
18269                             overflow: 'visible'
18270                          }
18271                      })
18272                 };
18273             } else {
18274             
18275                 // it's a top level one..
18276                 this.parent =  {
18277                     el : new Roo.BorderLayout(el || document.body, {
18278                         center: {
18279                             titlebar: false,
18280                             autoScroll:false,
18281                             closeOnTab: true,
18282                             tabPosition: 'top',
18283                              //resizeTabs: true,
18284                             alwaysShowTabs: el && hp? false :  true,
18285                             hideTabs: el || !hp ? true :  false,
18286                             minTabWidth: 140
18287                          }
18288                     })
18289                 };
18290             }
18291         }
18292         
18293         if (!this.parent.el) {
18294                 // probably an old style ctor, which has been disabled.
18295                 return;
18296
18297         }
18298                 // The 'tree' method is  '_tree now' 
18299             
18300         tree.region = tree.region || this.region;
18301         var is_body = false;
18302         if (this.parent.el === true) {
18303             // bootstrap... - body..
18304             if (el) {
18305                 tree.el = el;
18306             }
18307             this.parent.el = Roo.factory(tree);
18308             is_body = true;
18309         }
18310         
18311         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18312         this.fireEvent('built', this);
18313         
18314         this.panel = this.el;
18315         this.layout = this.panel.layout;
18316         this.parentLayout = this.parent.layout  || false;  
18317          
18318     }
18319     
18320 });
18321
18322 Roo.apply(Roo.XComponent, {
18323     /**
18324      * @property  hideProgress
18325      * true to disable the building progress bar.. usefull on single page renders.
18326      * @type Boolean
18327      */
18328     hideProgress : false,
18329     /**
18330      * @property  buildCompleted
18331      * True when the builder has completed building the interface.
18332      * @type Boolean
18333      */
18334     buildCompleted : false,
18335      
18336     /**
18337      * @property  topModule
18338      * the upper most module - uses document.element as it's constructor.
18339      * @type Object
18340      */
18341      
18342     topModule  : false,
18343       
18344     /**
18345      * @property  modules
18346      * array of modules to be created by registration system.
18347      * @type {Array} of Roo.XComponent
18348      */
18349     
18350     modules : [],
18351     /**
18352      * @property  elmodules
18353      * array of modules to be created by which use #ID 
18354      * @type {Array} of Roo.XComponent
18355      */
18356      
18357     elmodules : [],
18358
18359      /**
18360      * @property  is_alt
18361      * Is an alternative Root - normally used by bootstrap or other systems,
18362      *    where the top element in the tree can wrap 'body' 
18363      * @type {boolean}  (default false)
18364      */
18365      
18366     is_alt : false,
18367     /**
18368      * @property  build_from_html
18369      * Build elements from html - used by bootstrap HTML stuff 
18370      *    - this is cleared after build is completed
18371      * @type {boolean}    (default false)
18372      */
18373      
18374     build_from_html : false,
18375     /**
18376      * Register components to be built later.
18377      *
18378      * This solves the following issues
18379      * - Building is not done on page load, but after an authentication process has occured.
18380      * - Interface elements are registered on page load
18381      * - Parent Interface elements may not be loaded before child, so this handles that..
18382      * 
18383      *
18384      * example:
18385      * 
18386      * MyApp.register({
18387           order : '000001',
18388           module : 'Pman.Tab.projectMgr',
18389           region : 'center',
18390           parent : 'Pman.layout',
18391           disabled : false,  // or use a function..
18392         })
18393      
18394      * * @param {Object} details about module
18395      */
18396     register : function(obj) {
18397                 
18398         Roo.XComponent.event.fireEvent('register', obj);
18399         switch(typeof(obj.disabled) ) {
18400                 
18401             case 'undefined':
18402                 break;
18403             
18404             case 'function':
18405                 if ( obj.disabled() ) {
18406                         return;
18407                 }
18408                 break;
18409             
18410             default:
18411                 if (obj.disabled || obj.region == '#disabled') {
18412                         return;
18413                 }
18414                 break;
18415         }
18416                 
18417         this.modules.push(obj);
18418          
18419     },
18420     /**
18421      * convert a string to an object..
18422      * eg. 'AAA.BBB' -> finds AAA.BBB
18423
18424      */
18425     
18426     toObject : function(str)
18427     {
18428         if (!str || typeof(str) == 'object') {
18429             return str;
18430         }
18431         if (str.substring(0,1) == '#') {
18432             return str;
18433         }
18434
18435         var ar = str.split('.');
18436         var rt, o;
18437         rt = ar.shift();
18438             /** eval:var:o */
18439         try {
18440             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18441         } catch (e) {
18442             throw "Module not found : " + str;
18443         }
18444         
18445         if (o === false) {
18446             throw "Module not found : " + str;
18447         }
18448         Roo.each(ar, function(e) {
18449             if (typeof(o[e]) == 'undefined') {
18450                 throw "Module not found : " + str;
18451             }
18452             o = o[e];
18453         });
18454         
18455         return o;
18456         
18457     },
18458     
18459     
18460     /**
18461      * move modules into their correct place in the tree..
18462      * 
18463      */
18464     preBuild : function ()
18465     {
18466         var _t = this;
18467         Roo.each(this.modules , function (obj)
18468         {
18469             Roo.XComponent.event.fireEvent('beforebuild', obj);
18470             
18471             var opar = obj.parent;
18472             try { 
18473                 obj.parent = this.toObject(opar);
18474             } catch(e) {
18475                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18476                 return;
18477             }
18478             
18479             if (!obj.parent) {
18480                 Roo.debug && Roo.log("GOT top level module");
18481                 Roo.debug && Roo.log(obj);
18482                 obj.modules = new Roo.util.MixedCollection(false, 
18483                     function(o) { return o.order + '' }
18484                 );
18485                 this.topModule = obj;
18486                 return;
18487             }
18488                         // parent is a string (usually a dom element name..)
18489             if (typeof(obj.parent) == 'string') {
18490                 this.elmodules.push(obj);
18491                 return;
18492             }
18493             if (obj.parent.constructor != Roo.XComponent) {
18494                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18495             }
18496             if (!obj.parent.modules) {
18497                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18498                     function(o) { return o.order + '' }
18499                 );
18500             }
18501             if (obj.parent.disabled) {
18502                 obj.disabled = true;
18503             }
18504             obj.parent.modules.add(obj);
18505         }, this);
18506     },
18507     
18508      /**
18509      * make a list of modules to build.
18510      * @return {Array} list of modules. 
18511      */ 
18512     
18513     buildOrder : function()
18514     {
18515         var _this = this;
18516         var cmp = function(a,b) {   
18517             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18518         };
18519         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18520             throw "No top level modules to build";
18521         }
18522         
18523         // make a flat list in order of modules to build.
18524         var mods = this.topModule ? [ this.topModule ] : [];
18525                 
18526         
18527         // elmodules (is a list of DOM based modules )
18528         Roo.each(this.elmodules, function(e) {
18529             mods.push(e);
18530             if (!this.topModule &&
18531                 typeof(e.parent) == 'string' &&
18532                 e.parent.substring(0,1) == '#' &&
18533                 Roo.get(e.parent.substr(1))
18534                ) {
18535                 
18536                 _this.topModule = e;
18537             }
18538             
18539         });
18540
18541         
18542         // add modules to their parents..
18543         var addMod = function(m) {
18544             Roo.debug && Roo.log("build Order: add: " + m.name);
18545                 
18546             mods.push(m);
18547             if (m.modules && !m.disabled) {
18548                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18549                 m.modules.keySort('ASC',  cmp );
18550                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18551     
18552                 m.modules.each(addMod);
18553             } else {
18554                 Roo.debug && Roo.log("build Order: no child modules");
18555             }
18556             // not sure if this is used any more..
18557             if (m.finalize) {
18558                 m.finalize.name = m.name + " (clean up) ";
18559                 mods.push(m.finalize);
18560             }
18561             
18562         }
18563         if (this.topModule && this.topModule.modules) { 
18564             this.topModule.modules.keySort('ASC',  cmp );
18565             this.topModule.modules.each(addMod);
18566         } 
18567         return mods;
18568     },
18569     
18570      /**
18571      * Build the registered modules.
18572      * @param {Object} parent element.
18573      * @param {Function} optional method to call after module has been added.
18574      * 
18575      */ 
18576    
18577     build : function(opts) 
18578     {
18579         
18580         if (typeof(opts) != 'undefined') {
18581             Roo.apply(this,opts);
18582         }
18583         
18584         this.preBuild();
18585         var mods = this.buildOrder();
18586       
18587         //this.allmods = mods;
18588         //Roo.debug && Roo.log(mods);
18589         //return;
18590         if (!mods.length) { // should not happen
18591             throw "NO modules!!!";
18592         }
18593         
18594         
18595         var msg = "Building Interface...";
18596         // flash it up as modal - so we store the mask!?
18597         if (!this.hideProgress && Roo.MessageBox) {
18598             Roo.MessageBox.show({ title: 'loading' });
18599             Roo.MessageBox.show({
18600                title: "Please wait...",
18601                msg: msg,
18602                width:450,
18603                progress:true,
18604                buttons : false,
18605                closable:false,
18606                modal: false
18607               
18608             });
18609         }
18610         var total = mods.length;
18611         
18612         var _this = this;
18613         var progressRun = function() {
18614             if (!mods.length) {
18615                 Roo.debug && Roo.log('hide?');
18616                 if (!this.hideProgress && Roo.MessageBox) {
18617                     Roo.MessageBox.hide();
18618                 }
18619                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18620                 
18621                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18622                 
18623                 // THE END...
18624                 return false;   
18625             }
18626             
18627             var m = mods.shift();
18628             
18629             
18630             Roo.debug && Roo.log(m);
18631             // not sure if this is supported any more.. - modules that are are just function
18632             if (typeof(m) == 'function') { 
18633                 m.call(this);
18634                 return progressRun.defer(10, _this);
18635             } 
18636             
18637             
18638             msg = "Building Interface " + (total  - mods.length) + 
18639                     " of " + total + 
18640                     (m.name ? (' - ' + m.name) : '');
18641                         Roo.debug && Roo.log(msg);
18642             if (!_this.hideProgress &&  Roo.MessageBox) { 
18643                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18644             }
18645             
18646          
18647             // is the module disabled?
18648             var disabled = (typeof(m.disabled) == 'function') ?
18649                 m.disabled.call(m.module.disabled) : m.disabled;    
18650             
18651             
18652             if (disabled) {
18653                 return progressRun(); // we do not update the display!
18654             }
18655             
18656             // now build 
18657             
18658                         
18659                         
18660             m.render();
18661             // it's 10 on top level, and 1 on others??? why...
18662             return progressRun.defer(10, _this);
18663              
18664         }
18665         progressRun.defer(1, _this);
18666      
18667         
18668         
18669     },
18670     /**
18671      * Overlay a set of modified strings onto a component
18672      * This is dependant on our builder exporting the strings and 'named strings' elements.
18673      * 
18674      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18675      * @param {Object} associative array of 'named' string and it's new value.
18676      * 
18677      */
18678         overlayStrings : function( component, strings )
18679     {
18680         if (typeof(component['_named_strings']) == 'undefined') {
18681             throw "ERROR: component does not have _named_strings";
18682         }
18683         for ( var k in strings ) {
18684             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18685             if (md !== false) {
18686                 component['_strings'][md] = strings[k];
18687             } else {
18688                 Roo.log('could not find named string: ' + k + ' in');
18689                 Roo.log(component);
18690             }
18691             
18692         }
18693         
18694     },
18695     
18696         
18697         /**
18698          * Event Object.
18699          *
18700          *
18701          */
18702         event: false, 
18703     /**
18704          * wrapper for event.on - aliased later..  
18705          * Typically use to register a event handler for register:
18706          *
18707          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18708          *
18709          */
18710     on : false
18711    
18712     
18713     
18714 });
18715
18716 Roo.XComponent.event = new Roo.util.Observable({
18717                 events : { 
18718                         /**
18719                          * @event register
18720                          * Fires when an Component is registered,
18721                          * set the disable property on the Component to stop registration.
18722                          * @param {Roo.XComponent} c the component being registerd.
18723                          * 
18724                          */
18725                         'register' : true,
18726             /**
18727                          * @event beforebuild
18728                          * Fires before each Component is built
18729                          * can be used to apply permissions.
18730                          * @param {Roo.XComponent} c the component being registerd.
18731                          * 
18732                          */
18733                         'beforebuild' : true,
18734                         /**
18735                          * @event buildcomplete
18736                          * Fires on the top level element when all elements have been built
18737                          * @param {Roo.XComponent} the top level component.
18738                          */
18739                         'buildcomplete' : true
18740                         
18741                 }
18742 });
18743
18744 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18745  //
18746  /**
18747  * marked - a markdown parser
18748  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18749  * https://github.com/chjj/marked
18750  */
18751
18752
18753 /**
18754  *
18755  * Roo.Markdown - is a very crude wrapper around marked..
18756  *
18757  * usage:
18758  * 
18759  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18760  * 
18761  * Note: move the sample code to the bottom of this
18762  * file before uncommenting it.
18763  *
18764  */
18765
18766 Roo.Markdown = {};
18767 Roo.Markdown.toHtml = function(text) {
18768     
18769     var c = new Roo.Markdown.marked.setOptions({
18770             renderer: new Roo.Markdown.marked.Renderer(),
18771             gfm: true,
18772             tables: true,
18773             breaks: false,
18774             pedantic: false,
18775             sanitize: false,
18776             smartLists: true,
18777             smartypants: false
18778           });
18779     // A FEW HACKS!!?
18780     
18781     text = text.replace(/\\\n/g,' ');
18782     return Roo.Markdown.marked(text);
18783 };
18784 //
18785 // converter
18786 //
18787 // Wraps all "globals" so that the only thing
18788 // exposed is makeHtml().
18789 //
18790 (function() {
18791     
18792      /**
18793          * eval:var:escape
18794          * eval:var:unescape
18795          * eval:var:replace
18796          */
18797       
18798     /**
18799      * Helpers
18800      */
18801     
18802     var escape = function (html, encode) {
18803       return html
18804         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18805         .replace(/</g, '&lt;')
18806         .replace(/>/g, '&gt;')
18807         .replace(/"/g, '&quot;')
18808         .replace(/'/g, '&#39;');
18809     }
18810     
18811     var unescape = function (html) {
18812         // explicitly match decimal, hex, and named HTML entities 
18813       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18814         n = n.toLowerCase();
18815         if (n === 'colon') { return ':'; }
18816         if (n.charAt(0) === '#') {
18817           return n.charAt(1) === 'x'
18818             ? String.fromCharCode(parseInt(n.substring(2), 16))
18819             : String.fromCharCode(+n.substring(1));
18820         }
18821         return '';
18822       });
18823     }
18824     
18825     var replace = function (regex, opt) {
18826       regex = regex.source;
18827       opt = opt || '';
18828       return function self(name, val) {
18829         if (!name) { return new RegExp(regex, opt); }
18830         val = val.source || val;
18831         val = val.replace(/(^|[^\[])\^/g, '$1');
18832         regex = regex.replace(name, val);
18833         return self;
18834       };
18835     }
18836
18837
18838          /**
18839          * eval:var:noop
18840     */
18841     var noop = function () {}
18842     noop.exec = noop;
18843     
18844          /**
18845          * eval:var:merge
18846     */
18847     var merge = function (obj) {
18848       var i = 1
18849         , target
18850         , key;
18851     
18852       for (; i < arguments.length; i++) {
18853         target = arguments[i];
18854         for (key in target) {
18855           if (Object.prototype.hasOwnProperty.call(target, key)) {
18856             obj[key] = target[key];
18857           }
18858         }
18859       }
18860     
18861       return obj;
18862     }
18863     
18864     
18865     /**
18866      * Block-Level Grammar
18867      */
18868     
18869     
18870     
18871     
18872     var block = {
18873       newline: /^\n+/,
18874       code: /^( {4}[^\n]+\n*)+/,
18875       fences: noop,
18876       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18877       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18878       nptable: noop,
18879       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18880       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18881       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18882       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18883       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18884       table: noop,
18885       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18886       text: /^[^\n]+/
18887     };
18888     
18889     block.bullet = /(?:[*+-]|\d+\.)/;
18890     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18891     block.item = replace(block.item, 'gm')
18892       (/bull/g, block.bullet)
18893       ();
18894     
18895     block.list = replace(block.list)
18896       (/bull/g, block.bullet)
18897       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18898       ('def', '\\n+(?=' + block.def.source + ')')
18899       ();
18900     
18901     block.blockquote = replace(block.blockquote)
18902       ('def', block.def)
18903       ();
18904     
18905     block._tag = '(?!(?:'
18906       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18907       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18908       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18909     
18910     block.html = replace(block.html)
18911       ('comment', /<!--[\s\S]*?-->/)
18912       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18913       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18914       (/tag/g, block._tag)
18915       ();
18916     
18917     block.paragraph = replace(block.paragraph)
18918       ('hr', block.hr)
18919       ('heading', block.heading)
18920       ('lheading', block.lheading)
18921       ('blockquote', block.blockquote)
18922       ('tag', '<' + block._tag)
18923       ('def', block.def)
18924       ();
18925     
18926     /**
18927      * Normal Block Grammar
18928      */
18929     
18930     block.normal = merge({}, block);
18931     
18932     /**
18933      * GFM Block Grammar
18934      */
18935     
18936     block.gfm = merge({}, block.normal, {
18937       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18938       paragraph: /^/,
18939       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18940     });
18941     
18942     block.gfm.paragraph = replace(block.paragraph)
18943       ('(?!', '(?!'
18944         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18945         + block.list.source.replace('\\1', '\\3') + '|')
18946       ();
18947     
18948     /**
18949      * GFM + Tables Block Grammar
18950      */
18951     
18952     block.tables = merge({}, block.gfm, {
18953       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18954       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18955     });
18956     
18957     /**
18958      * Block Lexer
18959      */
18960     
18961     var Lexer = function (options) {
18962       this.tokens = [];
18963       this.tokens.links = {};
18964       this.options = options || marked.defaults;
18965       this.rules = block.normal;
18966     
18967       if (this.options.gfm) {
18968         if (this.options.tables) {
18969           this.rules = block.tables;
18970         } else {
18971           this.rules = block.gfm;
18972         }
18973       }
18974     }
18975     
18976     /**
18977      * Expose Block Rules
18978      */
18979     
18980     Lexer.rules = block;
18981     
18982     /**
18983      * Static Lex Method
18984      */
18985     
18986     Lexer.lex = function(src, options) {
18987       var lexer = new Lexer(options);
18988       return lexer.lex(src);
18989     };
18990     
18991     /**
18992      * Preprocessing
18993      */
18994     
18995     Lexer.prototype.lex = function(src) {
18996       src = src
18997         .replace(/\r\n|\r/g, '\n')
18998         .replace(/\t/g, '    ')
18999         .replace(/\u00a0/g, ' ')
19000         .replace(/\u2424/g, '\n');
19001     
19002       return this.token(src, true);
19003     };
19004     
19005     /**
19006      * Lexing
19007      */
19008     
19009     Lexer.prototype.token = function(src, top, bq) {
19010       var src = src.replace(/^ +$/gm, '')
19011         , next
19012         , loose
19013         , cap
19014         , bull
19015         , b
19016         , item
19017         , space
19018         , i
19019         , l;
19020     
19021       while (src) {
19022         // newline
19023         if (cap = this.rules.newline.exec(src)) {
19024           src = src.substring(cap[0].length);
19025           if (cap[0].length > 1) {
19026             this.tokens.push({
19027               type: 'space'
19028             });
19029           }
19030         }
19031     
19032         // code
19033         if (cap = this.rules.code.exec(src)) {
19034           src = src.substring(cap[0].length);
19035           cap = cap[0].replace(/^ {4}/gm, '');
19036           this.tokens.push({
19037             type: 'code',
19038             text: !this.options.pedantic
19039               ? cap.replace(/\n+$/, '')
19040               : cap
19041           });
19042           continue;
19043         }
19044     
19045         // fences (gfm)
19046         if (cap = this.rules.fences.exec(src)) {
19047           src = src.substring(cap[0].length);
19048           this.tokens.push({
19049             type: 'code',
19050             lang: cap[2],
19051             text: cap[3] || ''
19052           });
19053           continue;
19054         }
19055     
19056         // heading
19057         if (cap = this.rules.heading.exec(src)) {
19058           src = src.substring(cap[0].length);
19059           this.tokens.push({
19060             type: 'heading',
19061             depth: cap[1].length,
19062             text: cap[2]
19063           });
19064           continue;
19065         }
19066     
19067         // table no leading pipe (gfm)
19068         if (top && (cap = this.rules.nptable.exec(src))) {
19069           src = src.substring(cap[0].length);
19070     
19071           item = {
19072             type: 'table',
19073             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19074             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19075             cells: cap[3].replace(/\n$/, '').split('\n')
19076           };
19077     
19078           for (i = 0; i < item.align.length; i++) {
19079             if (/^ *-+: *$/.test(item.align[i])) {
19080               item.align[i] = 'right';
19081             } else if (/^ *:-+: *$/.test(item.align[i])) {
19082               item.align[i] = 'center';
19083             } else if (/^ *:-+ *$/.test(item.align[i])) {
19084               item.align[i] = 'left';
19085             } else {
19086               item.align[i] = null;
19087             }
19088           }
19089     
19090           for (i = 0; i < item.cells.length; i++) {
19091             item.cells[i] = item.cells[i].split(/ *\| */);
19092           }
19093     
19094           this.tokens.push(item);
19095     
19096           continue;
19097         }
19098     
19099         // lheading
19100         if (cap = this.rules.lheading.exec(src)) {
19101           src = src.substring(cap[0].length);
19102           this.tokens.push({
19103             type: 'heading',
19104             depth: cap[2] === '=' ? 1 : 2,
19105             text: cap[1]
19106           });
19107           continue;
19108         }
19109     
19110         // hr
19111         if (cap = this.rules.hr.exec(src)) {
19112           src = src.substring(cap[0].length);
19113           this.tokens.push({
19114             type: 'hr'
19115           });
19116           continue;
19117         }
19118     
19119         // blockquote
19120         if (cap = this.rules.blockquote.exec(src)) {
19121           src = src.substring(cap[0].length);
19122     
19123           this.tokens.push({
19124             type: 'blockquote_start'
19125           });
19126     
19127           cap = cap[0].replace(/^ *> ?/gm, '');
19128     
19129           // Pass `top` to keep the current
19130           // "toplevel" state. This is exactly
19131           // how markdown.pl works.
19132           this.token(cap, top, true);
19133     
19134           this.tokens.push({
19135             type: 'blockquote_end'
19136           });
19137     
19138           continue;
19139         }
19140     
19141         // list
19142         if (cap = this.rules.list.exec(src)) {
19143           src = src.substring(cap[0].length);
19144           bull = cap[2];
19145     
19146           this.tokens.push({
19147             type: 'list_start',
19148             ordered: bull.length > 1
19149           });
19150     
19151           // Get each top-level item.
19152           cap = cap[0].match(this.rules.item);
19153     
19154           next = false;
19155           l = cap.length;
19156           i = 0;
19157     
19158           for (; i < l; i++) {
19159             item = cap[i];
19160     
19161             // Remove the list item's bullet
19162             // so it is seen as the next token.
19163             space = item.length;
19164             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19165     
19166             // Outdent whatever the
19167             // list item contains. Hacky.
19168             if (~item.indexOf('\n ')) {
19169               space -= item.length;
19170               item = !this.options.pedantic
19171                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19172                 : item.replace(/^ {1,4}/gm, '');
19173             }
19174     
19175             // Determine whether the next list item belongs here.
19176             // Backpedal if it does not belong in this list.
19177             if (this.options.smartLists && i !== l - 1) {
19178               b = block.bullet.exec(cap[i + 1])[0];
19179               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19180                 src = cap.slice(i + 1).join('\n') + src;
19181                 i = l - 1;
19182               }
19183             }
19184     
19185             // Determine whether item is loose or not.
19186             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19187             // for discount behavior.
19188             loose = next || /\n\n(?!\s*$)/.test(item);
19189             if (i !== l - 1) {
19190               next = item.charAt(item.length - 1) === '\n';
19191               if (!loose) { loose = next; }
19192             }
19193     
19194             this.tokens.push({
19195               type: loose
19196                 ? 'loose_item_start'
19197                 : 'list_item_start'
19198             });
19199     
19200             // Recurse.
19201             this.token(item, false, bq);
19202     
19203             this.tokens.push({
19204               type: 'list_item_end'
19205             });
19206           }
19207     
19208           this.tokens.push({
19209             type: 'list_end'
19210           });
19211     
19212           continue;
19213         }
19214     
19215         // html
19216         if (cap = this.rules.html.exec(src)) {
19217           src = src.substring(cap[0].length);
19218           this.tokens.push({
19219             type: this.options.sanitize
19220               ? 'paragraph'
19221               : 'html',
19222             pre: !this.options.sanitizer
19223               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19224             text: cap[0]
19225           });
19226           continue;
19227         }
19228     
19229         // def
19230         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19231           src = src.substring(cap[0].length);
19232           this.tokens.links[cap[1].toLowerCase()] = {
19233             href: cap[2],
19234             title: cap[3]
19235           };
19236           continue;
19237         }
19238     
19239         // table (gfm)
19240         if (top && (cap = this.rules.table.exec(src))) {
19241           src = src.substring(cap[0].length);
19242     
19243           item = {
19244             type: 'table',
19245             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19246             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19247             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19248           };
19249     
19250           for (i = 0; i < item.align.length; i++) {
19251             if (/^ *-+: *$/.test(item.align[i])) {
19252               item.align[i] = 'right';
19253             } else if (/^ *:-+: *$/.test(item.align[i])) {
19254               item.align[i] = 'center';
19255             } else if (/^ *:-+ *$/.test(item.align[i])) {
19256               item.align[i] = 'left';
19257             } else {
19258               item.align[i] = null;
19259             }
19260           }
19261     
19262           for (i = 0; i < item.cells.length; i++) {
19263             item.cells[i] = item.cells[i]
19264               .replace(/^ *\| *| *\| *$/g, '')
19265               .split(/ *\| */);
19266           }
19267     
19268           this.tokens.push(item);
19269     
19270           continue;
19271         }
19272     
19273         // top-level paragraph
19274         if (top && (cap = this.rules.paragraph.exec(src))) {
19275           src = src.substring(cap[0].length);
19276           this.tokens.push({
19277             type: 'paragraph',
19278             text: cap[1].charAt(cap[1].length - 1) === '\n'
19279               ? cap[1].slice(0, -1)
19280               : cap[1]
19281           });
19282           continue;
19283         }
19284     
19285         // text
19286         if (cap = this.rules.text.exec(src)) {
19287           // Top-level should never reach here.
19288           src = src.substring(cap[0].length);
19289           this.tokens.push({
19290             type: 'text',
19291             text: cap[0]
19292           });
19293           continue;
19294         }
19295     
19296         if (src) {
19297           throw new
19298             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19299         }
19300       }
19301     
19302       return this.tokens;
19303     };
19304     
19305     /**
19306      * Inline-Level Grammar
19307      */
19308     
19309     var inline = {
19310       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19311       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19312       url: noop,
19313       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19314       link: /^!?\[(inside)\]\(href\)/,
19315       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19316       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19317       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19318       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19319       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19320       br: /^ {2,}\n(?!\s*$)/,
19321       del: noop,
19322       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19323     };
19324     
19325     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19326     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19327     
19328     inline.link = replace(inline.link)
19329       ('inside', inline._inside)
19330       ('href', inline._href)
19331       ();
19332     
19333     inline.reflink = replace(inline.reflink)
19334       ('inside', inline._inside)
19335       ();
19336     
19337     /**
19338      * Normal Inline Grammar
19339      */
19340     
19341     inline.normal = merge({}, inline);
19342     
19343     /**
19344      * Pedantic Inline Grammar
19345      */
19346     
19347     inline.pedantic = merge({}, inline.normal, {
19348       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19349       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19350     });
19351     
19352     /**
19353      * GFM Inline Grammar
19354      */
19355     
19356     inline.gfm = merge({}, inline.normal, {
19357       escape: replace(inline.escape)('])', '~|])')(),
19358       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19359       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19360       text: replace(inline.text)
19361         (']|', '~]|')
19362         ('|', '|https?://|')
19363         ()
19364     });
19365     
19366     /**
19367      * GFM + Line Breaks Inline Grammar
19368      */
19369     
19370     inline.breaks = merge({}, inline.gfm, {
19371       br: replace(inline.br)('{2,}', '*')(),
19372       text: replace(inline.gfm.text)('{2,}', '*')()
19373     });
19374     
19375     /**
19376      * Inline Lexer & Compiler
19377      */
19378     
19379     var InlineLexer  = function (links, options) {
19380       this.options = options || marked.defaults;
19381       this.links = links;
19382       this.rules = inline.normal;
19383       this.renderer = this.options.renderer || new Renderer;
19384       this.renderer.options = this.options;
19385     
19386       if (!this.links) {
19387         throw new
19388           Error('Tokens array requires a `links` property.');
19389       }
19390     
19391       if (this.options.gfm) {
19392         if (this.options.breaks) {
19393           this.rules = inline.breaks;
19394         } else {
19395           this.rules = inline.gfm;
19396         }
19397       } else if (this.options.pedantic) {
19398         this.rules = inline.pedantic;
19399       }
19400     }
19401     
19402     /**
19403      * Expose Inline Rules
19404      */
19405     
19406     InlineLexer.rules = inline;
19407     
19408     /**
19409      * Static Lexing/Compiling Method
19410      */
19411     
19412     InlineLexer.output = function(src, links, options) {
19413       var inline = new InlineLexer(links, options);
19414       return inline.output(src);
19415     };
19416     
19417     /**
19418      * Lexing/Compiling
19419      */
19420     
19421     InlineLexer.prototype.output = function(src) {
19422       var out = ''
19423         , link
19424         , text
19425         , href
19426         , cap;
19427     
19428       while (src) {
19429         // escape
19430         if (cap = this.rules.escape.exec(src)) {
19431           src = src.substring(cap[0].length);
19432           out += cap[1];
19433           continue;
19434         }
19435     
19436         // autolink
19437         if (cap = this.rules.autolink.exec(src)) {
19438           src = src.substring(cap[0].length);
19439           if (cap[2] === '@') {
19440             text = cap[1].charAt(6) === ':'
19441               ? this.mangle(cap[1].substring(7))
19442               : this.mangle(cap[1]);
19443             href = this.mangle('mailto:') + text;
19444           } else {
19445             text = escape(cap[1]);
19446             href = text;
19447           }
19448           out += this.renderer.link(href, null, text);
19449           continue;
19450         }
19451     
19452         // url (gfm)
19453         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19454           src = src.substring(cap[0].length);
19455           text = escape(cap[1]);
19456           href = text;
19457           out += this.renderer.link(href, null, text);
19458           continue;
19459         }
19460     
19461         // tag
19462         if (cap = this.rules.tag.exec(src)) {
19463           if (!this.inLink && /^<a /i.test(cap[0])) {
19464             this.inLink = true;
19465           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19466             this.inLink = false;
19467           }
19468           src = src.substring(cap[0].length);
19469           out += this.options.sanitize
19470             ? this.options.sanitizer
19471               ? this.options.sanitizer(cap[0])
19472               : escape(cap[0])
19473             : cap[0];
19474           continue;
19475         }
19476     
19477         // link
19478         if (cap = this.rules.link.exec(src)) {
19479           src = src.substring(cap[0].length);
19480           this.inLink = true;
19481           out += this.outputLink(cap, {
19482             href: cap[2],
19483             title: cap[3]
19484           });
19485           this.inLink = false;
19486           continue;
19487         }
19488     
19489         // reflink, nolink
19490         if ((cap = this.rules.reflink.exec(src))
19491             || (cap = this.rules.nolink.exec(src))) {
19492           src = src.substring(cap[0].length);
19493           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19494           link = this.links[link.toLowerCase()];
19495           if (!link || !link.href) {
19496             out += cap[0].charAt(0);
19497             src = cap[0].substring(1) + src;
19498             continue;
19499           }
19500           this.inLink = true;
19501           out += this.outputLink(cap, link);
19502           this.inLink = false;
19503           continue;
19504         }
19505     
19506         // strong
19507         if (cap = this.rules.strong.exec(src)) {
19508           src = src.substring(cap[0].length);
19509           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19510           continue;
19511         }
19512     
19513         // em
19514         if (cap = this.rules.em.exec(src)) {
19515           src = src.substring(cap[0].length);
19516           out += this.renderer.em(this.output(cap[2] || cap[1]));
19517           continue;
19518         }
19519     
19520         // code
19521         if (cap = this.rules.code.exec(src)) {
19522           src = src.substring(cap[0].length);
19523           out += this.renderer.codespan(escape(cap[2], true));
19524           continue;
19525         }
19526     
19527         // br
19528         if (cap = this.rules.br.exec(src)) {
19529           src = src.substring(cap[0].length);
19530           out += this.renderer.br();
19531           continue;
19532         }
19533     
19534         // del (gfm)
19535         if (cap = this.rules.del.exec(src)) {
19536           src = src.substring(cap[0].length);
19537           out += this.renderer.del(this.output(cap[1]));
19538           continue;
19539         }
19540     
19541         // text
19542         if (cap = this.rules.text.exec(src)) {
19543           src = src.substring(cap[0].length);
19544           out += this.renderer.text(escape(this.smartypants(cap[0])));
19545           continue;
19546         }
19547     
19548         if (src) {
19549           throw new
19550             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19551         }
19552       }
19553     
19554       return out;
19555     };
19556     
19557     /**
19558      * Compile Link
19559      */
19560     
19561     InlineLexer.prototype.outputLink = function(cap, link) {
19562       var href = escape(link.href)
19563         , title = link.title ? escape(link.title) : null;
19564     
19565       return cap[0].charAt(0) !== '!'
19566         ? this.renderer.link(href, title, this.output(cap[1]))
19567         : this.renderer.image(href, title, escape(cap[1]));
19568     };
19569     
19570     /**
19571      * Smartypants Transformations
19572      */
19573     
19574     InlineLexer.prototype.smartypants = function(text) {
19575       if (!this.options.smartypants)  { return text; }
19576       return text
19577         // em-dashes
19578         .replace(/---/g, '\u2014')
19579         // en-dashes
19580         .replace(/--/g, '\u2013')
19581         // opening singles
19582         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19583         // closing singles & apostrophes
19584         .replace(/'/g, '\u2019')
19585         // opening doubles
19586         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19587         // closing doubles
19588         .replace(/"/g, '\u201d')
19589         // ellipses
19590         .replace(/\.{3}/g, '\u2026');
19591     };
19592     
19593     /**
19594      * Mangle Links
19595      */
19596     
19597     InlineLexer.prototype.mangle = function(text) {
19598       if (!this.options.mangle) { return text; }
19599       var out = ''
19600         , l = text.length
19601         , i = 0
19602         , ch;
19603     
19604       for (; i < l; i++) {
19605         ch = text.charCodeAt(i);
19606         if (Math.random() > 0.5) {
19607           ch = 'x' + ch.toString(16);
19608         }
19609         out += '&#' + ch + ';';
19610       }
19611     
19612       return out;
19613     };
19614     
19615     /**
19616      * Renderer
19617      */
19618     
19619      /**
19620          * eval:var:Renderer
19621     */
19622     
19623     var Renderer   = function (options) {
19624       this.options = options || {};
19625     }
19626     
19627     Renderer.prototype.code = function(code, lang, escaped) {
19628       if (this.options.highlight) {
19629         var out = this.options.highlight(code, lang);
19630         if (out != null && out !== code) {
19631           escaped = true;
19632           code = out;
19633         }
19634       } else {
19635             // hack!!! - it's already escapeD?
19636             escaped = true;
19637       }
19638     
19639       if (!lang) {
19640         return '<pre><code>'
19641           + (escaped ? code : escape(code, true))
19642           + '\n</code></pre>';
19643       }
19644     
19645       return '<pre><code class="'
19646         + this.options.langPrefix
19647         + escape(lang, true)
19648         + '">'
19649         + (escaped ? code : escape(code, true))
19650         + '\n</code></pre>\n';
19651     };
19652     
19653     Renderer.prototype.blockquote = function(quote) {
19654       return '<blockquote>\n' + quote + '</blockquote>\n';
19655     };
19656     
19657     Renderer.prototype.html = function(html) {
19658       return html;
19659     };
19660     
19661     Renderer.prototype.heading = function(text, level, raw) {
19662       return '<h'
19663         + level
19664         + ' id="'
19665         + this.options.headerPrefix
19666         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19667         + '">'
19668         + text
19669         + '</h'
19670         + level
19671         + '>\n';
19672     };
19673     
19674     Renderer.prototype.hr = function() {
19675       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19676     };
19677     
19678     Renderer.prototype.list = function(body, ordered) {
19679       var type = ordered ? 'ol' : 'ul';
19680       return '<' + type + '>\n' + body + '</' + type + '>\n';
19681     };
19682     
19683     Renderer.prototype.listitem = function(text) {
19684       return '<li>' + text + '</li>\n';
19685     };
19686     
19687     Renderer.prototype.paragraph = function(text) {
19688       return '<p>' + text + '</p>\n';
19689     };
19690     
19691     Renderer.prototype.table = function(header, body) {
19692       return '<table class="table table-striped">\n'
19693         + '<thead>\n'
19694         + header
19695         + '</thead>\n'
19696         + '<tbody>\n'
19697         + body
19698         + '</tbody>\n'
19699         + '</table>\n';
19700     };
19701     
19702     Renderer.prototype.tablerow = function(content) {
19703       return '<tr>\n' + content + '</tr>\n';
19704     };
19705     
19706     Renderer.prototype.tablecell = function(content, flags) {
19707       var type = flags.header ? 'th' : 'td';
19708       var tag = flags.align
19709         ? '<' + type + ' style="text-align:' + flags.align + '">'
19710         : '<' + type + '>';
19711       return tag + content + '</' + type + '>\n';
19712     };
19713     
19714     // span level renderer
19715     Renderer.prototype.strong = function(text) {
19716       return '<strong>' + text + '</strong>';
19717     };
19718     
19719     Renderer.prototype.em = function(text) {
19720       return '<em>' + text + '</em>';
19721     };
19722     
19723     Renderer.prototype.codespan = function(text) {
19724       return '<code>' + text + '</code>';
19725     };
19726     
19727     Renderer.prototype.br = function() {
19728       return this.options.xhtml ? '<br/>' : '<br>';
19729     };
19730     
19731     Renderer.prototype.del = function(text) {
19732       return '<del>' + text + '</del>';
19733     };
19734     
19735     Renderer.prototype.link = function(href, title, text) {
19736       if (this.options.sanitize) {
19737         try {
19738           var prot = decodeURIComponent(unescape(href))
19739             .replace(/[^\w:]/g, '')
19740             .toLowerCase();
19741         } catch (e) {
19742           return '';
19743         }
19744         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19745           return '';
19746         }
19747       }
19748       var out = '<a href="' + href + '"';
19749       if (title) {
19750         out += ' title="' + title + '"';
19751       }
19752       out += '>' + text + '</a>';
19753       return out;
19754     };
19755     
19756     Renderer.prototype.image = function(href, title, text) {
19757       var out = '<img src="' + href + '" alt="' + text + '"';
19758       if (title) {
19759         out += ' title="' + title + '"';
19760       }
19761       out += this.options.xhtml ? '/>' : '>';
19762       return out;
19763     };
19764     
19765     Renderer.prototype.text = function(text) {
19766       return text;
19767     };
19768     
19769     /**
19770      * Parsing & Compiling
19771      */
19772          /**
19773          * eval:var:Parser
19774     */
19775     
19776     var Parser= function (options) {
19777       this.tokens = [];
19778       this.token = null;
19779       this.options = options || marked.defaults;
19780       this.options.renderer = this.options.renderer || new Renderer;
19781       this.renderer = this.options.renderer;
19782       this.renderer.options = this.options;
19783     }
19784     
19785     /**
19786      * Static Parse Method
19787      */
19788     
19789     Parser.parse = function(src, options, renderer) {
19790       var parser = new Parser(options, renderer);
19791       return parser.parse(src);
19792     };
19793     
19794     /**
19795      * Parse Loop
19796      */
19797     
19798     Parser.prototype.parse = function(src) {
19799       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19800       this.tokens = src.reverse();
19801     
19802       var out = '';
19803       while (this.next()) {
19804         out += this.tok();
19805       }
19806     
19807       return out;
19808     };
19809     
19810     /**
19811      * Next Token
19812      */
19813     
19814     Parser.prototype.next = function() {
19815       return this.token = this.tokens.pop();
19816     };
19817     
19818     /**
19819      * Preview Next Token
19820      */
19821     
19822     Parser.prototype.peek = function() {
19823       return this.tokens[this.tokens.length - 1] || 0;
19824     };
19825     
19826     /**
19827      * Parse Text Tokens
19828      */
19829     
19830     Parser.prototype.parseText = function() {
19831       var body = this.token.text;
19832     
19833       while (this.peek().type === 'text') {
19834         body += '\n' + this.next().text;
19835       }
19836     
19837       return this.inline.output(body);
19838     };
19839     
19840     /**
19841      * Parse Current Token
19842      */
19843     
19844     Parser.prototype.tok = function() {
19845       switch (this.token.type) {
19846         case 'space': {
19847           return '';
19848         }
19849         case 'hr': {
19850           return this.renderer.hr();
19851         }
19852         case 'heading': {
19853           return this.renderer.heading(
19854             this.inline.output(this.token.text),
19855             this.token.depth,
19856             this.token.text);
19857         }
19858         case 'code': {
19859           return this.renderer.code(this.token.text,
19860             this.token.lang,
19861             this.token.escaped);
19862         }
19863         case 'table': {
19864           var header = ''
19865             , body = ''
19866             , i
19867             , row
19868             , cell
19869             , flags
19870             , j;
19871     
19872           // header
19873           cell = '';
19874           for (i = 0; i < this.token.header.length; i++) {
19875             flags = { header: true, align: this.token.align[i] };
19876             cell += this.renderer.tablecell(
19877               this.inline.output(this.token.header[i]),
19878               { header: true, align: this.token.align[i] }
19879             );
19880           }
19881           header += this.renderer.tablerow(cell);
19882     
19883           for (i = 0; i < this.token.cells.length; i++) {
19884             row = this.token.cells[i];
19885     
19886             cell = '';
19887             for (j = 0; j < row.length; j++) {
19888               cell += this.renderer.tablecell(
19889                 this.inline.output(row[j]),
19890                 { header: false, align: this.token.align[j] }
19891               );
19892             }
19893     
19894             body += this.renderer.tablerow(cell);
19895           }
19896           return this.renderer.table(header, body);
19897         }
19898         case 'blockquote_start': {
19899           var body = '';
19900     
19901           while (this.next().type !== 'blockquote_end') {
19902             body += this.tok();
19903           }
19904     
19905           return this.renderer.blockquote(body);
19906         }
19907         case 'list_start': {
19908           var body = ''
19909             , ordered = this.token.ordered;
19910     
19911           while (this.next().type !== 'list_end') {
19912             body += this.tok();
19913           }
19914     
19915           return this.renderer.list(body, ordered);
19916         }
19917         case 'list_item_start': {
19918           var body = '';
19919     
19920           while (this.next().type !== 'list_item_end') {
19921             body += this.token.type === 'text'
19922               ? this.parseText()
19923               : this.tok();
19924           }
19925     
19926           return this.renderer.listitem(body);
19927         }
19928         case 'loose_item_start': {
19929           var body = '';
19930     
19931           while (this.next().type !== 'list_item_end') {
19932             body += this.tok();
19933           }
19934     
19935           return this.renderer.listitem(body);
19936         }
19937         case 'html': {
19938           var html = !this.token.pre && !this.options.pedantic
19939             ? this.inline.output(this.token.text)
19940             : this.token.text;
19941           return this.renderer.html(html);
19942         }
19943         case 'paragraph': {
19944           return this.renderer.paragraph(this.inline.output(this.token.text));
19945         }
19946         case 'text': {
19947           return this.renderer.paragraph(this.parseText());
19948         }
19949       }
19950     };
19951   
19952     
19953     /**
19954      * Marked
19955      */
19956          /**
19957          * eval:var:marked
19958     */
19959     var marked = function (src, opt, callback) {
19960       if (callback || typeof opt === 'function') {
19961         if (!callback) {
19962           callback = opt;
19963           opt = null;
19964         }
19965     
19966         opt = merge({}, marked.defaults, opt || {});
19967     
19968         var highlight = opt.highlight
19969           , tokens
19970           , pending
19971           , i = 0;
19972     
19973         try {
19974           tokens = Lexer.lex(src, opt)
19975         } catch (e) {
19976           return callback(e);
19977         }
19978     
19979         pending = tokens.length;
19980          /**
19981          * eval:var:done
19982     */
19983         var done = function(err) {
19984           if (err) {
19985             opt.highlight = highlight;
19986             return callback(err);
19987           }
19988     
19989           var out;
19990     
19991           try {
19992             out = Parser.parse(tokens, opt);
19993           } catch (e) {
19994             err = e;
19995           }
19996     
19997           opt.highlight = highlight;
19998     
19999           return err
20000             ? callback(err)
20001             : callback(null, out);
20002         };
20003     
20004         if (!highlight || highlight.length < 3) {
20005           return done();
20006         }
20007     
20008         delete opt.highlight;
20009     
20010         if (!pending) { return done(); }
20011     
20012         for (; i < tokens.length; i++) {
20013           (function(token) {
20014             if (token.type !== 'code') {
20015               return --pending || done();
20016             }
20017             return highlight(token.text, token.lang, function(err, code) {
20018               if (err) { return done(err); }
20019               if (code == null || code === token.text) {
20020                 return --pending || done();
20021               }
20022               token.text = code;
20023               token.escaped = true;
20024               --pending || done();
20025             });
20026           })(tokens[i]);
20027         }
20028     
20029         return;
20030       }
20031       try {
20032         if (opt) { opt = merge({}, marked.defaults, opt); }
20033         return Parser.parse(Lexer.lex(src, opt), opt);
20034       } catch (e) {
20035         e.message += '\nPlease report this to https://github.com/chjj/marked.';
20036         if ((opt || marked.defaults).silent) {
20037           return '<p>An error occured:</p><pre>'
20038             + escape(e.message + '', true)
20039             + '</pre>';
20040         }
20041         throw e;
20042       }
20043     }
20044     
20045     /**
20046      * Options
20047      */
20048     
20049     marked.options =
20050     marked.setOptions = function(opt) {
20051       merge(marked.defaults, opt);
20052       return marked;
20053     };
20054     
20055     marked.defaults = {
20056       gfm: true,
20057       tables: true,
20058       breaks: false,
20059       pedantic: false,
20060       sanitize: false,
20061       sanitizer: null,
20062       mangle: true,
20063       smartLists: false,
20064       silent: false,
20065       highlight: null,
20066       langPrefix: 'lang-',
20067       smartypants: false,
20068       headerPrefix: '',
20069       renderer: new Renderer,
20070       xhtml: false
20071     };
20072     
20073     /**
20074      * Expose
20075      */
20076     
20077     marked.Parser = Parser;
20078     marked.parser = Parser.parse;
20079     
20080     marked.Renderer = Renderer;
20081     
20082     marked.Lexer = Lexer;
20083     marked.lexer = Lexer.lex;
20084     
20085     marked.InlineLexer = InlineLexer;
20086     marked.inlineLexer = InlineLexer.output;
20087     
20088     marked.parse = marked;
20089     
20090     Roo.Markdown.marked = marked;
20091
20092 })();/*
20093  * Based on:
20094  * Ext JS Library 1.1.1
20095  * Copyright(c) 2006-2007, Ext JS, LLC.
20096  *
20097  * Originally Released Under LGPL - original licence link has changed is not relivant.
20098  *
20099  * Fork - LGPL
20100  * <script type="text/javascript">
20101  */
20102
20103
20104
20105 /*
20106  * These classes are derivatives of the similarly named classes in the YUI Library.
20107  * The original license:
20108  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
20109  * Code licensed under the BSD License:
20110  * http://developer.yahoo.net/yui/license.txt
20111  */
20112
20113 (function() {
20114
20115 var Event=Roo.EventManager;
20116 var Dom=Roo.lib.Dom;
20117
20118 /**
20119  * @class Roo.dd.DragDrop
20120  * @extends Roo.util.Observable
20121  * Defines the interface and base operation of items that that can be
20122  * dragged or can be drop targets.  It was designed to be extended, overriding
20123  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
20124  * Up to three html elements can be associated with a DragDrop instance:
20125  * <ul>
20126  * <li>linked element: the element that is passed into the constructor.
20127  * This is the element which defines the boundaries for interaction with
20128  * other DragDrop objects.</li>
20129  * <li>handle element(s): The drag operation only occurs if the element that
20130  * was clicked matches a handle element.  By default this is the linked
20131  * element, but there are times that you will want only a portion of the
20132  * linked element to initiate the drag operation, and the setHandleElId()
20133  * method provides a way to define this.</li>
20134  * <li>drag element: this represents the element that would be moved along
20135  * with the cursor during a drag operation.  By default, this is the linked
20136  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
20137  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
20138  * </li>
20139  * </ul>
20140  * This class should not be instantiated until the onload event to ensure that
20141  * the associated elements are available.
20142  * The following would define a DragDrop obj that would interact with any
20143  * other DragDrop obj in the "group1" group:
20144  * <pre>
20145  *  dd = new Roo.dd.DragDrop("div1", "group1");
20146  * </pre>
20147  * Since none of the event handlers have been implemented, nothing would
20148  * actually happen if you were to run the code above.  Normally you would
20149  * override this class or one of the default implementations, but you can
20150  * also override the methods you want on an instance of the class...
20151  * <pre>
20152  *  dd.onDragDrop = function(e, id) {
20153  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
20154  *  }
20155  * </pre>
20156  * @constructor
20157  * @param {String} id of the element that is linked to this instance
20158  * @param {String} sGroup the group of related DragDrop objects
20159  * @param {object} config an object containing configurable attributes
20160  *                Valid properties for DragDrop:
20161  *                    padding, isTarget, maintainOffset, primaryButtonOnly
20162  */
20163 Roo.dd.DragDrop = function(id, sGroup, config) {
20164     if (id) {
20165         this.init(id, sGroup, config);
20166     }
20167     
20168 };
20169
20170 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20171
20172     /**
20173      * The id of the element associated with this object.  This is what we
20174      * refer to as the "linked element" because the size and position of
20175      * this element is used to determine when the drag and drop objects have
20176      * interacted.
20177      * @property id
20178      * @type String
20179      */
20180     id: null,
20181
20182     /**
20183      * Configuration attributes passed into the constructor
20184      * @property config
20185      * @type object
20186      */
20187     config: null,
20188
20189     /**
20190      * The id of the element that will be dragged.  By default this is same
20191      * as the linked element , but could be changed to another element. Ex:
20192      * Roo.dd.DDProxy
20193      * @property dragElId
20194      * @type String
20195      * @private
20196      */
20197     dragElId: null,
20198
20199     /**
20200      * the id of the element that initiates the drag operation.  By default
20201      * this is the linked element, but could be changed to be a child of this
20202      * element.  This lets us do things like only starting the drag when the
20203      * header element within the linked html element is clicked.
20204      * @property handleElId
20205      * @type String
20206      * @private
20207      */
20208     handleElId: null,
20209
20210     /**
20211      * An associative array of HTML tags that will be ignored if clicked.
20212      * @property invalidHandleTypes
20213      * @type {string: string}
20214      */
20215     invalidHandleTypes: null,
20216
20217     /**
20218      * An associative array of ids for elements that will be ignored if clicked
20219      * @property invalidHandleIds
20220      * @type {string: string}
20221      */
20222     invalidHandleIds: null,
20223
20224     /**
20225      * An indexted array of css class names for elements that will be ignored
20226      * if clicked.
20227      * @property invalidHandleClasses
20228      * @type string[]
20229      */
20230     invalidHandleClasses: null,
20231
20232     /**
20233      * The linked element's absolute X position at the time the drag was
20234      * started
20235      * @property startPageX
20236      * @type int
20237      * @private
20238      */
20239     startPageX: 0,
20240
20241     /**
20242      * The linked element's absolute X position at the time the drag was
20243      * started
20244      * @property startPageY
20245      * @type int
20246      * @private
20247      */
20248     startPageY: 0,
20249
20250     /**
20251      * The group defines a logical collection of DragDrop objects that are
20252      * related.  Instances only get events when interacting with other
20253      * DragDrop object in the same group.  This lets us define multiple
20254      * groups using a single DragDrop subclass if we want.
20255      * @property groups
20256      * @type {string: string}
20257      */
20258     groups: null,
20259
20260     /**
20261      * Individual drag/drop instances can be locked.  This will prevent
20262      * onmousedown start drag.
20263      * @property locked
20264      * @type boolean
20265      * @private
20266      */
20267     locked: false,
20268
20269     /**
20270      * Lock this instance
20271      * @method lock
20272      */
20273     lock: function() { this.locked = true; },
20274
20275     /**
20276      * Unlock this instace
20277      * @method unlock
20278      */
20279     unlock: function() { this.locked = false; },
20280
20281     /**
20282      * By default, all insances can be a drop target.  This can be disabled by
20283      * setting isTarget to false.
20284      * @method isTarget
20285      * @type boolean
20286      */
20287     isTarget: true,
20288
20289     /**
20290      * The padding configured for this drag and drop object for calculating
20291      * the drop zone intersection with this object.
20292      * @method padding
20293      * @type int[]
20294      */
20295     padding: null,
20296
20297     /**
20298      * Cached reference to the linked element
20299      * @property _domRef
20300      * @private
20301      */
20302     _domRef: null,
20303
20304     /**
20305      * Internal typeof flag
20306      * @property __ygDragDrop
20307      * @private
20308      */
20309     __ygDragDrop: true,
20310
20311     /**
20312      * Set to true when horizontal contraints are applied
20313      * @property constrainX
20314      * @type boolean
20315      * @private
20316      */
20317     constrainX: false,
20318
20319     /**
20320      * Set to true when vertical contraints are applied
20321      * @property constrainY
20322      * @type boolean
20323      * @private
20324      */
20325     constrainY: false,
20326
20327     /**
20328      * The left constraint
20329      * @property minX
20330      * @type int
20331      * @private
20332      */
20333     minX: 0,
20334
20335     /**
20336      * The right constraint
20337      * @property maxX
20338      * @type int
20339      * @private
20340      */
20341     maxX: 0,
20342
20343     /**
20344      * The up constraint
20345      * @property minY
20346      * @type int
20347      * @type int
20348      * @private
20349      */
20350     minY: 0,
20351
20352     /**
20353      * The down constraint
20354      * @property maxY
20355      * @type int
20356      * @private
20357      */
20358     maxY: 0,
20359
20360     /**
20361      * Maintain offsets when we resetconstraints.  Set to true when you want
20362      * the position of the element relative to its parent to stay the same
20363      * when the page changes
20364      *
20365      * @property maintainOffset
20366      * @type boolean
20367      */
20368     maintainOffset: false,
20369
20370     /**
20371      * Array of pixel locations the element will snap to if we specified a
20372      * horizontal graduation/interval.  This array is generated automatically
20373      * when you define a tick interval.
20374      * @property xTicks
20375      * @type int[]
20376      */
20377     xTicks: null,
20378
20379     /**
20380      * Array of pixel locations the element will snap to if we specified a
20381      * vertical graduation/interval.  This array is generated automatically
20382      * when you define a tick interval.
20383      * @property yTicks
20384      * @type int[]
20385      */
20386     yTicks: null,
20387
20388     /**
20389      * By default the drag and drop instance will only respond to the primary
20390      * button click (left button for a right-handed mouse).  Set to true to
20391      * allow drag and drop to start with any mouse click that is propogated
20392      * by the browser
20393      * @property primaryButtonOnly
20394      * @type boolean
20395      */
20396     primaryButtonOnly: true,
20397
20398     /**
20399      * The availabe property is false until the linked dom element is accessible.
20400      * @property available
20401      * @type boolean
20402      */
20403     available: false,
20404
20405     /**
20406      * By default, drags can only be initiated if the mousedown occurs in the
20407      * region the linked element is.  This is done in part to work around a
20408      * bug in some browsers that mis-report the mousedown if the previous
20409      * mouseup happened outside of the window.  This property is set to true
20410      * if outer handles are defined.
20411      *
20412      * @property hasOuterHandles
20413      * @type boolean
20414      * @default false
20415      */
20416     hasOuterHandles: false,
20417
20418     /**
20419      * Code that executes immediately before the startDrag event
20420      * @method b4StartDrag
20421      * @private
20422      */
20423     b4StartDrag: function(x, y) { },
20424
20425     /**
20426      * Abstract method called after a drag/drop object is clicked
20427      * and the drag or mousedown time thresholds have beeen met.
20428      * @method startDrag
20429      * @param {int} X click location
20430      * @param {int} Y click location
20431      */
20432     startDrag: function(x, y) { /* override this */ },
20433
20434     /**
20435      * Code that executes immediately before the onDrag event
20436      * @method b4Drag
20437      * @private
20438      */
20439     b4Drag: function(e) { },
20440
20441     /**
20442      * Abstract method called during the onMouseMove event while dragging an
20443      * object.
20444      * @method onDrag
20445      * @param {Event} e the mousemove event
20446      */
20447     onDrag: function(e) { /* override this */ },
20448
20449     /**
20450      * Abstract method called when this element fist begins hovering over
20451      * another DragDrop obj
20452      * @method onDragEnter
20453      * @param {Event} e the mousemove event
20454      * @param {String|DragDrop[]} id In POINT mode, the element
20455      * id this is hovering over.  In INTERSECT mode, an array of one or more
20456      * dragdrop items being hovered over.
20457      */
20458     onDragEnter: function(e, id) { /* override this */ },
20459
20460     /**
20461      * Code that executes immediately before the onDragOver event
20462      * @method b4DragOver
20463      * @private
20464      */
20465     b4DragOver: function(e) { },
20466
20467     /**
20468      * Abstract method called when this element is hovering over another
20469      * DragDrop obj
20470      * @method onDragOver
20471      * @param {Event} e the mousemove event
20472      * @param {String|DragDrop[]} id In POINT mode, the element
20473      * id this is hovering over.  In INTERSECT mode, an array of dd items
20474      * being hovered over.
20475      */
20476     onDragOver: function(e, id) { /* override this */ },
20477
20478     /**
20479      * Code that executes immediately before the onDragOut event
20480      * @method b4DragOut
20481      * @private
20482      */
20483     b4DragOut: function(e) { },
20484
20485     /**
20486      * Abstract method called when we are no longer hovering over an element
20487      * @method onDragOut
20488      * @param {Event} e the mousemove event
20489      * @param {String|DragDrop[]} id In POINT mode, the element
20490      * id this was hovering over.  In INTERSECT mode, an array of dd items
20491      * that the mouse is no longer over.
20492      */
20493     onDragOut: function(e, id) { /* override this */ },
20494
20495     /**
20496      * Code that executes immediately before the onDragDrop event
20497      * @method b4DragDrop
20498      * @private
20499      */
20500     b4DragDrop: function(e) { },
20501
20502     /**
20503      * Abstract method called when this item is dropped on another DragDrop
20504      * obj
20505      * @method onDragDrop
20506      * @param {Event} e the mouseup event
20507      * @param {String|DragDrop[]} id In POINT mode, the element
20508      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20509      * was dropped on.
20510      */
20511     onDragDrop: function(e, id) { /* override this */ },
20512
20513     /**
20514      * Abstract method called when this item is dropped on an area with no
20515      * drop target
20516      * @method onInvalidDrop
20517      * @param {Event} e the mouseup event
20518      */
20519     onInvalidDrop: function(e) { /* override this */ },
20520
20521     /**
20522      * Code that executes immediately before the endDrag event
20523      * @method b4EndDrag
20524      * @private
20525      */
20526     b4EndDrag: function(e) { },
20527
20528     /**
20529      * Fired when we are done dragging the object
20530      * @method endDrag
20531      * @param {Event} e the mouseup event
20532      */
20533     endDrag: function(e) { /* override this */ },
20534
20535     /**
20536      * Code executed immediately before the onMouseDown event
20537      * @method b4MouseDown
20538      * @param {Event} e the mousedown event
20539      * @private
20540      */
20541     b4MouseDown: function(e) {  },
20542
20543     /**
20544      * Event handler that fires when a drag/drop obj gets a mousedown
20545      * @method onMouseDown
20546      * @param {Event} e the mousedown event
20547      */
20548     onMouseDown: function(e) { /* override this */ },
20549
20550     /**
20551      * Event handler that fires when a drag/drop obj gets a mouseup
20552      * @method onMouseUp
20553      * @param {Event} e the mouseup event
20554      */
20555     onMouseUp: function(e) { /* override this */ },
20556
20557     /**
20558      * Override the onAvailable method to do what is needed after the initial
20559      * position was determined.
20560      * @method onAvailable
20561      */
20562     onAvailable: function () {
20563     },
20564
20565     /*
20566      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20567      * @type Object
20568      */
20569     defaultPadding : {left:0, right:0, top:0, bottom:0},
20570
20571     /*
20572      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20573  *
20574  * Usage:
20575  <pre><code>
20576  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20577                 { dragElId: "existingProxyDiv" });
20578  dd.startDrag = function(){
20579      this.constrainTo("parent-id");
20580  };
20581  </code></pre>
20582  * Or you can initalize it using the {@link Roo.Element} object:
20583  <pre><code>
20584  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20585      startDrag : function(){
20586          this.constrainTo("parent-id");
20587      }
20588  });
20589  </code></pre>
20590      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20591      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20592      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20593      * an object containing the sides to pad. For example: {right:10, bottom:10}
20594      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20595      */
20596     constrainTo : function(constrainTo, pad, inContent){
20597         if(typeof pad == "number"){
20598             pad = {left: pad, right:pad, top:pad, bottom:pad};
20599         }
20600         pad = pad || this.defaultPadding;
20601         var b = Roo.get(this.getEl()).getBox();
20602         var ce = Roo.get(constrainTo);
20603         var s = ce.getScroll();
20604         var c, cd = ce.dom;
20605         if(cd == document.body){
20606             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20607         }else{
20608             xy = ce.getXY();
20609             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20610         }
20611
20612
20613         var topSpace = b.y - c.y;
20614         var leftSpace = b.x - c.x;
20615
20616         this.resetConstraints();
20617         this.setXConstraint(leftSpace - (pad.left||0), // left
20618                 c.width - leftSpace - b.width - (pad.right||0) //right
20619         );
20620         this.setYConstraint(topSpace - (pad.top||0), //top
20621                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20622         );
20623     },
20624
20625     /**
20626      * Returns a reference to the linked element
20627      * @method getEl
20628      * @return {HTMLElement} the html element
20629      */
20630     getEl: function() {
20631         if (!this._domRef) {
20632             this._domRef = Roo.getDom(this.id);
20633         }
20634
20635         return this._domRef;
20636     },
20637
20638     /**
20639      * Returns a reference to the actual element to drag.  By default this is
20640      * the same as the html element, but it can be assigned to another
20641      * element. An example of this can be found in Roo.dd.DDProxy
20642      * @method getDragEl
20643      * @return {HTMLElement} the html element
20644      */
20645     getDragEl: function() {
20646         return Roo.getDom(this.dragElId);
20647     },
20648
20649     /**
20650      * Sets up the DragDrop object.  Must be called in the constructor of any
20651      * Roo.dd.DragDrop subclass
20652      * @method init
20653      * @param id the id of the linked element
20654      * @param {String} sGroup the group of related items
20655      * @param {object} config configuration attributes
20656      */
20657     init: function(id, sGroup, config) {
20658         this.initTarget(id, sGroup, config);
20659         if (!Roo.isTouch) {
20660             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20661         }
20662         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20663         // Event.on(this.id, "selectstart", Event.preventDefault);
20664     },
20665
20666     /**
20667      * Initializes Targeting functionality only... the object does not
20668      * get a mousedown handler.
20669      * @method initTarget
20670      * @param id the id of the linked element
20671      * @param {String} sGroup the group of related items
20672      * @param {object} config configuration attributes
20673      */
20674     initTarget: function(id, sGroup, config) {
20675
20676         // configuration attributes
20677         this.config = config || {};
20678
20679         // create a local reference to the drag and drop manager
20680         this.DDM = Roo.dd.DDM;
20681         // initialize the groups array
20682         this.groups = {};
20683
20684         // assume that we have an element reference instead of an id if the
20685         // parameter is not a string
20686         if (typeof id !== "string") {
20687             id = Roo.id(id);
20688         }
20689
20690         // set the id
20691         this.id = id;
20692
20693         // add to an interaction group
20694         this.addToGroup((sGroup) ? sGroup : "default");
20695
20696         // We don't want to register this as the handle with the manager
20697         // so we just set the id rather than calling the setter.
20698         this.handleElId = id;
20699
20700         // the linked element is the element that gets dragged by default
20701         this.setDragElId(id);
20702
20703         // by default, clicked anchors will not start drag operations.
20704         this.invalidHandleTypes = { A: "A" };
20705         this.invalidHandleIds = {};
20706         this.invalidHandleClasses = [];
20707
20708         this.applyConfig();
20709
20710         this.handleOnAvailable();
20711     },
20712
20713     /**
20714      * Applies the configuration parameters that were passed into the constructor.
20715      * This is supposed to happen at each level through the inheritance chain.  So
20716      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20717      * DragDrop in order to get all of the parameters that are available in
20718      * each object.
20719      * @method applyConfig
20720      */
20721     applyConfig: function() {
20722
20723         // configurable properties:
20724         //    padding, isTarget, maintainOffset, primaryButtonOnly
20725         this.padding           = this.config.padding || [0, 0, 0, 0];
20726         this.isTarget          = (this.config.isTarget !== false);
20727         this.maintainOffset    = (this.config.maintainOffset);
20728         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20729
20730     },
20731
20732     /**
20733      * Executed when the linked element is available
20734      * @method handleOnAvailable
20735      * @private
20736      */
20737     handleOnAvailable: function() {
20738         this.available = true;
20739         this.resetConstraints();
20740         this.onAvailable();
20741     },
20742
20743      /**
20744      * Configures the padding for the target zone in px.  Effectively expands
20745      * (or reduces) the virtual object size for targeting calculations.
20746      * Supports css-style shorthand; if only one parameter is passed, all sides
20747      * will have that padding, and if only two are passed, the top and bottom
20748      * will have the first param, the left and right the second.
20749      * @method setPadding
20750      * @param {int} iTop    Top pad
20751      * @param {int} iRight  Right pad
20752      * @param {int} iBot    Bot pad
20753      * @param {int} iLeft   Left pad
20754      */
20755     setPadding: function(iTop, iRight, iBot, iLeft) {
20756         // this.padding = [iLeft, iRight, iTop, iBot];
20757         if (!iRight && 0 !== iRight) {
20758             this.padding = [iTop, iTop, iTop, iTop];
20759         } else if (!iBot && 0 !== iBot) {
20760             this.padding = [iTop, iRight, iTop, iRight];
20761         } else {
20762             this.padding = [iTop, iRight, iBot, iLeft];
20763         }
20764     },
20765
20766     /**
20767      * Stores the initial placement of the linked element.
20768      * @method setInitialPosition
20769      * @param {int} diffX   the X offset, default 0
20770      * @param {int} diffY   the Y offset, default 0
20771      */
20772     setInitPosition: function(diffX, diffY) {
20773         var el = this.getEl();
20774
20775         if (!this.DDM.verifyEl(el)) {
20776             return;
20777         }
20778
20779         var dx = diffX || 0;
20780         var dy = diffY || 0;
20781
20782         var p = Dom.getXY( el );
20783
20784         this.initPageX = p[0] - dx;
20785         this.initPageY = p[1] - dy;
20786
20787         this.lastPageX = p[0];
20788         this.lastPageY = p[1];
20789
20790
20791         this.setStartPosition(p);
20792     },
20793
20794     /**
20795      * Sets the start position of the element.  This is set when the obj
20796      * is initialized, the reset when a drag is started.
20797      * @method setStartPosition
20798      * @param pos current position (from previous lookup)
20799      * @private
20800      */
20801     setStartPosition: function(pos) {
20802         var p = pos || Dom.getXY( this.getEl() );
20803         this.deltaSetXY = null;
20804
20805         this.startPageX = p[0];
20806         this.startPageY = p[1];
20807     },
20808
20809     /**
20810      * Add this instance to a group of related drag/drop objects.  All
20811      * instances belong to at least one group, and can belong to as many
20812      * groups as needed.
20813      * @method addToGroup
20814      * @param sGroup {string} the name of the group
20815      */
20816     addToGroup: function(sGroup) {
20817         this.groups[sGroup] = true;
20818         this.DDM.regDragDrop(this, sGroup);
20819     },
20820
20821     /**
20822      * Remove's this instance from the supplied interaction group
20823      * @method removeFromGroup
20824      * @param {string}  sGroup  The group to drop
20825      */
20826     removeFromGroup: function(sGroup) {
20827         if (this.groups[sGroup]) {
20828             delete this.groups[sGroup];
20829         }
20830
20831         this.DDM.removeDDFromGroup(this, sGroup);
20832     },
20833
20834     /**
20835      * Allows you to specify that an element other than the linked element
20836      * will be moved with the cursor during a drag
20837      * @method setDragElId
20838      * @param id {string} the id of the element that will be used to initiate the drag
20839      */
20840     setDragElId: function(id) {
20841         this.dragElId = id;
20842     },
20843
20844     /**
20845      * Allows you to specify a child of the linked element that should be
20846      * used to initiate the drag operation.  An example of this would be if
20847      * you have a content div with text and links.  Clicking anywhere in the
20848      * content area would normally start the drag operation.  Use this method
20849      * to specify that an element inside of the content div is the element
20850      * that starts the drag operation.
20851      * @method setHandleElId
20852      * @param id {string} the id of the element that will be used to
20853      * initiate the drag.
20854      */
20855     setHandleElId: function(id) {
20856         if (typeof id !== "string") {
20857             id = Roo.id(id);
20858         }
20859         this.handleElId = id;
20860         this.DDM.regHandle(this.id, id);
20861     },
20862
20863     /**
20864      * Allows you to set an element outside of the linked element as a drag
20865      * handle
20866      * @method setOuterHandleElId
20867      * @param id the id of the element that will be used to initiate the drag
20868      */
20869     setOuterHandleElId: function(id) {
20870         if (typeof id !== "string") {
20871             id = Roo.id(id);
20872         }
20873         Event.on(id, "mousedown",
20874                 this.handleMouseDown, this);
20875         this.setHandleElId(id);
20876
20877         this.hasOuterHandles = true;
20878     },
20879
20880     /**
20881      * Remove all drag and drop hooks for this element
20882      * @method unreg
20883      */
20884     unreg: function() {
20885         Event.un(this.id, "mousedown",
20886                 this.handleMouseDown);
20887         Event.un(this.id, "touchstart",
20888                 this.handleMouseDown);
20889         this._domRef = null;
20890         this.DDM._remove(this);
20891     },
20892
20893     destroy : function(){
20894         this.unreg();
20895     },
20896
20897     /**
20898      * Returns true if this instance is locked, or the drag drop mgr is locked
20899      * (meaning that all drag/drop is disabled on the page.)
20900      * @method isLocked
20901      * @return {boolean} true if this obj or all drag/drop is locked, else
20902      * false
20903      */
20904     isLocked: function() {
20905         return (this.DDM.isLocked() || this.locked);
20906     },
20907
20908     /**
20909      * Fired when this object is clicked
20910      * @method handleMouseDown
20911      * @param {Event} e
20912      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20913      * @private
20914      */
20915     handleMouseDown: function(e, oDD){
20916      
20917         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20918             //Roo.log('not touch/ button !=0');
20919             return;
20920         }
20921         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20922             return; // double touch..
20923         }
20924         
20925
20926         if (this.isLocked()) {
20927             //Roo.log('locked');
20928             return;
20929         }
20930
20931         this.DDM.refreshCache(this.groups);
20932 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20933         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20934         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20935             //Roo.log('no outer handes or not over target');
20936                 // do nothing.
20937         } else {
20938 //            Roo.log('check validator');
20939             if (this.clickValidator(e)) {
20940 //                Roo.log('validate success');
20941                 // set the initial element position
20942                 this.setStartPosition();
20943
20944
20945                 this.b4MouseDown(e);
20946                 this.onMouseDown(e);
20947
20948                 this.DDM.handleMouseDown(e, this);
20949
20950                 this.DDM.stopEvent(e);
20951             } else {
20952
20953
20954             }
20955         }
20956     },
20957
20958     clickValidator: function(e) {
20959         var target = e.getTarget();
20960         return ( this.isValidHandleChild(target) &&
20961                     (this.id == this.handleElId ||
20962                         this.DDM.handleWasClicked(target, this.id)) );
20963     },
20964
20965     /**
20966      * Allows you to specify a tag name that should not start a drag operation
20967      * when clicked.  This is designed to facilitate embedding links within a
20968      * drag handle that do something other than start the drag.
20969      * @method addInvalidHandleType
20970      * @param {string} tagName the type of element to exclude
20971      */
20972     addInvalidHandleType: function(tagName) {
20973         var type = tagName.toUpperCase();
20974         this.invalidHandleTypes[type] = type;
20975     },
20976
20977     /**
20978      * Lets you to specify an element id for a child of a drag handle
20979      * that should not initiate a drag
20980      * @method addInvalidHandleId
20981      * @param {string} id the element id of the element you wish to ignore
20982      */
20983     addInvalidHandleId: function(id) {
20984         if (typeof id !== "string") {
20985             id = Roo.id(id);
20986         }
20987         this.invalidHandleIds[id] = id;
20988     },
20989
20990     /**
20991      * Lets you specify a css class of elements that will not initiate a drag
20992      * @method addInvalidHandleClass
20993      * @param {string} cssClass the class of the elements you wish to ignore
20994      */
20995     addInvalidHandleClass: function(cssClass) {
20996         this.invalidHandleClasses.push(cssClass);
20997     },
20998
20999     /**
21000      * Unsets an excluded tag name set by addInvalidHandleType
21001      * @method removeInvalidHandleType
21002      * @param {string} tagName the type of element to unexclude
21003      */
21004     removeInvalidHandleType: function(tagName) {
21005         var type = tagName.toUpperCase();
21006         // this.invalidHandleTypes[type] = null;
21007         delete this.invalidHandleTypes[type];
21008     },
21009
21010     /**
21011      * Unsets an invalid handle id
21012      * @method removeInvalidHandleId
21013      * @param {string} id the id of the element to re-enable
21014      */
21015     removeInvalidHandleId: function(id) {
21016         if (typeof id !== "string") {
21017             id = Roo.id(id);
21018         }
21019         delete this.invalidHandleIds[id];
21020     },
21021
21022     /**
21023      * Unsets an invalid css class
21024      * @method removeInvalidHandleClass
21025      * @param {string} cssClass the class of the element(s) you wish to
21026      * re-enable
21027      */
21028     removeInvalidHandleClass: function(cssClass) {
21029         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
21030             if (this.invalidHandleClasses[i] == cssClass) {
21031                 delete this.invalidHandleClasses[i];
21032             }
21033         }
21034     },
21035
21036     /**
21037      * Checks the tag exclusion list to see if this click should be ignored
21038      * @method isValidHandleChild
21039      * @param {HTMLElement} node the HTMLElement to evaluate
21040      * @return {boolean} true if this is a valid tag type, false if not
21041      */
21042     isValidHandleChild: function(node) {
21043
21044         var valid = true;
21045         // var n = (node.nodeName == "#text") ? node.parentNode : node;
21046         var nodeName;
21047         try {
21048             nodeName = node.nodeName.toUpperCase();
21049         } catch(e) {
21050             nodeName = node.nodeName;
21051         }
21052         valid = valid && !this.invalidHandleTypes[nodeName];
21053         valid = valid && !this.invalidHandleIds[node.id];
21054
21055         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
21056             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
21057         }
21058
21059
21060         return valid;
21061
21062     },
21063
21064     /**
21065      * Create the array of horizontal tick marks if an interval was specified
21066      * in setXConstraint().
21067      * @method setXTicks
21068      * @private
21069      */
21070     setXTicks: function(iStartX, iTickSize) {
21071         this.xTicks = [];
21072         this.xTickSize = iTickSize;
21073
21074         var tickMap = {};
21075
21076         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
21077             if (!tickMap[i]) {
21078                 this.xTicks[this.xTicks.length] = i;
21079                 tickMap[i] = true;
21080             }
21081         }
21082
21083         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
21084             if (!tickMap[i]) {
21085                 this.xTicks[this.xTicks.length] = i;
21086                 tickMap[i] = true;
21087             }
21088         }
21089
21090         this.xTicks.sort(this.DDM.numericSort) ;
21091     },
21092
21093     /**
21094      * Create the array of vertical tick marks if an interval was specified in
21095      * setYConstraint().
21096      * @method setYTicks
21097      * @private
21098      */
21099     setYTicks: function(iStartY, iTickSize) {
21100         this.yTicks = [];
21101         this.yTickSize = iTickSize;
21102
21103         var tickMap = {};
21104
21105         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
21106             if (!tickMap[i]) {
21107                 this.yTicks[this.yTicks.length] = i;
21108                 tickMap[i] = true;
21109             }
21110         }
21111
21112         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
21113             if (!tickMap[i]) {
21114                 this.yTicks[this.yTicks.length] = i;
21115                 tickMap[i] = true;
21116             }
21117         }
21118
21119         this.yTicks.sort(this.DDM.numericSort) ;
21120     },
21121
21122     /**
21123      * By default, the element can be dragged any place on the screen.  Use
21124      * this method to limit the horizontal travel of the element.  Pass in
21125      * 0,0 for the parameters if you want to lock the drag to the y axis.
21126      * @method setXConstraint
21127      * @param {int} iLeft the number of pixels the element can move to the left
21128      * @param {int} iRight the number of pixels the element can move to the
21129      * right
21130      * @param {int} iTickSize optional parameter for specifying that the
21131      * element
21132      * should move iTickSize pixels at a time.
21133      */
21134     setXConstraint: function(iLeft, iRight, iTickSize) {
21135         this.leftConstraint = iLeft;
21136         this.rightConstraint = iRight;
21137
21138         this.minX = this.initPageX - iLeft;
21139         this.maxX = this.initPageX + iRight;
21140         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
21141
21142         this.constrainX = true;
21143     },
21144
21145     /**
21146      * Clears any constraints applied to this instance.  Also clears ticks
21147      * since they can't exist independent of a constraint at this time.
21148      * @method clearConstraints
21149      */
21150     clearConstraints: function() {
21151         this.constrainX = false;
21152         this.constrainY = false;
21153         this.clearTicks();
21154     },
21155
21156     /**
21157      * Clears any tick interval defined for this instance
21158      * @method clearTicks
21159      */
21160     clearTicks: function() {
21161         this.xTicks = null;
21162         this.yTicks = null;
21163         this.xTickSize = 0;
21164         this.yTickSize = 0;
21165     },
21166
21167     /**
21168      * By default, the element can be dragged any place on the screen.  Set
21169      * this to limit the vertical travel of the element.  Pass in 0,0 for the
21170      * parameters if you want to lock the drag to the x axis.
21171      * @method setYConstraint
21172      * @param {int} iUp the number of pixels the element can move up
21173      * @param {int} iDown the number of pixels the element can move down
21174      * @param {int} iTickSize optional parameter for specifying that the
21175      * element should move iTickSize pixels at a time.
21176      */
21177     setYConstraint: function(iUp, iDown, iTickSize) {
21178         this.topConstraint = iUp;
21179         this.bottomConstraint = iDown;
21180
21181         this.minY = this.initPageY - iUp;
21182         this.maxY = this.initPageY + iDown;
21183         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21184
21185         this.constrainY = true;
21186
21187     },
21188
21189     /**
21190      * resetConstraints must be called if you manually reposition a dd element.
21191      * @method resetConstraints
21192      * @param {boolean} maintainOffset
21193      */
21194     resetConstraints: function() {
21195
21196
21197         // Maintain offsets if necessary
21198         if (this.initPageX || this.initPageX === 0) {
21199             // figure out how much this thing has moved
21200             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21201             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21202
21203             this.setInitPosition(dx, dy);
21204
21205         // This is the first time we have detected the element's position
21206         } else {
21207             this.setInitPosition();
21208         }
21209
21210         if (this.constrainX) {
21211             this.setXConstraint( this.leftConstraint,
21212                                  this.rightConstraint,
21213                                  this.xTickSize        );
21214         }
21215
21216         if (this.constrainY) {
21217             this.setYConstraint( this.topConstraint,
21218                                  this.bottomConstraint,
21219                                  this.yTickSize         );
21220         }
21221     },
21222
21223     /**
21224      * Normally the drag element is moved pixel by pixel, but we can specify
21225      * that it move a number of pixels at a time.  This method resolves the
21226      * location when we have it set up like this.
21227      * @method getTick
21228      * @param {int} val where we want to place the object
21229      * @param {int[]} tickArray sorted array of valid points
21230      * @return {int} the closest tick
21231      * @private
21232      */
21233     getTick: function(val, tickArray) {
21234
21235         if (!tickArray) {
21236             // If tick interval is not defined, it is effectively 1 pixel,
21237             // so we return the value passed to us.
21238             return val;
21239         } else if (tickArray[0] >= val) {
21240             // The value is lower than the first tick, so we return the first
21241             // tick.
21242             return tickArray[0];
21243         } else {
21244             for (var i=0, len=tickArray.length; i<len; ++i) {
21245                 var next = i + 1;
21246                 if (tickArray[next] && tickArray[next] >= val) {
21247                     var diff1 = val - tickArray[i];
21248                     var diff2 = tickArray[next] - val;
21249                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21250                 }
21251             }
21252
21253             // The value is larger than the last tick, so we return the last
21254             // tick.
21255             return tickArray[tickArray.length - 1];
21256         }
21257     },
21258
21259     /**
21260      * toString method
21261      * @method toString
21262      * @return {string} string representation of the dd obj
21263      */
21264     toString: function() {
21265         return ("DragDrop " + this.id);
21266     }
21267
21268 });
21269
21270 })();
21271 /*
21272  * Based on:
21273  * Ext JS Library 1.1.1
21274  * Copyright(c) 2006-2007, Ext JS, LLC.
21275  *
21276  * Originally Released Under LGPL - original licence link has changed is not relivant.
21277  *
21278  * Fork - LGPL
21279  * <script type="text/javascript">
21280  */
21281
21282
21283 /**
21284  * The drag and drop utility provides a framework for building drag and drop
21285  * applications.  In addition to enabling drag and drop for specific elements,
21286  * the drag and drop elements are tracked by the manager class, and the
21287  * interactions between the various elements are tracked during the drag and
21288  * the implementing code is notified about these important moments.
21289  */
21290
21291 // Only load the library once.  Rewriting the manager class would orphan
21292 // existing drag and drop instances.
21293 if (!Roo.dd.DragDropMgr) {
21294
21295 /**
21296  * @class Roo.dd.DragDropMgr
21297  * DragDropMgr is a singleton that tracks the element interaction for
21298  * all DragDrop items in the window.  Generally, you will not call
21299  * this class directly, but it does have helper methods that could
21300  * be useful in your DragDrop implementations.
21301  * @static
21302  */
21303 Roo.dd.DragDropMgr = function() {
21304
21305     var Event = Roo.EventManager;
21306
21307     return {
21308
21309         /**
21310          * Two dimensional Array of registered DragDrop objects.  The first
21311          * dimension is the DragDrop item group, the second the DragDrop
21312          * object.
21313          * @property ids
21314          * @type {string: string}
21315          * @private
21316          * @static
21317          */
21318         ids: {},
21319
21320         /**
21321          * Array of element ids defined as drag handles.  Used to determine
21322          * if the element that generated the mousedown event is actually the
21323          * handle and not the html element itself.
21324          * @property handleIds
21325          * @type {string: string}
21326          * @private
21327          * @static
21328          */
21329         handleIds: {},
21330
21331         /**
21332          * the DragDrop object that is currently being dragged
21333          * @property dragCurrent
21334          * @type DragDrop
21335          * @private
21336          * @static
21337          **/
21338         dragCurrent: null,
21339
21340         /**
21341          * the DragDrop object(s) that are being hovered over
21342          * @property dragOvers
21343          * @type Array
21344          * @private
21345          * @static
21346          */
21347         dragOvers: {},
21348
21349         /**
21350          * the X distance between the cursor and the object being dragged
21351          * @property deltaX
21352          * @type int
21353          * @private
21354          * @static
21355          */
21356         deltaX: 0,
21357
21358         /**
21359          * the Y distance between the cursor and the object being dragged
21360          * @property deltaY
21361          * @type int
21362          * @private
21363          * @static
21364          */
21365         deltaY: 0,
21366
21367         /**
21368          * Flag to determine if we should prevent the default behavior of the
21369          * events we define. By default this is true, but this can be set to
21370          * false if you need the default behavior (not recommended)
21371          * @property preventDefault
21372          * @type boolean
21373          * @static
21374          */
21375         preventDefault: true,
21376
21377         /**
21378          * Flag to determine if we should stop the propagation of the events
21379          * we generate. This is true by default but you may want to set it to
21380          * false if the html element contains other features that require the
21381          * mouse click.
21382          * @property stopPropagation
21383          * @type boolean
21384          * @static
21385          */
21386         stopPropagation: true,
21387
21388         /**
21389          * Internal flag that is set to true when drag and drop has been
21390          * intialized
21391          * @property initialized
21392          * @private
21393          * @static
21394          */
21395         initalized: false,
21396
21397         /**
21398          * All drag and drop can be disabled.
21399          * @property locked
21400          * @private
21401          * @static
21402          */
21403         locked: false,
21404
21405         /**
21406          * Called the first time an element is registered.
21407          * @method init
21408          * @private
21409          * @static
21410          */
21411         init: function() {
21412             this.initialized = true;
21413         },
21414
21415         /**
21416          * In point mode, drag and drop interaction is defined by the
21417          * location of the cursor during the drag/drop
21418          * @property POINT
21419          * @type int
21420          * @static
21421          */
21422         POINT: 0,
21423
21424         /**
21425          * In intersect mode, drag and drop interactio nis defined by the
21426          * overlap of two or more drag and drop objects.
21427          * @property INTERSECT
21428          * @type int
21429          * @static
21430          */
21431         INTERSECT: 1,
21432
21433         /**
21434          * The current drag and drop mode.  Default: POINT
21435          * @property mode
21436          * @type int
21437          * @static
21438          */
21439         mode: 0,
21440
21441         /**
21442          * Runs method on all drag and drop objects
21443          * @method _execOnAll
21444          * @private
21445          * @static
21446          */
21447         _execOnAll: function(sMethod, args) {
21448             for (var i in this.ids) {
21449                 for (var j in this.ids[i]) {
21450                     var oDD = this.ids[i][j];
21451                     if (! this.isTypeOfDD(oDD)) {
21452                         continue;
21453                     }
21454                     oDD[sMethod].apply(oDD, args);
21455                 }
21456             }
21457         },
21458
21459         /**
21460          * Drag and drop initialization.  Sets up the global event handlers
21461          * @method _onLoad
21462          * @private
21463          * @static
21464          */
21465         _onLoad: function() {
21466
21467             this.init();
21468
21469             if (!Roo.isTouch) {
21470                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21471                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21472             }
21473             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21474             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21475             
21476             Event.on(window,   "unload",    this._onUnload, this, true);
21477             Event.on(window,   "resize",    this._onResize, this, true);
21478             // Event.on(window,   "mouseout",    this._test);
21479
21480         },
21481
21482         /**
21483          * Reset constraints on all drag and drop objs
21484          * @method _onResize
21485          * @private
21486          * @static
21487          */
21488         _onResize: function(e) {
21489             this._execOnAll("resetConstraints", []);
21490         },
21491
21492         /**
21493          * Lock all drag and drop functionality
21494          * @method lock
21495          * @static
21496          */
21497         lock: function() { this.locked = true; },
21498
21499         /**
21500          * Unlock all drag and drop functionality
21501          * @method unlock
21502          * @static
21503          */
21504         unlock: function() { this.locked = false; },
21505
21506         /**
21507          * Is drag and drop locked?
21508          * @method isLocked
21509          * @return {boolean} True if drag and drop is locked, false otherwise.
21510          * @static
21511          */
21512         isLocked: function() { return this.locked; },
21513
21514         /**
21515          * Location cache that is set for all drag drop objects when a drag is
21516          * initiated, cleared when the drag is finished.
21517          * @property locationCache
21518          * @private
21519          * @static
21520          */
21521         locationCache: {},
21522
21523         /**
21524          * Set useCache to false if you want to force object the lookup of each
21525          * drag and drop linked element constantly during a drag.
21526          * @property useCache
21527          * @type boolean
21528          * @static
21529          */
21530         useCache: true,
21531
21532         /**
21533          * The number of pixels that the mouse needs to move after the
21534          * mousedown before the drag is initiated.  Default=3;
21535          * @property clickPixelThresh
21536          * @type int
21537          * @static
21538          */
21539         clickPixelThresh: 3,
21540
21541         /**
21542          * The number of milliseconds after the mousedown event to initiate the
21543          * drag if we don't get a mouseup event. Default=1000
21544          * @property clickTimeThresh
21545          * @type int
21546          * @static
21547          */
21548         clickTimeThresh: 350,
21549
21550         /**
21551          * Flag that indicates that either the drag pixel threshold or the
21552          * mousdown time threshold has been met
21553          * @property dragThreshMet
21554          * @type boolean
21555          * @private
21556          * @static
21557          */
21558         dragThreshMet: false,
21559
21560         /**
21561          * Timeout used for the click time threshold
21562          * @property clickTimeout
21563          * @type Object
21564          * @private
21565          * @static
21566          */
21567         clickTimeout: null,
21568
21569         /**
21570          * The X position of the mousedown event stored for later use when a
21571          * drag threshold is met.
21572          * @property startX
21573          * @type int
21574          * @private
21575          * @static
21576          */
21577         startX: 0,
21578
21579         /**
21580          * The Y position of the mousedown event stored for later use when a
21581          * drag threshold is met.
21582          * @property startY
21583          * @type int
21584          * @private
21585          * @static
21586          */
21587         startY: 0,
21588
21589         /**
21590          * Each DragDrop instance must be registered with the DragDropMgr.
21591          * This is executed in DragDrop.init()
21592          * @method regDragDrop
21593          * @param {DragDrop} oDD the DragDrop object to register
21594          * @param {String} sGroup the name of the group this element belongs to
21595          * @static
21596          */
21597         regDragDrop: function(oDD, sGroup) {
21598             if (!this.initialized) { this.init(); }
21599
21600             if (!this.ids[sGroup]) {
21601                 this.ids[sGroup] = {};
21602             }
21603             this.ids[sGroup][oDD.id] = oDD;
21604         },
21605
21606         /**
21607          * Removes the supplied dd instance from the supplied group. Executed
21608          * by DragDrop.removeFromGroup, so don't call this function directly.
21609          * @method removeDDFromGroup
21610          * @private
21611          * @static
21612          */
21613         removeDDFromGroup: function(oDD, sGroup) {
21614             if (!this.ids[sGroup]) {
21615                 this.ids[sGroup] = {};
21616             }
21617
21618             var obj = this.ids[sGroup];
21619             if (obj && obj[oDD.id]) {
21620                 delete obj[oDD.id];
21621             }
21622         },
21623
21624         /**
21625          * Unregisters a drag and drop item.  This is executed in
21626          * DragDrop.unreg, use that method instead of calling this directly.
21627          * @method _remove
21628          * @private
21629          * @static
21630          */
21631         _remove: function(oDD) {
21632             for (var g in oDD.groups) {
21633                 if (g && this.ids[g][oDD.id]) {
21634                     delete this.ids[g][oDD.id];
21635                 }
21636             }
21637             delete this.handleIds[oDD.id];
21638         },
21639
21640         /**
21641          * Each DragDrop handle element must be registered.  This is done
21642          * automatically when executing DragDrop.setHandleElId()
21643          * @method regHandle
21644          * @param {String} sDDId the DragDrop id this element is a handle for
21645          * @param {String} sHandleId the id of the element that is the drag
21646          * handle
21647          * @static
21648          */
21649         regHandle: function(sDDId, sHandleId) {
21650             if (!this.handleIds[sDDId]) {
21651                 this.handleIds[sDDId] = {};
21652             }
21653             this.handleIds[sDDId][sHandleId] = sHandleId;
21654         },
21655
21656         /**
21657          * Utility function to determine if a given element has been
21658          * registered as a drag drop item.
21659          * @method isDragDrop
21660          * @param {String} id the element id to check
21661          * @return {boolean} true if this element is a DragDrop item,
21662          * false otherwise
21663          * @static
21664          */
21665         isDragDrop: function(id) {
21666             return ( this.getDDById(id) ) ? true : false;
21667         },
21668
21669         /**
21670          * Returns the drag and drop instances that are in all groups the
21671          * passed in instance belongs to.
21672          * @method getRelated
21673          * @param {DragDrop} p_oDD the obj to get related data for
21674          * @param {boolean} bTargetsOnly if true, only return targetable objs
21675          * @return {DragDrop[]} the related instances
21676          * @static
21677          */
21678         getRelated: function(p_oDD, bTargetsOnly) {
21679             var oDDs = [];
21680             for (var i in p_oDD.groups) {
21681                 for (j in this.ids[i]) {
21682                     var dd = this.ids[i][j];
21683                     if (! this.isTypeOfDD(dd)) {
21684                         continue;
21685                     }
21686                     if (!bTargetsOnly || dd.isTarget) {
21687                         oDDs[oDDs.length] = dd;
21688                     }
21689                 }
21690             }
21691
21692             return oDDs;
21693         },
21694
21695         /**
21696          * Returns true if the specified dd target is a legal target for
21697          * the specifice drag obj
21698          * @method isLegalTarget
21699          * @param {DragDrop} the drag obj
21700          * @param {DragDrop} the target
21701          * @return {boolean} true if the target is a legal target for the
21702          * dd obj
21703          * @static
21704          */
21705         isLegalTarget: function (oDD, oTargetDD) {
21706             var targets = this.getRelated(oDD, true);
21707             for (var i=0, len=targets.length;i<len;++i) {
21708                 if (targets[i].id == oTargetDD.id) {
21709                     return true;
21710                 }
21711             }
21712
21713             return false;
21714         },
21715
21716         /**
21717          * My goal is to be able to transparently determine if an object is
21718          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21719          * returns "object", oDD.constructor.toString() always returns
21720          * "DragDrop" and not the name of the subclass.  So for now it just
21721          * evaluates a well-known variable in DragDrop.
21722          * @method isTypeOfDD
21723          * @param {Object} the object to evaluate
21724          * @return {boolean} true if typeof oDD = DragDrop
21725          * @static
21726          */
21727         isTypeOfDD: function (oDD) {
21728             return (oDD && oDD.__ygDragDrop);
21729         },
21730
21731         /**
21732          * Utility function to determine if a given element has been
21733          * registered as a drag drop handle for the given Drag Drop object.
21734          * @method isHandle
21735          * @param {String} id the element id to check
21736          * @return {boolean} true if this element is a DragDrop handle, false
21737          * otherwise
21738          * @static
21739          */
21740         isHandle: function(sDDId, sHandleId) {
21741             return ( this.handleIds[sDDId] &&
21742                             this.handleIds[sDDId][sHandleId] );
21743         },
21744
21745         /**
21746          * Returns the DragDrop instance for a given id
21747          * @method getDDById
21748          * @param {String} id the id of the DragDrop object
21749          * @return {DragDrop} the drag drop object, null if it is not found
21750          * @static
21751          */
21752         getDDById: function(id) {
21753             for (var i in this.ids) {
21754                 if (this.ids[i][id]) {
21755                     return this.ids[i][id];
21756                 }
21757             }
21758             return null;
21759         },
21760
21761         /**
21762          * Fired after a registered DragDrop object gets the mousedown event.
21763          * Sets up the events required to track the object being dragged
21764          * @method handleMouseDown
21765          * @param {Event} e the event
21766          * @param oDD the DragDrop object being dragged
21767          * @private
21768          * @static
21769          */
21770         handleMouseDown: function(e, oDD) {
21771             if(Roo.QuickTips){
21772                 Roo.QuickTips.disable();
21773             }
21774             this.currentTarget = e.getTarget();
21775
21776             this.dragCurrent = oDD;
21777
21778             var el = oDD.getEl();
21779
21780             // track start position
21781             this.startX = e.getPageX();
21782             this.startY = e.getPageY();
21783
21784             this.deltaX = this.startX - el.offsetLeft;
21785             this.deltaY = this.startY - el.offsetTop;
21786
21787             this.dragThreshMet = false;
21788
21789             this.clickTimeout = setTimeout(
21790                     function() {
21791                         var DDM = Roo.dd.DDM;
21792                         DDM.startDrag(DDM.startX, DDM.startY);
21793                     },
21794                     this.clickTimeThresh );
21795         },
21796
21797         /**
21798          * Fired when either the drag pixel threshol or the mousedown hold
21799          * time threshold has been met.
21800          * @method startDrag
21801          * @param x {int} the X position of the original mousedown
21802          * @param y {int} the Y position of the original mousedown
21803          * @static
21804          */
21805         startDrag: function(x, y) {
21806             clearTimeout(this.clickTimeout);
21807             if (this.dragCurrent) {
21808                 this.dragCurrent.b4StartDrag(x, y);
21809                 this.dragCurrent.startDrag(x, y);
21810             }
21811             this.dragThreshMet = true;
21812         },
21813
21814         /**
21815          * Internal function to handle the mouseup event.  Will be invoked
21816          * from the context of the document.
21817          * @method handleMouseUp
21818          * @param {Event} e the event
21819          * @private
21820          * @static
21821          */
21822         handleMouseUp: function(e) {
21823
21824             if(Roo.QuickTips){
21825                 Roo.QuickTips.enable();
21826             }
21827             if (! this.dragCurrent) {
21828                 return;
21829             }
21830
21831             clearTimeout(this.clickTimeout);
21832
21833             if (this.dragThreshMet) {
21834                 this.fireEvents(e, true);
21835             } else {
21836             }
21837
21838             this.stopDrag(e);
21839
21840             this.stopEvent(e);
21841         },
21842
21843         /**
21844          * Utility to stop event propagation and event default, if these
21845          * features are turned on.
21846          * @method stopEvent
21847          * @param {Event} e the event as returned by this.getEvent()
21848          * @static
21849          */
21850         stopEvent: function(e){
21851             if(this.stopPropagation) {
21852                 e.stopPropagation();
21853             }
21854
21855             if (this.preventDefault) {
21856                 e.preventDefault();
21857             }
21858         },
21859
21860         /**
21861          * Internal function to clean up event handlers after the drag
21862          * operation is complete
21863          * @method stopDrag
21864          * @param {Event} e the event
21865          * @private
21866          * @static
21867          */
21868         stopDrag: function(e) {
21869             // Fire the drag end event for the item that was dragged
21870             if (this.dragCurrent) {
21871                 if (this.dragThreshMet) {
21872                     this.dragCurrent.b4EndDrag(e);
21873                     this.dragCurrent.endDrag(e);
21874                 }
21875
21876                 this.dragCurrent.onMouseUp(e);
21877             }
21878
21879             this.dragCurrent = null;
21880             this.dragOvers = {};
21881         },
21882
21883         /**
21884          * Internal function to handle the mousemove event.  Will be invoked
21885          * from the context of the html element.
21886          *
21887          * @TODO figure out what we can do about mouse events lost when the
21888          * user drags objects beyond the window boundary.  Currently we can
21889          * detect this in internet explorer by verifying that the mouse is
21890          * down during the mousemove event.  Firefox doesn't give us the
21891          * button state on the mousemove event.
21892          * @method handleMouseMove
21893          * @param {Event} e the event
21894          * @private
21895          * @static
21896          */
21897         handleMouseMove: function(e) {
21898             if (! this.dragCurrent) {
21899                 return true;
21900             }
21901
21902             // var button = e.which || e.button;
21903
21904             // check for IE mouseup outside of page boundary
21905             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21906                 this.stopEvent(e);
21907                 return this.handleMouseUp(e);
21908             }
21909
21910             if (!this.dragThreshMet) {
21911                 var diffX = Math.abs(this.startX - e.getPageX());
21912                 var diffY = Math.abs(this.startY - e.getPageY());
21913                 if (diffX > this.clickPixelThresh ||
21914                             diffY > this.clickPixelThresh) {
21915                     this.startDrag(this.startX, this.startY);
21916                 }
21917             }
21918
21919             if (this.dragThreshMet) {
21920                 this.dragCurrent.b4Drag(e);
21921                 this.dragCurrent.onDrag(e);
21922                 if(!this.dragCurrent.moveOnly){
21923                     this.fireEvents(e, false);
21924                 }
21925             }
21926
21927             this.stopEvent(e);
21928
21929             return true;
21930         },
21931
21932         /**
21933          * Iterates over all of the DragDrop elements to find ones we are
21934          * hovering over or dropping on
21935          * @method fireEvents
21936          * @param {Event} e the event
21937          * @param {boolean} isDrop is this a drop op or a mouseover op?
21938          * @private
21939          * @static
21940          */
21941         fireEvents: function(e, isDrop) {
21942             var dc = this.dragCurrent;
21943
21944             // If the user did the mouse up outside of the window, we could
21945             // get here even though we have ended the drag.
21946             if (!dc || dc.isLocked()) {
21947                 return;
21948             }
21949
21950             var pt = e.getPoint();
21951
21952             // cache the previous dragOver array
21953             var oldOvers = [];
21954
21955             var outEvts   = [];
21956             var overEvts  = [];
21957             var dropEvts  = [];
21958             var enterEvts = [];
21959
21960             // Check to see if the object(s) we were hovering over is no longer
21961             // being hovered over so we can fire the onDragOut event
21962             for (var i in this.dragOvers) {
21963
21964                 var ddo = this.dragOvers[i];
21965
21966                 if (! this.isTypeOfDD(ddo)) {
21967                     continue;
21968                 }
21969
21970                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21971                     outEvts.push( ddo );
21972                 }
21973
21974                 oldOvers[i] = true;
21975                 delete this.dragOvers[i];
21976             }
21977
21978             for (var sGroup in dc.groups) {
21979
21980                 if ("string" != typeof sGroup) {
21981                     continue;
21982                 }
21983
21984                 for (i in this.ids[sGroup]) {
21985                     var oDD = this.ids[sGroup][i];
21986                     if (! this.isTypeOfDD(oDD)) {
21987                         continue;
21988                     }
21989
21990                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21991                         if (this.isOverTarget(pt, oDD, this.mode)) {
21992                             // look for drop interactions
21993                             if (isDrop) {
21994                                 dropEvts.push( oDD );
21995                             // look for drag enter and drag over interactions
21996                             } else {
21997
21998                                 // initial drag over: dragEnter fires
21999                                 if (!oldOvers[oDD.id]) {
22000                                     enterEvts.push( oDD );
22001                                 // subsequent drag overs: dragOver fires
22002                                 } else {
22003                                     overEvts.push( oDD );
22004                                 }
22005
22006                                 this.dragOvers[oDD.id] = oDD;
22007                             }
22008                         }
22009                     }
22010                 }
22011             }
22012
22013             if (this.mode) {
22014                 if (outEvts.length) {
22015                     dc.b4DragOut(e, outEvts);
22016                     dc.onDragOut(e, outEvts);
22017                 }
22018
22019                 if (enterEvts.length) {
22020                     dc.onDragEnter(e, enterEvts);
22021                 }
22022
22023                 if (overEvts.length) {
22024                     dc.b4DragOver(e, overEvts);
22025                     dc.onDragOver(e, overEvts);
22026                 }
22027
22028                 if (dropEvts.length) {
22029                     dc.b4DragDrop(e, dropEvts);
22030                     dc.onDragDrop(e, dropEvts);
22031                 }
22032
22033             } else {
22034                 // fire dragout events
22035                 var len = 0;
22036                 for (i=0, len=outEvts.length; i<len; ++i) {
22037                     dc.b4DragOut(e, outEvts[i].id);
22038                     dc.onDragOut(e, outEvts[i].id);
22039                 }
22040
22041                 // fire enter events
22042                 for (i=0,len=enterEvts.length; i<len; ++i) {
22043                     // dc.b4DragEnter(e, oDD.id);
22044                     dc.onDragEnter(e, enterEvts[i].id);
22045                 }
22046
22047                 // fire over events
22048                 for (i=0,len=overEvts.length; i<len; ++i) {
22049                     dc.b4DragOver(e, overEvts[i].id);
22050                     dc.onDragOver(e, overEvts[i].id);
22051                 }
22052
22053                 // fire drop events
22054                 for (i=0, len=dropEvts.length; i<len; ++i) {
22055                     dc.b4DragDrop(e, dropEvts[i].id);
22056                     dc.onDragDrop(e, dropEvts[i].id);
22057                 }
22058
22059             }
22060
22061             // notify about a drop that did not find a target
22062             if (isDrop && !dropEvts.length) {
22063                 dc.onInvalidDrop(e);
22064             }
22065
22066         },
22067
22068         /**
22069          * Helper function for getting the best match from the list of drag
22070          * and drop objects returned by the drag and drop events when we are
22071          * in INTERSECT mode.  It returns either the first object that the
22072          * cursor is over, or the object that has the greatest overlap with
22073          * the dragged element.
22074          * @method getBestMatch
22075          * @param  {DragDrop[]} dds The array of drag and drop objects
22076          * targeted
22077          * @return {DragDrop}       The best single match
22078          * @static
22079          */
22080         getBestMatch: function(dds) {
22081             var winner = null;
22082             // Return null if the input is not what we expect
22083             //if (!dds || !dds.length || dds.length == 0) {
22084                // winner = null;
22085             // If there is only one item, it wins
22086             //} else if (dds.length == 1) {
22087
22088             var len = dds.length;
22089
22090             if (len == 1) {
22091                 winner = dds[0];
22092             } else {
22093                 // Loop through the targeted items
22094                 for (var i=0; i<len; ++i) {
22095                     var dd = dds[i];
22096                     // If the cursor is over the object, it wins.  If the
22097                     // cursor is over multiple matches, the first one we come
22098                     // to wins.
22099                     if (dd.cursorIsOver) {
22100                         winner = dd;
22101                         break;
22102                     // Otherwise the object with the most overlap wins
22103                     } else {
22104                         if (!winner ||
22105                             winner.overlap.getArea() < dd.overlap.getArea()) {
22106                             winner = dd;
22107                         }
22108                     }
22109                 }
22110             }
22111
22112             return winner;
22113         },
22114
22115         /**
22116          * Refreshes the cache of the top-left and bottom-right points of the
22117          * drag and drop objects in the specified group(s).  This is in the
22118          * format that is stored in the drag and drop instance, so typical
22119          * usage is:
22120          * <code>
22121          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
22122          * </code>
22123          * Alternatively:
22124          * <code>
22125          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
22126          * </code>
22127          * @TODO this really should be an indexed array.  Alternatively this
22128          * method could accept both.
22129          * @method refreshCache
22130          * @param {Object} groups an associative array of groups to refresh
22131          * @static
22132          */
22133         refreshCache: function(groups) {
22134             for (var sGroup in groups) {
22135                 if ("string" != typeof sGroup) {
22136                     continue;
22137                 }
22138                 for (var i in this.ids[sGroup]) {
22139                     var oDD = this.ids[sGroup][i];
22140
22141                     if (this.isTypeOfDD(oDD)) {
22142                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
22143                         var loc = this.getLocation(oDD);
22144                         if (loc) {
22145                             this.locationCache[oDD.id] = loc;
22146                         } else {
22147                             delete this.locationCache[oDD.id];
22148                             // this will unregister the drag and drop object if
22149                             // the element is not in a usable state
22150                             // oDD.unreg();
22151                         }
22152                     }
22153                 }
22154             }
22155         },
22156
22157         /**
22158          * This checks to make sure an element exists and is in the DOM.  The
22159          * main purpose is to handle cases where innerHTML is used to remove
22160          * drag and drop objects from the DOM.  IE provides an 'unspecified
22161          * error' when trying to access the offsetParent of such an element
22162          * @method verifyEl
22163          * @param {HTMLElement} el the element to check
22164          * @return {boolean} true if the element looks usable
22165          * @static
22166          */
22167         verifyEl: function(el) {
22168             if (el) {
22169                 var parent;
22170                 if(Roo.isIE){
22171                     try{
22172                         parent = el.offsetParent;
22173                     }catch(e){}
22174                 }else{
22175                     parent = el.offsetParent;
22176                 }
22177                 if (parent) {
22178                     return true;
22179                 }
22180             }
22181
22182             return false;
22183         },
22184
22185         /**
22186          * Returns a Region object containing the drag and drop element's position
22187          * and size, including the padding configured for it
22188          * @method getLocation
22189          * @param {DragDrop} oDD the drag and drop object to get the
22190          *                       location for
22191          * @return {Roo.lib.Region} a Region object representing the total area
22192          *                             the element occupies, including any padding
22193          *                             the instance is configured for.
22194          * @static
22195          */
22196         getLocation: function(oDD) {
22197             if (! this.isTypeOfDD(oDD)) {
22198                 return null;
22199             }
22200
22201             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22202
22203             try {
22204                 pos= Roo.lib.Dom.getXY(el);
22205             } catch (e) { }
22206
22207             if (!pos) {
22208                 return null;
22209             }
22210
22211             x1 = pos[0];
22212             x2 = x1 + el.offsetWidth;
22213             y1 = pos[1];
22214             y2 = y1 + el.offsetHeight;
22215
22216             t = y1 - oDD.padding[0];
22217             r = x2 + oDD.padding[1];
22218             b = y2 + oDD.padding[2];
22219             l = x1 - oDD.padding[3];
22220
22221             return new Roo.lib.Region( t, r, b, l );
22222         },
22223
22224         /**
22225          * Checks the cursor location to see if it over the target
22226          * @method isOverTarget
22227          * @param {Roo.lib.Point} pt The point to evaluate
22228          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22229          * @return {boolean} true if the mouse is over the target
22230          * @private
22231          * @static
22232          */
22233         isOverTarget: function(pt, oTarget, intersect) {
22234             // use cache if available
22235             var loc = this.locationCache[oTarget.id];
22236             if (!loc || !this.useCache) {
22237                 loc = this.getLocation(oTarget);
22238                 this.locationCache[oTarget.id] = loc;
22239
22240             }
22241
22242             if (!loc) {
22243                 return false;
22244             }
22245
22246             oTarget.cursorIsOver = loc.contains( pt );
22247
22248             // DragDrop is using this as a sanity check for the initial mousedown
22249             // in this case we are done.  In POINT mode, if the drag obj has no
22250             // contraints, we are also done. Otherwise we need to evaluate the
22251             // location of the target as related to the actual location of the
22252             // dragged element.
22253             var dc = this.dragCurrent;
22254             if (!dc || !dc.getTargetCoord ||
22255                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22256                 return oTarget.cursorIsOver;
22257             }
22258
22259             oTarget.overlap = null;
22260
22261             // Get the current location of the drag element, this is the
22262             // location of the mouse event less the delta that represents
22263             // where the original mousedown happened on the element.  We
22264             // need to consider constraints and ticks as well.
22265             var pos = dc.getTargetCoord(pt.x, pt.y);
22266
22267             var el = dc.getDragEl();
22268             var curRegion = new Roo.lib.Region( pos.y,
22269                                                    pos.x + el.offsetWidth,
22270                                                    pos.y + el.offsetHeight,
22271                                                    pos.x );
22272
22273             var overlap = curRegion.intersect(loc);
22274
22275             if (overlap) {
22276                 oTarget.overlap = overlap;
22277                 return (intersect) ? true : oTarget.cursorIsOver;
22278             } else {
22279                 return false;
22280             }
22281         },
22282
22283         /**
22284          * unload event handler
22285          * @method _onUnload
22286          * @private
22287          * @static
22288          */
22289         _onUnload: function(e, me) {
22290             Roo.dd.DragDropMgr.unregAll();
22291         },
22292
22293         /**
22294          * Cleans up the drag and drop events and objects.
22295          * @method unregAll
22296          * @private
22297          * @static
22298          */
22299         unregAll: function() {
22300
22301             if (this.dragCurrent) {
22302                 this.stopDrag();
22303                 this.dragCurrent = null;
22304             }
22305
22306             this._execOnAll("unreg", []);
22307
22308             for (i in this.elementCache) {
22309                 delete this.elementCache[i];
22310             }
22311
22312             this.elementCache = {};
22313             this.ids = {};
22314         },
22315
22316         /**
22317          * A cache of DOM elements
22318          * @property elementCache
22319          * @private
22320          * @static
22321          */
22322         elementCache: {},
22323
22324         /**
22325          * Get the wrapper for the DOM element specified
22326          * @method getElWrapper
22327          * @param {String} id the id of the element to get
22328          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22329          * @private
22330          * @deprecated This wrapper isn't that useful
22331          * @static
22332          */
22333         getElWrapper: function(id) {
22334             var oWrapper = this.elementCache[id];
22335             if (!oWrapper || !oWrapper.el) {
22336                 oWrapper = this.elementCache[id] =
22337                     new this.ElementWrapper(Roo.getDom(id));
22338             }
22339             return oWrapper;
22340         },
22341
22342         /**
22343          * Returns the actual DOM element
22344          * @method getElement
22345          * @param {String} id the id of the elment to get
22346          * @return {Object} The element
22347          * @deprecated use Roo.getDom instead
22348          * @static
22349          */
22350         getElement: function(id) {
22351             return Roo.getDom(id);
22352         },
22353
22354         /**
22355          * Returns the style property for the DOM element (i.e.,
22356          * document.getElById(id).style)
22357          * @method getCss
22358          * @param {String} id the id of the elment to get
22359          * @return {Object} The style property of the element
22360          * @deprecated use Roo.getDom instead
22361          * @static
22362          */
22363         getCss: function(id) {
22364             var el = Roo.getDom(id);
22365             return (el) ? el.style : null;
22366         },
22367
22368         /**
22369          * Inner class for cached elements
22370          * @class DragDropMgr.ElementWrapper
22371          * @for DragDropMgr
22372          * @private
22373          * @deprecated
22374          */
22375         ElementWrapper: function(el) {
22376                 /**
22377                  * The element
22378                  * @property el
22379                  */
22380                 this.el = el || null;
22381                 /**
22382                  * The element id
22383                  * @property id
22384                  */
22385                 this.id = this.el && el.id;
22386                 /**
22387                  * A reference to the style property
22388                  * @property css
22389                  */
22390                 this.css = this.el && el.style;
22391             },
22392
22393         /**
22394          * Returns the X position of an html element
22395          * @method getPosX
22396          * @param el the element for which to get the position
22397          * @return {int} the X coordinate
22398          * @for DragDropMgr
22399          * @deprecated use Roo.lib.Dom.getX instead
22400          * @static
22401          */
22402         getPosX: function(el) {
22403             return Roo.lib.Dom.getX(el);
22404         },
22405
22406         /**
22407          * Returns the Y position of an html element
22408          * @method getPosY
22409          * @param el the element for which to get the position
22410          * @return {int} the Y coordinate
22411          * @deprecated use Roo.lib.Dom.getY instead
22412          * @static
22413          */
22414         getPosY: function(el) {
22415             return Roo.lib.Dom.getY(el);
22416         },
22417
22418         /**
22419          * Swap two nodes.  In IE, we use the native method, for others we
22420          * emulate the IE behavior
22421          * @method swapNode
22422          * @param n1 the first node to swap
22423          * @param n2 the other node to swap
22424          * @static
22425          */
22426         swapNode: function(n1, n2) {
22427             if (n1.swapNode) {
22428                 n1.swapNode(n2);
22429             } else {
22430                 var p = n2.parentNode;
22431                 var s = n2.nextSibling;
22432
22433                 if (s == n1) {
22434                     p.insertBefore(n1, n2);
22435                 } else if (n2 == n1.nextSibling) {
22436                     p.insertBefore(n2, n1);
22437                 } else {
22438                     n1.parentNode.replaceChild(n2, n1);
22439                     p.insertBefore(n1, s);
22440                 }
22441             }
22442         },
22443
22444         /**
22445          * Returns the current scroll position
22446          * @method getScroll
22447          * @private
22448          * @static
22449          */
22450         getScroll: function () {
22451             var t, l, dde=document.documentElement, db=document.body;
22452             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22453                 t = dde.scrollTop;
22454                 l = dde.scrollLeft;
22455             } else if (db) {
22456                 t = db.scrollTop;
22457                 l = db.scrollLeft;
22458             } else {
22459
22460             }
22461             return { top: t, left: l };
22462         },
22463
22464         /**
22465          * Returns the specified element style property
22466          * @method getStyle
22467          * @param {HTMLElement} el          the element
22468          * @param {string}      styleProp   the style property
22469          * @return {string} The value of the style property
22470          * @deprecated use Roo.lib.Dom.getStyle
22471          * @static
22472          */
22473         getStyle: function(el, styleProp) {
22474             return Roo.fly(el).getStyle(styleProp);
22475         },
22476
22477         /**
22478          * Gets the scrollTop
22479          * @method getScrollTop
22480          * @return {int} the document's scrollTop
22481          * @static
22482          */
22483         getScrollTop: function () { return this.getScroll().top; },
22484
22485         /**
22486          * Gets the scrollLeft
22487          * @method getScrollLeft
22488          * @return {int} the document's scrollTop
22489          * @static
22490          */
22491         getScrollLeft: function () { return this.getScroll().left; },
22492
22493         /**
22494          * Sets the x/y position of an element to the location of the
22495          * target element.
22496          * @method moveToEl
22497          * @param {HTMLElement} moveEl      The element to move
22498          * @param {HTMLElement} targetEl    The position reference element
22499          * @static
22500          */
22501         moveToEl: function (moveEl, targetEl) {
22502             var aCoord = Roo.lib.Dom.getXY(targetEl);
22503             Roo.lib.Dom.setXY(moveEl, aCoord);
22504         },
22505
22506         /**
22507          * Numeric array sort function
22508          * @method numericSort
22509          * @static
22510          */
22511         numericSort: function(a, b) { return (a - b); },
22512
22513         /**
22514          * Internal counter
22515          * @property _timeoutCount
22516          * @private
22517          * @static
22518          */
22519         _timeoutCount: 0,
22520
22521         /**
22522          * Trying to make the load order less important.  Without this we get
22523          * an error if this file is loaded before the Event Utility.
22524          * @method _addListeners
22525          * @private
22526          * @static
22527          */
22528         _addListeners: function() {
22529             var DDM = Roo.dd.DDM;
22530             if ( Roo.lib.Event && document ) {
22531                 DDM._onLoad();
22532             } else {
22533                 if (DDM._timeoutCount > 2000) {
22534                 } else {
22535                     setTimeout(DDM._addListeners, 10);
22536                     if (document && document.body) {
22537                         DDM._timeoutCount += 1;
22538                     }
22539                 }
22540             }
22541         },
22542
22543         /**
22544          * Recursively searches the immediate parent and all child nodes for
22545          * the handle element in order to determine wheter or not it was
22546          * clicked.
22547          * @method handleWasClicked
22548          * @param node the html element to inspect
22549          * @static
22550          */
22551         handleWasClicked: function(node, id) {
22552             if (this.isHandle(id, node.id)) {
22553                 return true;
22554             } else {
22555                 // check to see if this is a text node child of the one we want
22556                 var p = node.parentNode;
22557
22558                 while (p) {
22559                     if (this.isHandle(id, p.id)) {
22560                         return true;
22561                     } else {
22562                         p = p.parentNode;
22563                     }
22564                 }
22565             }
22566
22567             return false;
22568         }
22569
22570     };
22571
22572 }();
22573
22574 // shorter alias, save a few bytes
22575 Roo.dd.DDM = Roo.dd.DragDropMgr;
22576 Roo.dd.DDM._addListeners();
22577
22578 }/*
22579  * Based on:
22580  * Ext JS Library 1.1.1
22581  * Copyright(c) 2006-2007, Ext JS, LLC.
22582  *
22583  * Originally Released Under LGPL - original licence link has changed is not relivant.
22584  *
22585  * Fork - LGPL
22586  * <script type="text/javascript">
22587  */
22588
22589 /**
22590  * @class Roo.dd.DD
22591  * A DragDrop implementation where the linked element follows the
22592  * mouse cursor during a drag.
22593  * @extends Roo.dd.DragDrop
22594  * @constructor
22595  * @param {String} id the id of the linked element
22596  * @param {String} sGroup the group of related DragDrop items
22597  * @param {object} config an object containing configurable attributes
22598  *                Valid properties for DD:
22599  *                    scroll
22600  */
22601 Roo.dd.DD = function(id, sGroup, config) {
22602     if (id) {
22603         this.init(id, sGroup, config);
22604     }
22605 };
22606
22607 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22608
22609     /**
22610      * When set to true, the utility automatically tries to scroll the browser
22611      * window wehn a drag and drop element is dragged near the viewport boundary.
22612      * Defaults to true.
22613      * @property scroll
22614      * @type boolean
22615      */
22616     scroll: true,
22617
22618     /**
22619      * Sets the pointer offset to the distance between the linked element's top
22620      * left corner and the location the element was clicked
22621      * @method autoOffset
22622      * @param {int} iPageX the X coordinate of the click
22623      * @param {int} iPageY the Y coordinate of the click
22624      */
22625     autoOffset: function(iPageX, iPageY) {
22626         var x = iPageX - this.startPageX;
22627         var y = iPageY - this.startPageY;
22628         this.setDelta(x, y);
22629     },
22630
22631     /**
22632      * Sets the pointer offset.  You can call this directly to force the
22633      * offset to be in a particular location (e.g., pass in 0,0 to set it
22634      * to the center of the object)
22635      * @method setDelta
22636      * @param {int} iDeltaX the distance from the left
22637      * @param {int} iDeltaY the distance from the top
22638      */
22639     setDelta: function(iDeltaX, iDeltaY) {
22640         this.deltaX = iDeltaX;
22641         this.deltaY = iDeltaY;
22642     },
22643
22644     /**
22645      * Sets the drag element to the location of the mousedown or click event,
22646      * maintaining the cursor location relative to the location on the element
22647      * that was clicked.  Override this if you want to place the element in a
22648      * location other than where the cursor is.
22649      * @method setDragElPos
22650      * @param {int} iPageX the X coordinate of the mousedown or drag event
22651      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22652      */
22653     setDragElPos: function(iPageX, iPageY) {
22654         // the first time we do this, we are going to check to make sure
22655         // the element has css positioning
22656
22657         var el = this.getDragEl();
22658         this.alignElWithMouse(el, iPageX, iPageY);
22659     },
22660
22661     /**
22662      * Sets the element to the location of the mousedown or click event,
22663      * maintaining the cursor location relative to the location on the element
22664      * that was clicked.  Override this if you want to place the element in a
22665      * location other than where the cursor is.
22666      * @method alignElWithMouse
22667      * @param {HTMLElement} el the element to move
22668      * @param {int} iPageX the X coordinate of the mousedown or drag event
22669      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22670      */
22671     alignElWithMouse: function(el, iPageX, iPageY) {
22672         var oCoord = this.getTargetCoord(iPageX, iPageY);
22673         var fly = el.dom ? el : Roo.fly(el);
22674         if (!this.deltaSetXY) {
22675             var aCoord = [oCoord.x, oCoord.y];
22676             fly.setXY(aCoord);
22677             var newLeft = fly.getLeft(true);
22678             var newTop  = fly.getTop(true);
22679             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22680         } else {
22681             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22682         }
22683
22684         this.cachePosition(oCoord.x, oCoord.y);
22685         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22686         return oCoord;
22687     },
22688
22689     /**
22690      * Saves the most recent position so that we can reset the constraints and
22691      * tick marks on-demand.  We need to know this so that we can calculate the
22692      * number of pixels the element is offset from its original position.
22693      * @method cachePosition
22694      * @param iPageX the current x position (optional, this just makes it so we
22695      * don't have to look it up again)
22696      * @param iPageY the current y position (optional, this just makes it so we
22697      * don't have to look it up again)
22698      */
22699     cachePosition: function(iPageX, iPageY) {
22700         if (iPageX) {
22701             this.lastPageX = iPageX;
22702             this.lastPageY = iPageY;
22703         } else {
22704             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22705             this.lastPageX = aCoord[0];
22706             this.lastPageY = aCoord[1];
22707         }
22708     },
22709
22710     /**
22711      * Auto-scroll the window if the dragged object has been moved beyond the
22712      * visible window boundary.
22713      * @method autoScroll
22714      * @param {int} x the drag element's x position
22715      * @param {int} y the drag element's y position
22716      * @param {int} h the height of the drag element
22717      * @param {int} w the width of the drag element
22718      * @private
22719      */
22720     autoScroll: function(x, y, h, w) {
22721
22722         if (this.scroll) {
22723             // The client height
22724             var clientH = Roo.lib.Dom.getViewWidth();
22725
22726             // The client width
22727             var clientW = Roo.lib.Dom.getViewHeight();
22728
22729             // The amt scrolled down
22730             var st = this.DDM.getScrollTop();
22731
22732             // The amt scrolled right
22733             var sl = this.DDM.getScrollLeft();
22734
22735             // Location of the bottom of the element
22736             var bot = h + y;
22737
22738             // Location of the right of the element
22739             var right = w + x;
22740
22741             // The distance from the cursor to the bottom of the visible area,
22742             // adjusted so that we don't scroll if the cursor is beyond the
22743             // element drag constraints
22744             var toBot = (clientH + st - y - this.deltaY);
22745
22746             // The distance from the cursor to the right of the visible area
22747             var toRight = (clientW + sl - x - this.deltaX);
22748
22749
22750             // How close to the edge the cursor must be before we scroll
22751             // var thresh = (document.all) ? 100 : 40;
22752             var thresh = 40;
22753
22754             // How many pixels to scroll per autoscroll op.  This helps to reduce
22755             // clunky scrolling. IE is more sensitive about this ... it needs this
22756             // value to be higher.
22757             var scrAmt = (document.all) ? 80 : 30;
22758
22759             // Scroll down if we are near the bottom of the visible page and the
22760             // obj extends below the crease
22761             if ( bot > clientH && toBot < thresh ) {
22762                 window.scrollTo(sl, st + scrAmt);
22763             }
22764
22765             // Scroll up if the window is scrolled down and the top of the object
22766             // goes above the top border
22767             if ( y < st && st > 0 && y - st < thresh ) {
22768                 window.scrollTo(sl, st - scrAmt);
22769             }
22770
22771             // Scroll right if the obj is beyond the right border and the cursor is
22772             // near the border.
22773             if ( right > clientW && toRight < thresh ) {
22774                 window.scrollTo(sl + scrAmt, st);
22775             }
22776
22777             // Scroll left if the window has been scrolled to the right and the obj
22778             // extends past the left border
22779             if ( x < sl && sl > 0 && x - sl < thresh ) {
22780                 window.scrollTo(sl - scrAmt, st);
22781             }
22782         }
22783     },
22784
22785     /**
22786      * Finds the location the element should be placed if we want to move
22787      * it to where the mouse location less the click offset would place us.
22788      * @method getTargetCoord
22789      * @param {int} iPageX the X coordinate of the click
22790      * @param {int} iPageY the Y coordinate of the click
22791      * @return an object that contains the coordinates (Object.x and Object.y)
22792      * @private
22793      */
22794     getTargetCoord: function(iPageX, iPageY) {
22795
22796
22797         var x = iPageX - this.deltaX;
22798         var y = iPageY - this.deltaY;
22799
22800         if (this.constrainX) {
22801             if (x < this.minX) { x = this.minX; }
22802             if (x > this.maxX) { x = this.maxX; }
22803         }
22804
22805         if (this.constrainY) {
22806             if (y < this.minY) { y = this.minY; }
22807             if (y > this.maxY) { y = this.maxY; }
22808         }
22809
22810         x = this.getTick(x, this.xTicks);
22811         y = this.getTick(y, this.yTicks);
22812
22813
22814         return {x:x, y:y};
22815     },
22816
22817     /*
22818      * Sets up config options specific to this class. Overrides
22819      * Roo.dd.DragDrop, but all versions of this method through the
22820      * inheritance chain are called
22821      */
22822     applyConfig: function() {
22823         Roo.dd.DD.superclass.applyConfig.call(this);
22824         this.scroll = (this.config.scroll !== false);
22825     },
22826
22827     /*
22828      * Event that fires prior to the onMouseDown event.  Overrides
22829      * Roo.dd.DragDrop.
22830      */
22831     b4MouseDown: function(e) {
22832         // this.resetConstraints();
22833         this.autoOffset(e.getPageX(),
22834                             e.getPageY());
22835     },
22836
22837     /*
22838      * Event that fires prior to the onDrag event.  Overrides
22839      * Roo.dd.DragDrop.
22840      */
22841     b4Drag: function(e) {
22842         this.setDragElPos(e.getPageX(),
22843                             e.getPageY());
22844     },
22845
22846     toString: function() {
22847         return ("DD " + this.id);
22848     }
22849
22850     //////////////////////////////////////////////////////////////////////////
22851     // Debugging ygDragDrop events that can be overridden
22852     //////////////////////////////////////////////////////////////////////////
22853     /*
22854     startDrag: function(x, y) {
22855     },
22856
22857     onDrag: function(e) {
22858     },
22859
22860     onDragEnter: function(e, id) {
22861     },
22862
22863     onDragOver: function(e, id) {
22864     },
22865
22866     onDragOut: function(e, id) {
22867     },
22868
22869     onDragDrop: function(e, id) {
22870     },
22871
22872     endDrag: function(e) {
22873     }
22874
22875     */
22876
22877 });/*
22878  * Based on:
22879  * Ext JS Library 1.1.1
22880  * Copyright(c) 2006-2007, Ext JS, LLC.
22881  *
22882  * Originally Released Under LGPL - original licence link has changed is not relivant.
22883  *
22884  * Fork - LGPL
22885  * <script type="text/javascript">
22886  */
22887
22888 /**
22889  * @class Roo.dd.DDProxy
22890  * A DragDrop implementation that inserts an empty, bordered div into
22891  * the document that follows the cursor during drag operations.  At the time of
22892  * the click, the frame div is resized to the dimensions of the linked html
22893  * element, and moved to the exact location of the linked element.
22894  *
22895  * References to the "frame" element refer to the single proxy element that
22896  * was created to be dragged in place of all DDProxy elements on the
22897  * page.
22898  *
22899  * @extends Roo.dd.DD
22900  * @constructor
22901  * @param {String} id the id of the linked html element
22902  * @param {String} sGroup the group of related DragDrop objects
22903  * @param {object} config an object containing configurable attributes
22904  *                Valid properties for DDProxy in addition to those in DragDrop:
22905  *                   resizeFrame, centerFrame, dragElId
22906  */
22907 Roo.dd.DDProxy = function(id, sGroup, config) {
22908     if (id) {
22909         this.init(id, sGroup, config);
22910         this.initFrame();
22911     }
22912 };
22913
22914 /**
22915  * The default drag frame div id
22916  * @property Roo.dd.DDProxy.dragElId
22917  * @type String
22918  * @static
22919  */
22920 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22921
22922 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22923
22924     /**
22925      * By default we resize the drag frame to be the same size as the element
22926      * we want to drag (this is to get the frame effect).  We can turn it off
22927      * if we want a different behavior.
22928      * @property resizeFrame
22929      * @type boolean
22930      */
22931     resizeFrame: true,
22932
22933     /**
22934      * By default the frame is positioned exactly where the drag element is, so
22935      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22936      * you do not have constraints on the obj is to have the drag frame centered
22937      * around the cursor.  Set centerFrame to true for this effect.
22938      * @property centerFrame
22939      * @type boolean
22940      */
22941     centerFrame: false,
22942
22943     /**
22944      * Creates the proxy element if it does not yet exist
22945      * @method createFrame
22946      */
22947     createFrame: function() {
22948         var self = this;
22949         var body = document.body;
22950
22951         if (!body || !body.firstChild) {
22952             setTimeout( function() { self.createFrame(); }, 50 );
22953             return;
22954         }
22955
22956         var div = this.getDragEl();
22957
22958         if (!div) {
22959             div    = document.createElement("div");
22960             div.id = this.dragElId;
22961             var s  = div.style;
22962
22963             s.position   = "absolute";
22964             s.visibility = "hidden";
22965             s.cursor     = "move";
22966             s.border     = "2px solid #aaa";
22967             s.zIndex     = 999;
22968
22969             // appendChild can blow up IE if invoked prior to the window load event
22970             // while rendering a table.  It is possible there are other scenarios
22971             // that would cause this to happen as well.
22972             body.insertBefore(div, body.firstChild);
22973         }
22974     },
22975
22976     /**
22977      * Initialization for the drag frame element.  Must be called in the
22978      * constructor of all subclasses
22979      * @method initFrame
22980      */
22981     initFrame: function() {
22982         this.createFrame();
22983     },
22984
22985     applyConfig: function() {
22986         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22987
22988         this.resizeFrame = (this.config.resizeFrame !== false);
22989         this.centerFrame = (this.config.centerFrame);
22990         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22991     },
22992
22993     /**
22994      * Resizes the drag frame to the dimensions of the clicked object, positions
22995      * it over the object, and finally displays it
22996      * @method showFrame
22997      * @param {int} iPageX X click position
22998      * @param {int} iPageY Y click position
22999      * @private
23000      */
23001     showFrame: function(iPageX, iPageY) {
23002         var el = this.getEl();
23003         var dragEl = this.getDragEl();
23004         var s = dragEl.style;
23005
23006         this._resizeProxy();
23007
23008         if (this.centerFrame) {
23009             this.setDelta( Math.round(parseInt(s.width,  10)/2),
23010                            Math.round(parseInt(s.height, 10)/2) );
23011         }
23012
23013         this.setDragElPos(iPageX, iPageY);
23014
23015         Roo.fly(dragEl).show();
23016     },
23017
23018     /**
23019      * The proxy is automatically resized to the dimensions of the linked
23020      * element when a drag is initiated, unless resizeFrame is set to false
23021      * @method _resizeProxy
23022      * @private
23023      */
23024     _resizeProxy: function() {
23025         if (this.resizeFrame) {
23026             var el = this.getEl();
23027             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
23028         }
23029     },
23030
23031     // overrides Roo.dd.DragDrop
23032     b4MouseDown: function(e) {
23033         var x = e.getPageX();
23034         var y = e.getPageY();
23035         this.autoOffset(x, y);
23036         this.setDragElPos(x, y);
23037     },
23038
23039     // overrides Roo.dd.DragDrop
23040     b4StartDrag: function(x, y) {
23041         // show the drag frame
23042         this.showFrame(x, y);
23043     },
23044
23045     // overrides Roo.dd.DragDrop
23046     b4EndDrag: function(e) {
23047         Roo.fly(this.getDragEl()).hide();
23048     },
23049
23050     // overrides Roo.dd.DragDrop
23051     // By default we try to move the element to the last location of the frame.
23052     // This is so that the default behavior mirrors that of Roo.dd.DD.
23053     endDrag: function(e) {
23054
23055         var lel = this.getEl();
23056         var del = this.getDragEl();
23057
23058         // Show the drag frame briefly so we can get its position
23059         del.style.visibility = "";
23060
23061         this.beforeMove();
23062         // Hide the linked element before the move to get around a Safari
23063         // rendering bug.
23064         lel.style.visibility = "hidden";
23065         Roo.dd.DDM.moveToEl(lel, del);
23066         del.style.visibility = "hidden";
23067         lel.style.visibility = "";
23068
23069         this.afterDrag();
23070     },
23071
23072     beforeMove : function(){
23073
23074     },
23075
23076     afterDrag : function(){
23077
23078     },
23079
23080     toString: function() {
23081         return ("DDProxy " + this.id);
23082     }
23083
23084 });
23085 /*
23086  * Based on:
23087  * Ext JS Library 1.1.1
23088  * Copyright(c) 2006-2007, Ext JS, LLC.
23089  *
23090  * Originally Released Under LGPL - original licence link has changed is not relivant.
23091  *
23092  * Fork - LGPL
23093  * <script type="text/javascript">
23094  */
23095
23096  /**
23097  * @class Roo.dd.DDTarget
23098  * A DragDrop implementation that does not move, but can be a drop
23099  * target.  You would get the same result by simply omitting implementation
23100  * for the event callbacks, but this way we reduce the processing cost of the
23101  * event listener and the callbacks.
23102  * @extends Roo.dd.DragDrop
23103  * @constructor
23104  * @param {String} id the id of the element that is a drop target
23105  * @param {String} sGroup the group of related DragDrop objects
23106  * @param {object} config an object containing configurable attributes
23107  *                 Valid properties for DDTarget in addition to those in
23108  *                 DragDrop:
23109  *                    none
23110  */
23111 Roo.dd.DDTarget = function(id, sGroup, config) {
23112     if (id) {
23113         this.initTarget(id, sGroup, config);
23114     }
23115     if (config && (config.listeners || config.events)) { 
23116         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
23117             listeners : config.listeners || {}, 
23118             events : config.events || {} 
23119         });    
23120     }
23121 };
23122
23123 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
23124 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
23125     toString: function() {
23126         return ("DDTarget " + this.id);
23127     }
23128 });
23129 /*
23130  * Based on:
23131  * Ext JS Library 1.1.1
23132  * Copyright(c) 2006-2007, Ext JS, LLC.
23133  *
23134  * Originally Released Under LGPL - original licence link has changed is not relivant.
23135  *
23136  * Fork - LGPL
23137  * <script type="text/javascript">
23138  */
23139  
23140
23141 /**
23142  * @class Roo.dd.ScrollManager
23143  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
23144  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
23145  * @static
23146  */
23147 Roo.dd.ScrollManager = function(){
23148     var ddm = Roo.dd.DragDropMgr;
23149     var els = {};
23150     var dragEl = null;
23151     var proc = {};
23152     
23153     
23154     
23155     var onStop = function(e){
23156         dragEl = null;
23157         clearProc();
23158     };
23159     
23160     var triggerRefresh = function(){
23161         if(ddm.dragCurrent){
23162              ddm.refreshCache(ddm.dragCurrent.groups);
23163         }
23164     };
23165     
23166     var doScroll = function(){
23167         if(ddm.dragCurrent){
23168             var dds = Roo.dd.ScrollManager;
23169             if(!dds.animate){
23170                 if(proc.el.scroll(proc.dir, dds.increment)){
23171                     triggerRefresh();
23172                 }
23173             }else{
23174                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23175             }
23176         }
23177     };
23178     
23179     var clearProc = function(){
23180         if(proc.id){
23181             clearInterval(proc.id);
23182         }
23183         proc.id = 0;
23184         proc.el = null;
23185         proc.dir = "";
23186     };
23187     
23188     var startProc = function(el, dir){
23189          Roo.log('scroll startproc');
23190         clearProc();
23191         proc.el = el;
23192         proc.dir = dir;
23193         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23194     };
23195     
23196     var onFire = function(e, isDrop){
23197        
23198         if(isDrop || !ddm.dragCurrent){ return; }
23199         var dds = Roo.dd.ScrollManager;
23200         if(!dragEl || dragEl != ddm.dragCurrent){
23201             dragEl = ddm.dragCurrent;
23202             // refresh regions on drag start
23203             dds.refreshCache();
23204         }
23205         
23206         var xy = Roo.lib.Event.getXY(e);
23207         var pt = new Roo.lib.Point(xy[0], xy[1]);
23208         for(var id in els){
23209             var el = els[id], r = el._region;
23210             if(r && r.contains(pt) && el.isScrollable()){
23211                 if(r.bottom - pt.y <= dds.thresh){
23212                     if(proc.el != el){
23213                         startProc(el, "down");
23214                     }
23215                     return;
23216                 }else if(r.right - pt.x <= dds.thresh){
23217                     if(proc.el != el){
23218                         startProc(el, "left");
23219                     }
23220                     return;
23221                 }else if(pt.y - r.top <= dds.thresh){
23222                     if(proc.el != el){
23223                         startProc(el, "up");
23224                     }
23225                     return;
23226                 }else if(pt.x - r.left <= dds.thresh){
23227                     if(proc.el != el){
23228                         startProc(el, "right");
23229                     }
23230                     return;
23231                 }
23232             }
23233         }
23234         clearProc();
23235     };
23236     
23237     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23238     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23239     
23240     return {
23241         /**
23242          * Registers new overflow element(s) to auto scroll
23243          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23244          */
23245         register : function(el){
23246             if(el instanceof Array){
23247                 for(var i = 0, len = el.length; i < len; i++) {
23248                         this.register(el[i]);
23249                 }
23250             }else{
23251                 el = Roo.get(el);
23252                 els[el.id] = el;
23253             }
23254             Roo.dd.ScrollManager.els = els;
23255         },
23256         
23257         /**
23258          * Unregisters overflow element(s) so they are no longer scrolled
23259          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23260          */
23261         unregister : function(el){
23262             if(el instanceof Array){
23263                 for(var i = 0, len = el.length; i < len; i++) {
23264                         this.unregister(el[i]);
23265                 }
23266             }else{
23267                 el = Roo.get(el);
23268                 delete els[el.id];
23269             }
23270         },
23271         
23272         /**
23273          * The number of pixels from the edge of a container the pointer needs to be to 
23274          * trigger scrolling (defaults to 25)
23275          * @type Number
23276          */
23277         thresh : 25,
23278         
23279         /**
23280          * The number of pixels to scroll in each scroll increment (defaults to 50)
23281          * @type Number
23282          */
23283         increment : 100,
23284         
23285         /**
23286          * The frequency of scrolls in milliseconds (defaults to 500)
23287          * @type Number
23288          */
23289         frequency : 500,
23290         
23291         /**
23292          * True to animate the scroll (defaults to true)
23293          * @type Boolean
23294          */
23295         animate: true,
23296         
23297         /**
23298          * The animation duration in seconds - 
23299          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23300          * @type Number
23301          */
23302         animDuration: .4,
23303         
23304         /**
23305          * Manually trigger a cache refresh.
23306          */
23307         refreshCache : function(){
23308             for(var id in els){
23309                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23310                     els[id]._region = els[id].getRegion();
23311                 }
23312             }
23313         }
23314     };
23315 }();/*
23316  * Based on:
23317  * Ext JS Library 1.1.1
23318  * Copyright(c) 2006-2007, Ext JS, LLC.
23319  *
23320  * Originally Released Under LGPL - original licence link has changed is not relivant.
23321  *
23322  * Fork - LGPL
23323  * <script type="text/javascript">
23324  */
23325  
23326
23327 /**
23328  * @class Roo.dd.Registry
23329  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23330  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23331  * @static
23332  */
23333 Roo.dd.Registry = function(){
23334     var elements = {}; 
23335     var handles = {}; 
23336     var autoIdSeed = 0;
23337
23338     var getId = function(el, autogen){
23339         if(typeof el == "string"){
23340             return el;
23341         }
23342         var id = el.id;
23343         if(!id && autogen !== false){
23344             id = "roodd-" + (++autoIdSeed);
23345             el.id = id;
23346         }
23347         return id;
23348     };
23349     
23350     return {
23351     /**
23352      * Register a drag drop element
23353      * @param {String|HTMLElement} element The id or DOM node to register
23354      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23355      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23356      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23357      * populated in the data object (if applicable):
23358      * <pre>
23359 Value      Description<br />
23360 ---------  ------------------------------------------<br />
23361 handles    Array of DOM nodes that trigger dragging<br />
23362            for the element being registered<br />
23363 isHandle   True if the element passed in triggers<br />
23364            dragging itself, else false
23365 </pre>
23366      */
23367         register : function(el, data){
23368             data = data || {};
23369             if(typeof el == "string"){
23370                 el = document.getElementById(el);
23371             }
23372             data.ddel = el;
23373             elements[getId(el)] = data;
23374             if(data.isHandle !== false){
23375                 handles[data.ddel.id] = data;
23376             }
23377             if(data.handles){
23378                 var hs = data.handles;
23379                 for(var i = 0, len = hs.length; i < len; i++){
23380                         handles[getId(hs[i])] = data;
23381                 }
23382             }
23383         },
23384
23385     /**
23386      * Unregister a drag drop element
23387      * @param {String|HTMLElement}  element The id or DOM node to unregister
23388      */
23389         unregister : function(el){
23390             var id = getId(el, false);
23391             var data = elements[id];
23392             if(data){
23393                 delete elements[id];
23394                 if(data.handles){
23395                     var hs = data.handles;
23396                     for(var i = 0, len = hs.length; i < len; i++){
23397                         delete handles[getId(hs[i], false)];
23398                     }
23399                 }
23400             }
23401         },
23402
23403     /**
23404      * Returns the handle registered for a DOM Node by id
23405      * @param {String|HTMLElement} id The DOM node or id to look up
23406      * @return {Object} handle The custom handle data
23407      */
23408         getHandle : function(id){
23409             if(typeof id != "string"){ // must be element?
23410                 id = id.id;
23411             }
23412             return handles[id];
23413         },
23414
23415     /**
23416      * Returns the handle that is registered for the DOM node that is the target of the event
23417      * @param {Event} e The event
23418      * @return {Object} handle The custom handle data
23419      */
23420         getHandleFromEvent : function(e){
23421             var t = Roo.lib.Event.getTarget(e);
23422             return t ? handles[t.id] : null;
23423         },
23424
23425     /**
23426      * Returns a custom data object that is registered for a DOM node by id
23427      * @param {String|HTMLElement} id The DOM node or id to look up
23428      * @return {Object} data The custom data
23429      */
23430         getTarget : function(id){
23431             if(typeof id != "string"){ // must be element?
23432                 id = id.id;
23433             }
23434             return elements[id];
23435         },
23436
23437     /**
23438      * Returns a custom data object that is registered for the DOM node that is the target of the event
23439      * @param {Event} e The event
23440      * @return {Object} data The custom data
23441      */
23442         getTargetFromEvent : function(e){
23443             var t = Roo.lib.Event.getTarget(e);
23444             return t ? elements[t.id] || handles[t.id] : null;
23445         }
23446     };
23447 }();/*
23448  * Based on:
23449  * Ext JS Library 1.1.1
23450  * Copyright(c) 2006-2007, Ext JS, LLC.
23451  *
23452  * Originally Released Under LGPL - original licence link has changed is not relivant.
23453  *
23454  * Fork - LGPL
23455  * <script type="text/javascript">
23456  */
23457  
23458
23459 /**
23460  * @class Roo.dd.StatusProxy
23461  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23462  * default drag proxy used by all Roo.dd components.
23463  * @constructor
23464  * @param {Object} config
23465  */
23466 Roo.dd.StatusProxy = function(config){
23467     Roo.apply(this, config);
23468     this.id = this.id || Roo.id();
23469     this.el = new Roo.Layer({
23470         dh: {
23471             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23472                 {tag: "div", cls: "x-dd-drop-icon"},
23473                 {tag: "div", cls: "x-dd-drag-ghost"}
23474             ]
23475         }, 
23476         shadow: !config || config.shadow !== false
23477     });
23478     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23479     this.dropStatus = this.dropNotAllowed;
23480 };
23481
23482 Roo.dd.StatusProxy.prototype = {
23483     /**
23484      * @cfg {String} dropAllowed
23485      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23486      */
23487     dropAllowed : "x-dd-drop-ok",
23488     /**
23489      * @cfg {String} dropNotAllowed
23490      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23491      */
23492     dropNotAllowed : "x-dd-drop-nodrop",
23493
23494     /**
23495      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23496      * over the current target element.
23497      * @param {String} cssClass The css class for the new drop status indicator image
23498      */
23499     setStatus : function(cssClass){
23500         cssClass = cssClass || this.dropNotAllowed;
23501         if(this.dropStatus != cssClass){
23502             this.el.replaceClass(this.dropStatus, cssClass);
23503             this.dropStatus = cssClass;
23504         }
23505     },
23506
23507     /**
23508      * Resets the status indicator to the default dropNotAllowed value
23509      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23510      */
23511     reset : function(clearGhost){
23512         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23513         this.dropStatus = this.dropNotAllowed;
23514         if(clearGhost){
23515             this.ghost.update("");
23516         }
23517     },
23518
23519     /**
23520      * Updates the contents of the ghost element
23521      * @param {String} html The html that will replace the current innerHTML of the ghost element
23522      */
23523     update : function(html){
23524         if(typeof html == "string"){
23525             this.ghost.update(html);
23526         }else{
23527             this.ghost.update("");
23528             html.style.margin = "0";
23529             this.ghost.dom.appendChild(html);
23530         }
23531         // ensure float = none set?? cant remember why though.
23532         var el = this.ghost.dom.firstChild;
23533                 if(el){
23534                         Roo.fly(el).setStyle('float', 'none');
23535                 }
23536     },
23537     
23538     /**
23539      * Returns the underlying proxy {@link Roo.Layer}
23540      * @return {Roo.Layer} el
23541     */
23542     getEl : function(){
23543         return this.el;
23544     },
23545
23546     /**
23547      * Returns the ghost element
23548      * @return {Roo.Element} el
23549      */
23550     getGhost : function(){
23551         return this.ghost;
23552     },
23553
23554     /**
23555      * Hides the proxy
23556      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23557      */
23558     hide : function(clear){
23559         this.el.hide();
23560         if(clear){
23561             this.reset(true);
23562         }
23563     },
23564
23565     /**
23566      * Stops the repair animation if it's currently running
23567      */
23568     stop : function(){
23569         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23570             this.anim.stop();
23571         }
23572     },
23573
23574     /**
23575      * Displays this proxy
23576      */
23577     show : function(){
23578         this.el.show();
23579     },
23580
23581     /**
23582      * Force the Layer to sync its shadow and shim positions to the element
23583      */
23584     sync : function(){
23585         this.el.sync();
23586     },
23587
23588     /**
23589      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23590      * invalid drop operation by the item being dragged.
23591      * @param {Array} xy The XY position of the element ([x, y])
23592      * @param {Function} callback The function to call after the repair is complete
23593      * @param {Object} scope The scope in which to execute the callback
23594      */
23595     repair : function(xy, callback, scope){
23596         this.callback = callback;
23597         this.scope = scope;
23598         if(xy && this.animRepair !== false){
23599             this.el.addClass("x-dd-drag-repair");
23600             this.el.hideUnders(true);
23601             this.anim = this.el.shift({
23602                 duration: this.repairDuration || .5,
23603                 easing: 'easeOut',
23604                 xy: xy,
23605                 stopFx: true,
23606                 callback: this.afterRepair,
23607                 scope: this
23608             });
23609         }else{
23610             this.afterRepair();
23611         }
23612     },
23613
23614     // private
23615     afterRepair : function(){
23616         this.hide(true);
23617         if(typeof this.callback == "function"){
23618             this.callback.call(this.scope || this);
23619         }
23620         this.callback = null;
23621         this.scope = null;
23622     }
23623 };/*
23624  * Based on:
23625  * Ext JS Library 1.1.1
23626  * Copyright(c) 2006-2007, Ext JS, LLC.
23627  *
23628  * Originally Released Under LGPL - original licence link has changed is not relivant.
23629  *
23630  * Fork - LGPL
23631  * <script type="text/javascript">
23632  */
23633
23634 /**
23635  * @class Roo.dd.DragSource
23636  * @extends Roo.dd.DDProxy
23637  * A simple class that provides the basic implementation needed to make any element draggable.
23638  * @constructor
23639  * @param {String/HTMLElement/Element} el The container element
23640  * @param {Object} config
23641  */
23642 Roo.dd.DragSource = function(el, config){
23643     this.el = Roo.get(el);
23644     this.dragData = {};
23645     
23646     Roo.apply(this, config);
23647     
23648     if(!this.proxy){
23649         this.proxy = new Roo.dd.StatusProxy();
23650     }
23651
23652     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23653           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23654     
23655     this.dragging = false;
23656 };
23657
23658 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23659     /**
23660      * @cfg {String} dropAllowed
23661      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23662      */
23663     dropAllowed : "x-dd-drop-ok",
23664     /**
23665      * @cfg {String} dropNotAllowed
23666      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23667      */
23668     dropNotAllowed : "x-dd-drop-nodrop",
23669
23670     /**
23671      * Returns the data object associated with this drag source
23672      * @return {Object} data An object containing arbitrary data
23673      */
23674     getDragData : function(e){
23675         return this.dragData;
23676     },
23677
23678     // private
23679     onDragEnter : function(e, id){
23680         var target = Roo.dd.DragDropMgr.getDDById(id);
23681         this.cachedTarget = target;
23682         if(this.beforeDragEnter(target, e, id) !== false){
23683             if(target.isNotifyTarget){
23684                 var status = target.notifyEnter(this, e, this.dragData);
23685                 this.proxy.setStatus(status);
23686             }else{
23687                 this.proxy.setStatus(this.dropAllowed);
23688             }
23689             
23690             if(this.afterDragEnter){
23691                 /**
23692                  * An empty function by default, but provided so that you can perform a custom action
23693                  * when the dragged item enters the drop target by providing an implementation.
23694                  * @param {Roo.dd.DragDrop} target The drop target
23695                  * @param {Event} e The event object
23696                  * @param {String} id The id of the dragged element
23697                  * @method afterDragEnter
23698                  */
23699                 this.afterDragEnter(target, e, id);
23700             }
23701         }
23702     },
23703
23704     /**
23705      * An empty function by default, but provided so that you can perform a custom action
23706      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23707      * @param {Roo.dd.DragDrop} target The drop target
23708      * @param {Event} e The event object
23709      * @param {String} id The id of the dragged element
23710      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23711      */
23712     beforeDragEnter : function(target, e, id){
23713         return true;
23714     },
23715
23716     // private
23717     alignElWithMouse: function() {
23718         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23719         this.proxy.sync();
23720     },
23721
23722     // private
23723     onDragOver : function(e, id){
23724         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23725         if(this.beforeDragOver(target, e, id) !== false){
23726             if(target.isNotifyTarget){
23727                 var status = target.notifyOver(this, e, this.dragData);
23728                 this.proxy.setStatus(status);
23729             }
23730
23731             if(this.afterDragOver){
23732                 /**
23733                  * An empty function by default, but provided so that you can perform a custom action
23734                  * while the dragged item is over the drop target by providing an implementation.
23735                  * @param {Roo.dd.DragDrop} target The drop target
23736                  * @param {Event} e The event object
23737                  * @param {String} id The id of the dragged element
23738                  * @method afterDragOver
23739                  */
23740                 this.afterDragOver(target, e, id);
23741             }
23742         }
23743     },
23744
23745     /**
23746      * An empty function by default, but provided so that you can perform a custom action
23747      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23748      * @param {Roo.dd.DragDrop} target The drop target
23749      * @param {Event} e The event object
23750      * @param {String} id The id of the dragged element
23751      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23752      */
23753     beforeDragOver : function(target, e, id){
23754         return true;
23755     },
23756
23757     // private
23758     onDragOut : function(e, id){
23759         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23760         if(this.beforeDragOut(target, e, id) !== false){
23761             if(target.isNotifyTarget){
23762                 target.notifyOut(this, e, this.dragData);
23763             }
23764             this.proxy.reset();
23765             if(this.afterDragOut){
23766                 /**
23767                  * An empty function by default, but provided so that you can perform a custom action
23768                  * after the dragged item is dragged out of the target without dropping.
23769                  * @param {Roo.dd.DragDrop} target The drop target
23770                  * @param {Event} e The event object
23771                  * @param {String} id The id of the dragged element
23772                  * @method afterDragOut
23773                  */
23774                 this.afterDragOut(target, e, id);
23775             }
23776         }
23777         this.cachedTarget = null;
23778     },
23779
23780     /**
23781      * An empty function by default, but provided so that you can perform a custom action before the dragged
23782      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23783      * @param {Roo.dd.DragDrop} target The drop target
23784      * @param {Event} e The event object
23785      * @param {String} id The id of the dragged element
23786      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23787      */
23788     beforeDragOut : function(target, e, id){
23789         return true;
23790     },
23791     
23792     // private
23793     onDragDrop : function(e, id){
23794         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23795         if(this.beforeDragDrop(target, e, id) !== false){
23796             if(target.isNotifyTarget){
23797                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23798                     this.onValidDrop(target, e, id);
23799                 }else{
23800                     this.onInvalidDrop(target, e, id);
23801                 }
23802             }else{
23803                 this.onValidDrop(target, e, id);
23804             }
23805             
23806             if(this.afterDragDrop){
23807                 /**
23808                  * An empty function by default, but provided so that you can perform a custom action
23809                  * after a valid drag drop has occurred by providing an implementation.
23810                  * @param {Roo.dd.DragDrop} target The drop target
23811                  * @param {Event} e The event object
23812                  * @param {String} id The id of the dropped element
23813                  * @method afterDragDrop
23814                  */
23815                 this.afterDragDrop(target, e, id);
23816             }
23817         }
23818         delete this.cachedTarget;
23819     },
23820
23821     /**
23822      * An empty function by default, but provided so that you can perform a custom action before the dragged
23823      * item is dropped onto the target and optionally cancel the onDragDrop.
23824      * @param {Roo.dd.DragDrop} target The drop target
23825      * @param {Event} e The event object
23826      * @param {String} id The id of the dragged element
23827      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23828      */
23829     beforeDragDrop : function(target, e, id){
23830         return true;
23831     },
23832
23833     // private
23834     onValidDrop : function(target, e, id){
23835         this.hideProxy();
23836         if(this.afterValidDrop){
23837             /**
23838              * An empty function by default, but provided so that you can perform a custom action
23839              * after a valid drop has occurred by providing an implementation.
23840              * @param {Object} target The target DD 
23841              * @param {Event} e The event object
23842              * @param {String} id The id of the dropped element
23843              * @method afterInvalidDrop
23844              */
23845             this.afterValidDrop(target, e, id);
23846         }
23847     },
23848
23849     // private
23850     getRepairXY : function(e, data){
23851         return this.el.getXY();  
23852     },
23853
23854     // private
23855     onInvalidDrop : function(target, e, id){
23856         this.beforeInvalidDrop(target, e, id);
23857         if(this.cachedTarget){
23858             if(this.cachedTarget.isNotifyTarget){
23859                 this.cachedTarget.notifyOut(this, e, this.dragData);
23860             }
23861             this.cacheTarget = null;
23862         }
23863         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23864
23865         if(this.afterInvalidDrop){
23866             /**
23867              * An empty function by default, but provided so that you can perform a custom action
23868              * after an invalid drop has occurred by providing an implementation.
23869              * @param {Event} e The event object
23870              * @param {String} id The id of the dropped element
23871              * @method afterInvalidDrop
23872              */
23873             this.afterInvalidDrop(e, id);
23874         }
23875     },
23876
23877     // private
23878     afterRepair : function(){
23879         if(Roo.enableFx){
23880             this.el.highlight(this.hlColor || "c3daf9");
23881         }
23882         this.dragging = false;
23883     },
23884
23885     /**
23886      * An empty function by default, but provided so that you can perform a custom action after an invalid
23887      * drop has occurred.
23888      * @param {Roo.dd.DragDrop} target The drop target
23889      * @param {Event} e The event object
23890      * @param {String} id The id of the dragged element
23891      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23892      */
23893     beforeInvalidDrop : function(target, e, id){
23894         return true;
23895     },
23896
23897     // private
23898     handleMouseDown : function(e){
23899         if(this.dragging) {
23900             return;
23901         }
23902         var data = this.getDragData(e);
23903         if(data && this.onBeforeDrag(data, e) !== false){
23904             this.dragData = data;
23905             this.proxy.stop();
23906             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23907         } 
23908     },
23909
23910     /**
23911      * An empty function by default, but provided so that you can perform a custom action before the initial
23912      * drag event begins and optionally cancel it.
23913      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23914      * @param {Event} e The event object
23915      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23916      */
23917     onBeforeDrag : function(data, e){
23918         return true;
23919     },
23920
23921     /**
23922      * An empty function by default, but provided so that you can perform a custom action once the initial
23923      * drag event has begun.  The drag cannot be canceled from this function.
23924      * @param {Number} x The x position of the click on the dragged object
23925      * @param {Number} y The y position of the click on the dragged object
23926      */
23927     onStartDrag : Roo.emptyFn,
23928
23929     // private - YUI override
23930     startDrag : function(x, y){
23931         this.proxy.reset();
23932         this.dragging = true;
23933         this.proxy.update("");
23934         this.onInitDrag(x, y);
23935         this.proxy.show();
23936     },
23937
23938     // private
23939     onInitDrag : function(x, y){
23940         var clone = this.el.dom.cloneNode(true);
23941         clone.id = Roo.id(); // prevent duplicate ids
23942         this.proxy.update(clone);
23943         this.onStartDrag(x, y);
23944         return true;
23945     },
23946
23947     /**
23948      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23949      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23950      */
23951     getProxy : function(){
23952         return this.proxy;  
23953     },
23954
23955     /**
23956      * Hides the drag source's {@link Roo.dd.StatusProxy}
23957      */
23958     hideProxy : function(){
23959         this.proxy.hide();  
23960         this.proxy.reset(true);
23961         this.dragging = false;
23962     },
23963
23964     // private
23965     triggerCacheRefresh : function(){
23966         Roo.dd.DDM.refreshCache(this.groups);
23967     },
23968
23969     // private - override to prevent hiding
23970     b4EndDrag: function(e) {
23971     },
23972
23973     // private - override to prevent moving
23974     endDrag : function(e){
23975         this.onEndDrag(this.dragData, e);
23976     },
23977
23978     // private
23979     onEndDrag : function(data, e){
23980     },
23981     
23982     // private - pin to cursor
23983     autoOffset : function(x, y) {
23984         this.setDelta(-12, -20);
23985     }    
23986 });/*
23987  * Based on:
23988  * Ext JS Library 1.1.1
23989  * Copyright(c) 2006-2007, Ext JS, LLC.
23990  *
23991  * Originally Released Under LGPL - original licence link has changed is not relivant.
23992  *
23993  * Fork - LGPL
23994  * <script type="text/javascript">
23995  */
23996
23997
23998 /**
23999  * @class Roo.dd.DropTarget
24000  * @extends Roo.dd.DDTarget
24001  * A simple class that provides the basic implementation needed to make any element a drop target that can have
24002  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
24003  * @constructor
24004  * @param {String/HTMLElement/Element} el The container element
24005  * @param {Object} config
24006  */
24007 Roo.dd.DropTarget = function(el, config){
24008     this.el = Roo.get(el);
24009     
24010     var listeners = false; ;
24011     if (config && config.listeners) {
24012         listeners= config.listeners;
24013         delete config.listeners;
24014     }
24015     Roo.apply(this, config);
24016     
24017     if(this.containerScroll){
24018         Roo.dd.ScrollManager.register(this.el);
24019     }
24020     this.addEvents( {
24021          /**
24022          * @scope Roo.dd.DropTarget
24023          */
24024          
24025          /**
24026          * @event enter
24027          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
24028          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
24029          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
24030          * 
24031          * IMPORTANT : it should set  this.valid to true|false
24032          * 
24033          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24034          * @param {Event} e The event
24035          * @param {Object} data An object containing arbitrary data supplied by the drag source
24036          */
24037         "enter" : true,
24038         
24039          /**
24040          * @event over
24041          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
24042          * This method will be called on every mouse movement while the drag source is over the drop target.
24043          * This default implementation simply returns the dropAllowed config value.
24044          * 
24045          * IMPORTANT : it should set  this.valid to true|false
24046          * 
24047          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24048          * @param {Event} e The event
24049          * @param {Object} data An object containing arbitrary data supplied by the drag source
24050          
24051          */
24052         "over" : true,
24053         /**
24054          * @event out
24055          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
24056          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
24057          * overClass (if any) from the drop element.
24058          * 
24059          * 
24060          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24061          * @param {Event} e The event
24062          * @param {Object} data An object containing arbitrary data supplied by the drag source
24063          */
24064          "out" : true,
24065          
24066         /**
24067          * @event drop
24068          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
24069          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
24070          * implementation that does something to process the drop event and returns true so that the drag source's
24071          * repair action does not run.
24072          * 
24073          * IMPORTANT : it should set this.success
24074          * 
24075          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24076          * @param {Event} e The event
24077          * @param {Object} data An object containing arbitrary data supplied by the drag source
24078         */
24079          "drop" : true
24080     });
24081             
24082      
24083     Roo.dd.DropTarget.superclass.constructor.call(  this, 
24084         this.el.dom, 
24085         this.ddGroup || this.group,
24086         {
24087             isTarget: true,
24088             listeners : listeners || {} 
24089            
24090         
24091         }
24092     );
24093
24094 };
24095
24096 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
24097     /**
24098      * @cfg {String} overClass
24099      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
24100      */
24101      /**
24102      * @cfg {String} ddGroup
24103      * The drag drop group to handle drop events for
24104      */
24105      
24106     /**
24107      * @cfg {String} dropAllowed
24108      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
24109      */
24110     dropAllowed : "x-dd-drop-ok",
24111     /**
24112      * @cfg {String} dropNotAllowed
24113      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
24114      */
24115     dropNotAllowed : "x-dd-drop-nodrop",
24116     /**
24117      * @cfg {boolean} success
24118      * set this after drop listener.. 
24119      */
24120     success : false,
24121     /**
24122      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
24123      * if the drop point is valid for over/enter..
24124      */
24125     valid : false,
24126     // private
24127     isTarget : true,
24128
24129     // private
24130     isNotifyTarget : true,
24131     
24132     /**
24133      * @hide
24134      */
24135     notifyEnter : function(dd, e, data)
24136     {
24137         this.valid = true;
24138         this.fireEvent('enter', dd, e, data);
24139         if(this.overClass){
24140             this.el.addClass(this.overClass);
24141         }
24142         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24143             this.valid ? this.dropAllowed : this.dropNotAllowed
24144         );
24145     },
24146
24147     /**
24148      * @hide
24149      */
24150     notifyOver : function(dd, e, data)
24151     {
24152         this.valid = true;
24153         this.fireEvent('over', dd, e, data);
24154         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24155             this.valid ? this.dropAllowed : this.dropNotAllowed
24156         );
24157     },
24158
24159     /**
24160      * @hide
24161      */
24162     notifyOut : function(dd, e, data)
24163     {
24164         this.fireEvent('out', dd, e, data);
24165         if(this.overClass){
24166             this.el.removeClass(this.overClass);
24167         }
24168     },
24169
24170     /**
24171      * @hide
24172      */
24173     notifyDrop : function(dd, e, data)
24174     {
24175         this.success = false;
24176         this.fireEvent('drop', dd, e, data);
24177         return this.success;
24178     }
24179 });/*
24180  * Based on:
24181  * Ext JS Library 1.1.1
24182  * Copyright(c) 2006-2007, Ext JS, LLC.
24183  *
24184  * Originally Released Under LGPL - original licence link has changed is not relivant.
24185  *
24186  * Fork - LGPL
24187  * <script type="text/javascript">
24188  */
24189
24190
24191 /**
24192  * @class Roo.dd.DragZone
24193  * @extends Roo.dd.DragSource
24194  * This class provides a container DD instance that proxies for multiple child node sources.<br />
24195  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24196  * @constructor
24197  * @param {String/HTMLElement/Element} el The container element
24198  * @param {Object} config
24199  */
24200 Roo.dd.DragZone = function(el, config){
24201     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24202     if(this.containerScroll){
24203         Roo.dd.ScrollManager.register(this.el);
24204     }
24205 };
24206
24207 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24208     /**
24209      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24210      * for auto scrolling during drag operations.
24211      */
24212     /**
24213      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24214      * method after a failed drop (defaults to "c3daf9" - light blue)
24215      */
24216
24217     /**
24218      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24219      * for a valid target to drag based on the mouse down. Override this method
24220      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24221      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24222      * @param {EventObject} e The mouse down event
24223      * @return {Object} The dragData
24224      */
24225     getDragData : function(e){
24226         return Roo.dd.Registry.getHandleFromEvent(e);
24227     },
24228     
24229     /**
24230      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24231      * this.dragData.ddel
24232      * @param {Number} x The x position of the click on the dragged object
24233      * @param {Number} y The y position of the click on the dragged object
24234      * @return {Boolean} true to continue the drag, false to cancel
24235      */
24236     onInitDrag : function(x, y){
24237         this.proxy.update(this.dragData.ddel.cloneNode(true));
24238         this.onStartDrag(x, y);
24239         return true;
24240     },
24241     
24242     /**
24243      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24244      */
24245     afterRepair : function(){
24246         if(Roo.enableFx){
24247             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24248         }
24249         this.dragging = false;
24250     },
24251
24252     /**
24253      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24254      * the XY of this.dragData.ddel
24255      * @param {EventObject} e The mouse up event
24256      * @return {Array} The xy location (e.g. [100, 200])
24257      */
24258     getRepairXY : function(e){
24259         return Roo.Element.fly(this.dragData.ddel).getXY();  
24260     }
24261 });/*
24262  * Based on:
24263  * Ext JS Library 1.1.1
24264  * Copyright(c) 2006-2007, Ext JS, LLC.
24265  *
24266  * Originally Released Under LGPL - original licence link has changed is not relivant.
24267  *
24268  * Fork - LGPL
24269  * <script type="text/javascript">
24270  */
24271 /**
24272  * @class Roo.dd.DropZone
24273  * @extends Roo.dd.DropTarget
24274  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24275  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24276  * @constructor
24277  * @param {String/HTMLElement/Element} el The container element
24278  * @param {Object} config
24279  */
24280 Roo.dd.DropZone = function(el, config){
24281     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24282 };
24283
24284 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24285     /**
24286      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24287      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24288      * provide your own custom lookup.
24289      * @param {Event} e The event
24290      * @return {Object} data The custom data
24291      */
24292     getTargetFromEvent : function(e){
24293         return Roo.dd.Registry.getTargetFromEvent(e);
24294     },
24295
24296     /**
24297      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24298      * that it has registered.  This method has no default implementation and should be overridden to provide
24299      * node-specific processing if necessary.
24300      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24301      * {@link #getTargetFromEvent} for this node)
24302      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24303      * @param {Event} e The event
24304      * @param {Object} data An object containing arbitrary data supplied by the drag source
24305      */
24306     onNodeEnter : function(n, dd, e, data){
24307         
24308     },
24309
24310     /**
24311      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24312      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24313      * overridden to provide the proper feedback.
24314      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24315      * {@link #getTargetFromEvent} for this node)
24316      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24317      * @param {Event} e The event
24318      * @param {Object} data An object containing arbitrary data supplied by the drag source
24319      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24320      * underlying {@link Roo.dd.StatusProxy} can be updated
24321      */
24322     onNodeOver : function(n, dd, e, data){
24323         return this.dropAllowed;
24324     },
24325
24326     /**
24327      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24328      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24329      * node-specific processing if necessary.
24330      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24331      * {@link #getTargetFromEvent} for this node)
24332      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24333      * @param {Event} e The event
24334      * @param {Object} data An object containing arbitrary data supplied by the drag source
24335      */
24336     onNodeOut : function(n, dd, e, data){
24337         
24338     },
24339
24340     /**
24341      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24342      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24343      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24344      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24345      * {@link #getTargetFromEvent} for this node)
24346      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24347      * @param {Event} e The event
24348      * @param {Object} data An object containing arbitrary data supplied by the drag source
24349      * @return {Boolean} True if the drop was valid, else false
24350      */
24351     onNodeDrop : function(n, dd, e, data){
24352         return false;
24353     },
24354
24355     /**
24356      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24357      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24358      * it should be overridden to provide the proper feedback if necessary.
24359      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24360      * @param {Event} e The event
24361      * @param {Object} data An object containing arbitrary data supplied by the drag source
24362      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24363      * underlying {@link Roo.dd.StatusProxy} can be updated
24364      */
24365     onContainerOver : function(dd, e, data){
24366         return this.dropNotAllowed;
24367     },
24368
24369     /**
24370      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24371      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24372      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24373      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24374      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24375      * @param {Event} e The event
24376      * @param {Object} data An object containing arbitrary data supplied by the drag source
24377      * @return {Boolean} True if the drop was valid, else false
24378      */
24379     onContainerDrop : function(dd, e, data){
24380         return false;
24381     },
24382
24383     /**
24384      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24385      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24386      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24387      * you should override this method and provide a custom implementation.
24388      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24389      * @param {Event} e The event
24390      * @param {Object} data An object containing arbitrary data supplied by the drag source
24391      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24392      * underlying {@link Roo.dd.StatusProxy} can be updated
24393      */
24394     notifyEnter : function(dd, e, data){
24395         return this.dropNotAllowed;
24396     },
24397
24398     /**
24399      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24400      * This method will be called on every mouse movement while the drag source is over the drop zone.
24401      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24402      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24403      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24404      * registered node, it will call {@link #onContainerOver}.
24405      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24406      * @param {Event} e The event
24407      * @param {Object} data An object containing arbitrary data supplied by the drag source
24408      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24409      * underlying {@link Roo.dd.StatusProxy} can be updated
24410      */
24411     notifyOver : function(dd, e, data){
24412         var n = this.getTargetFromEvent(e);
24413         if(!n){ // not over valid drop target
24414             if(this.lastOverNode){
24415                 this.onNodeOut(this.lastOverNode, dd, e, data);
24416                 this.lastOverNode = null;
24417             }
24418             return this.onContainerOver(dd, e, data);
24419         }
24420         if(this.lastOverNode != n){
24421             if(this.lastOverNode){
24422                 this.onNodeOut(this.lastOverNode, dd, e, data);
24423             }
24424             this.onNodeEnter(n, dd, e, data);
24425             this.lastOverNode = n;
24426         }
24427         return this.onNodeOver(n, dd, e, data);
24428     },
24429
24430     /**
24431      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24432      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24433      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24434      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24435      * @param {Event} e The event
24436      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24437      */
24438     notifyOut : function(dd, e, data){
24439         if(this.lastOverNode){
24440             this.onNodeOut(this.lastOverNode, dd, e, data);
24441             this.lastOverNode = null;
24442         }
24443     },
24444
24445     /**
24446      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24447      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24448      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24449      * otherwise it will call {@link #onContainerDrop}.
24450      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24451      * @param {Event} e The event
24452      * @param {Object} data An object containing arbitrary data supplied by the drag source
24453      * @return {Boolean} True if the drop was valid, else false
24454      */
24455     notifyDrop : function(dd, e, data){
24456         if(this.lastOverNode){
24457             this.onNodeOut(this.lastOverNode, dd, e, data);
24458             this.lastOverNode = null;
24459         }
24460         var n = this.getTargetFromEvent(e);
24461         return n ?
24462             this.onNodeDrop(n, dd, e, data) :
24463             this.onContainerDrop(dd, e, data);
24464     },
24465
24466     // private
24467     triggerCacheRefresh : function(){
24468         Roo.dd.DDM.refreshCache(this.groups);
24469     }  
24470 });/*
24471  * Based on:
24472  * Ext JS Library 1.1.1
24473  * Copyright(c) 2006-2007, Ext JS, LLC.
24474  *
24475  * Originally Released Under LGPL - original licence link has changed is not relivant.
24476  *
24477  * Fork - LGPL
24478  * <script type="text/javascript">
24479  */
24480
24481
24482 /**
24483  * @class Roo.data.SortTypes
24484  * @static
24485  * Defines the default sorting (casting?) comparison functions used when sorting data.
24486  */
24487 Roo.data.SortTypes = {
24488     /**
24489      * Default sort that does nothing
24490      * @param {Mixed} s The value being converted
24491      * @return {Mixed} The comparison value
24492      */
24493     none : function(s){
24494         return s;
24495     },
24496     
24497     /**
24498      * The regular expression used to strip tags
24499      * @type {RegExp}
24500      * @property
24501      */
24502     stripTagsRE : /<\/?[^>]+>/gi,
24503     
24504     /**
24505      * Strips all HTML tags to sort on text only
24506      * @param {Mixed} s The value being converted
24507      * @return {String} The comparison value
24508      */
24509     asText : function(s){
24510         return String(s).replace(this.stripTagsRE, "");
24511     },
24512     
24513     /**
24514      * Strips all HTML tags to sort on text only - Case insensitive
24515      * @param {Mixed} s The value being converted
24516      * @return {String} The comparison value
24517      */
24518     asUCText : function(s){
24519         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24520     },
24521     
24522     /**
24523      * Case insensitive string
24524      * @param {Mixed} s The value being converted
24525      * @return {String} The comparison value
24526      */
24527     asUCString : function(s) {
24528         return String(s).toUpperCase();
24529     },
24530     
24531     /**
24532      * Date sorting
24533      * @param {Mixed} s The value being converted
24534      * @return {Number} The comparison value
24535      */
24536     asDate : function(s) {
24537         if(!s){
24538             return 0;
24539         }
24540         if(s instanceof Date){
24541             return s.getTime();
24542         }
24543         return Date.parse(String(s));
24544     },
24545     
24546     /**
24547      * Float sorting
24548      * @param {Mixed} s The value being converted
24549      * @return {Float} The comparison value
24550      */
24551     asFloat : function(s) {
24552         var val = parseFloat(String(s).replace(/,/g, ""));
24553         if(isNaN(val)) {
24554             val = 0;
24555         }
24556         return val;
24557     },
24558     
24559     /**
24560      * Integer sorting
24561      * @param {Mixed} s The value being converted
24562      * @return {Number} The comparison value
24563      */
24564     asInt : function(s) {
24565         var val = parseInt(String(s).replace(/,/g, ""));
24566         if(isNaN(val)) {
24567             val = 0;
24568         }
24569         return val;
24570     }
24571 };/*
24572  * Based on:
24573  * Ext JS Library 1.1.1
24574  * Copyright(c) 2006-2007, Ext JS, LLC.
24575  *
24576  * Originally Released Under LGPL - original licence link has changed is not relivant.
24577  *
24578  * Fork - LGPL
24579  * <script type="text/javascript">
24580  */
24581
24582 /**
24583 * @class Roo.data.Record
24584  * Instances of this class encapsulate both record <em>definition</em> information, and record
24585  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24586  * to access Records cached in an {@link Roo.data.Store} object.<br>
24587  * <p>
24588  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24589  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24590  * objects.<br>
24591  * <p>
24592  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24593  * @constructor
24594  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24595  * {@link #create}. The parameters are the same.
24596  * @param {Array} data An associative Array of data values keyed by the field name.
24597  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24598  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24599  * not specified an integer id is generated.
24600  */
24601 Roo.data.Record = function(data, id){
24602     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24603     this.data = data;
24604 };
24605
24606 /**
24607  * Generate a constructor for a specific record layout.
24608  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24609  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24610  * Each field definition object may contain the following properties: <ul>
24611  * <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,
24612  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24613  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24614  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24615  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24616  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24617  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24618  * this may be omitted.</p></li>
24619  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24620  * <ul><li>auto (Default, implies no conversion)</li>
24621  * <li>string</li>
24622  * <li>int</li>
24623  * <li>float</li>
24624  * <li>boolean</li>
24625  * <li>date</li></ul></p></li>
24626  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24627  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24628  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24629  * by the Reader into an object that will be stored in the Record. It is passed the
24630  * following parameters:<ul>
24631  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24632  * </ul></p></li>
24633  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24634  * </ul>
24635  * <br>usage:<br><pre><code>
24636 var TopicRecord = Roo.data.Record.create(
24637     {name: 'title', mapping: 'topic_title'},
24638     {name: 'author', mapping: 'username'},
24639     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24640     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24641     {name: 'lastPoster', mapping: 'user2'},
24642     {name: 'excerpt', mapping: 'post_text'}
24643 );
24644
24645 var myNewRecord = new TopicRecord({
24646     title: 'Do my job please',
24647     author: 'noobie',
24648     totalPosts: 1,
24649     lastPost: new Date(),
24650     lastPoster: 'Animal',
24651     excerpt: 'No way dude!'
24652 });
24653 myStore.add(myNewRecord);
24654 </code></pre>
24655  * @method create
24656  * @static
24657  */
24658 Roo.data.Record.create = function(o){
24659     var f = function(){
24660         f.superclass.constructor.apply(this, arguments);
24661     };
24662     Roo.extend(f, Roo.data.Record);
24663     var p = f.prototype;
24664     p.fields = new Roo.util.MixedCollection(false, function(field){
24665         return field.name;
24666     });
24667     for(var i = 0, len = o.length; i < len; i++){
24668         p.fields.add(new Roo.data.Field(o[i]));
24669     }
24670     f.getField = function(name){
24671         return p.fields.get(name);  
24672     };
24673     return f;
24674 };
24675
24676 Roo.data.Record.AUTO_ID = 1000;
24677 Roo.data.Record.EDIT = 'edit';
24678 Roo.data.Record.REJECT = 'reject';
24679 Roo.data.Record.COMMIT = 'commit';
24680
24681 Roo.data.Record.prototype = {
24682     /**
24683      * Readonly flag - true if this record has been modified.
24684      * @type Boolean
24685      */
24686     dirty : false,
24687     editing : false,
24688     error: null,
24689     modified: null,
24690
24691     // private
24692     join : function(store){
24693         this.store = store;
24694     },
24695
24696     /**
24697      * Set the named field to the specified value.
24698      * @param {String} name The name of the field to set.
24699      * @param {Object} value The value to set the field to.
24700      */
24701     set : function(name, value){
24702         if(this.data[name] == value){
24703             return;
24704         }
24705         this.dirty = true;
24706         if(!this.modified){
24707             this.modified = {};
24708         }
24709         if(typeof this.modified[name] == 'undefined'){
24710             this.modified[name] = this.data[name];
24711         }
24712         this.data[name] = value;
24713         if(!this.editing && this.store){
24714             this.store.afterEdit(this);
24715         }       
24716     },
24717
24718     /**
24719      * Get the value of the named field.
24720      * @param {String} name The name of the field to get the value of.
24721      * @return {Object} The value of the field.
24722      */
24723     get : function(name){
24724         return this.data[name]; 
24725     },
24726
24727     // private
24728     beginEdit : function(){
24729         this.editing = true;
24730         this.modified = {}; 
24731     },
24732
24733     // private
24734     cancelEdit : function(){
24735         this.editing = false;
24736         delete this.modified;
24737     },
24738
24739     // private
24740     endEdit : function(){
24741         this.editing = false;
24742         if(this.dirty && this.store){
24743             this.store.afterEdit(this);
24744         }
24745     },
24746
24747     /**
24748      * Usually called by the {@link Roo.data.Store} which owns the Record.
24749      * Rejects all changes made to the Record since either creation, or the last commit operation.
24750      * Modified fields are reverted to their original values.
24751      * <p>
24752      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24753      * of reject operations.
24754      */
24755     reject : function(){
24756         var m = this.modified;
24757         for(var n in m){
24758             if(typeof m[n] != "function"){
24759                 this.data[n] = m[n];
24760             }
24761         }
24762         this.dirty = false;
24763         delete this.modified;
24764         this.editing = false;
24765         if(this.store){
24766             this.store.afterReject(this);
24767         }
24768     },
24769
24770     /**
24771      * Usually called by the {@link Roo.data.Store} which owns the Record.
24772      * Commits all changes made to the Record since either creation, or the last commit operation.
24773      * <p>
24774      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24775      * of commit operations.
24776      */
24777     commit : function(){
24778         this.dirty = false;
24779         delete this.modified;
24780         this.editing = false;
24781         if(this.store){
24782             this.store.afterCommit(this);
24783         }
24784     },
24785
24786     // private
24787     hasError : function(){
24788         return this.error != null;
24789     },
24790
24791     // private
24792     clearError : function(){
24793         this.error = null;
24794     },
24795
24796     /**
24797      * Creates a copy of this record.
24798      * @param {String} id (optional) A new record id if you don't want to use this record's id
24799      * @return {Record}
24800      */
24801     copy : function(newId) {
24802         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24803     }
24804 };/*
24805  * Based on:
24806  * Ext JS Library 1.1.1
24807  * Copyright(c) 2006-2007, Ext JS, LLC.
24808  *
24809  * Originally Released Under LGPL - original licence link has changed is not relivant.
24810  *
24811  * Fork - LGPL
24812  * <script type="text/javascript">
24813  */
24814
24815
24816
24817 /**
24818  * @class Roo.data.Store
24819  * @extends Roo.util.Observable
24820  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24821  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24822  * <p>
24823  * 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
24824  * has no knowledge of the format of the data returned by the Proxy.<br>
24825  * <p>
24826  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24827  * instances from the data object. These records are cached and made available through accessor functions.
24828  * @constructor
24829  * Creates a new Store.
24830  * @param {Object} config A config object containing the objects needed for the Store to access data,
24831  * and read the data into Records.
24832  */
24833 Roo.data.Store = function(config){
24834     this.data = new Roo.util.MixedCollection(false);
24835     this.data.getKey = function(o){
24836         return o.id;
24837     };
24838     this.baseParams = {};
24839     // private
24840     this.paramNames = {
24841         "start" : "start",
24842         "limit" : "limit",
24843         "sort" : "sort",
24844         "dir" : "dir",
24845         "multisort" : "_multisort"
24846     };
24847
24848     if(config && config.data){
24849         this.inlineData = config.data;
24850         delete config.data;
24851     }
24852
24853     Roo.apply(this, config);
24854     
24855     if(this.reader){ // reader passed
24856         this.reader = Roo.factory(this.reader, Roo.data);
24857         this.reader.xmodule = this.xmodule || false;
24858         if(!this.recordType){
24859             this.recordType = this.reader.recordType;
24860         }
24861         if(this.reader.onMetaChange){
24862             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24863         }
24864     }
24865
24866     if(this.recordType){
24867         this.fields = this.recordType.prototype.fields;
24868     }
24869     this.modified = [];
24870
24871     this.addEvents({
24872         /**
24873          * @event datachanged
24874          * Fires when the data cache has changed, and a widget which is using this Store
24875          * as a Record cache should refresh its view.
24876          * @param {Store} this
24877          */
24878         datachanged : true,
24879         /**
24880          * @event metachange
24881          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24882          * @param {Store} this
24883          * @param {Object} meta The JSON metadata
24884          */
24885         metachange : true,
24886         /**
24887          * @event add
24888          * Fires when Records have been added to the Store
24889          * @param {Store} this
24890          * @param {Roo.data.Record[]} records The array of Records added
24891          * @param {Number} index The index at which the record(s) were added
24892          */
24893         add : true,
24894         /**
24895          * @event remove
24896          * Fires when a Record has been removed from the Store
24897          * @param {Store} this
24898          * @param {Roo.data.Record} record The Record that was removed
24899          * @param {Number} index The index at which the record was removed
24900          */
24901         remove : true,
24902         /**
24903          * @event update
24904          * Fires when a Record has been updated
24905          * @param {Store} this
24906          * @param {Roo.data.Record} record The Record that was updated
24907          * @param {String} operation The update operation being performed.  Value may be one of:
24908          * <pre><code>
24909  Roo.data.Record.EDIT
24910  Roo.data.Record.REJECT
24911  Roo.data.Record.COMMIT
24912          * </code></pre>
24913          */
24914         update : true,
24915         /**
24916          * @event clear
24917          * Fires when the data cache has been cleared.
24918          * @param {Store} this
24919          */
24920         clear : true,
24921         /**
24922          * @event beforeload
24923          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24924          * the load action will be canceled.
24925          * @param {Store} this
24926          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24927          */
24928         beforeload : true,
24929         /**
24930          * @event beforeloadadd
24931          * Fires after a new set of Records has been loaded.
24932          * @param {Store} this
24933          * @param {Roo.data.Record[]} records The Records that were loaded
24934          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24935          */
24936         beforeloadadd : true,
24937         /**
24938          * @event load
24939          * Fires after a new set of Records has been loaded, before they are added to the store.
24940          * @param {Store} this
24941          * @param {Roo.data.Record[]} records The Records that were loaded
24942          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24943          * @params {Object} return from reader
24944          */
24945         load : true,
24946         /**
24947          * @event loadexception
24948          * Fires if an exception occurs in the Proxy during loading.
24949          * Called with the signature of the Proxy's "loadexception" event.
24950          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24951          * 
24952          * @param {Proxy} 
24953          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24954          * @param {Object} load options 
24955          * @param {Object} jsonData from your request (normally this contains the Exception)
24956          */
24957         loadexception : true
24958     });
24959     
24960     if(this.proxy){
24961         this.proxy = Roo.factory(this.proxy, Roo.data);
24962         this.proxy.xmodule = this.xmodule || false;
24963         this.relayEvents(this.proxy,  ["loadexception"]);
24964     }
24965     this.sortToggle = {};
24966     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24967
24968     Roo.data.Store.superclass.constructor.call(this);
24969
24970     if(this.inlineData){
24971         this.loadData(this.inlineData);
24972         delete this.inlineData;
24973     }
24974 };
24975
24976 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24977      /**
24978     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24979     * without a remote query - used by combo/forms at present.
24980     */
24981     
24982     /**
24983     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24984     */
24985     /**
24986     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24987     */
24988     /**
24989     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24990     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24991     */
24992     /**
24993     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24994     * on any HTTP request
24995     */
24996     /**
24997     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24998     */
24999     /**
25000     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
25001     */
25002     multiSort: false,
25003     /**
25004     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
25005     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
25006     */
25007     remoteSort : false,
25008
25009     /**
25010     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
25011      * loaded or when a record is removed. (defaults to false).
25012     */
25013     pruneModifiedRecords : false,
25014
25015     // private
25016     lastOptions : null,
25017
25018     /**
25019      * Add Records to the Store and fires the add event.
25020      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25021      */
25022     add : function(records){
25023         records = [].concat(records);
25024         for(var i = 0, len = records.length; i < len; i++){
25025             records[i].join(this);
25026         }
25027         var index = this.data.length;
25028         this.data.addAll(records);
25029         this.fireEvent("add", this, records, index);
25030     },
25031
25032     /**
25033      * Remove a Record from the Store and fires the remove event.
25034      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
25035      */
25036     remove : function(record){
25037         var index = this.data.indexOf(record);
25038         this.data.removeAt(index);
25039  
25040         if(this.pruneModifiedRecords){
25041             this.modified.remove(record);
25042         }
25043         this.fireEvent("remove", this, record, index);
25044     },
25045
25046     /**
25047      * Remove all Records from the Store and fires the clear event.
25048      */
25049     removeAll : function(){
25050         this.data.clear();
25051         if(this.pruneModifiedRecords){
25052             this.modified = [];
25053         }
25054         this.fireEvent("clear", this);
25055     },
25056
25057     /**
25058      * Inserts Records to the Store at the given index and fires the add event.
25059      * @param {Number} index The start index at which to insert the passed Records.
25060      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25061      */
25062     insert : function(index, records){
25063         records = [].concat(records);
25064         for(var i = 0, len = records.length; i < len; i++){
25065             this.data.insert(index, records[i]);
25066             records[i].join(this);
25067         }
25068         this.fireEvent("add", this, records, index);
25069     },
25070
25071     /**
25072      * Get the index within the cache of the passed Record.
25073      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
25074      * @return {Number} The index of the passed Record. Returns -1 if not found.
25075      */
25076     indexOf : function(record){
25077         return this.data.indexOf(record);
25078     },
25079
25080     /**
25081      * Get the index within the cache of the Record with the passed id.
25082      * @param {String} id The id of the Record to find.
25083      * @return {Number} The index of the Record. Returns -1 if not found.
25084      */
25085     indexOfId : function(id){
25086         return this.data.indexOfKey(id);
25087     },
25088
25089     /**
25090      * Get the Record with the specified id.
25091      * @param {String} id The id of the Record to find.
25092      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
25093      */
25094     getById : function(id){
25095         return this.data.key(id);
25096     },
25097
25098     /**
25099      * Get the Record at the specified index.
25100      * @param {Number} index The index of the Record to find.
25101      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
25102      */
25103     getAt : function(index){
25104         return this.data.itemAt(index);
25105     },
25106
25107     /**
25108      * Returns a range of Records between specified indices.
25109      * @param {Number} startIndex (optional) The starting index (defaults to 0)
25110      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
25111      * @return {Roo.data.Record[]} An array of Records
25112      */
25113     getRange : function(start, end){
25114         return this.data.getRange(start, end);
25115     },
25116
25117     // private
25118     storeOptions : function(o){
25119         o = Roo.apply({}, o);
25120         delete o.callback;
25121         delete o.scope;
25122         this.lastOptions = o;
25123     },
25124
25125     /**
25126      * Loads the Record cache from the configured Proxy using the configured Reader.
25127      * <p>
25128      * If using remote paging, then the first load call must specify the <em>start</em>
25129      * and <em>limit</em> properties in the options.params property to establish the initial
25130      * position within the dataset, and the number of Records to cache on each read from the Proxy.
25131      * <p>
25132      * <strong>It is important to note that for remote data sources, loading is asynchronous,
25133      * and this call will return before the new data has been loaded. Perform any post-processing
25134      * in a callback function, or in a "load" event handler.</strong>
25135      * <p>
25136      * @param {Object} options An object containing properties which control loading options:<ul>
25137      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
25138      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
25139      * <pre>
25140                 {
25141                     data : data,  // array of key=>value data like JsonReader
25142                     total : data.length,
25143                     success : true
25144                     
25145                 }
25146         </pre>
25147             }.</li>
25148      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
25149      * passed the following arguments:<ul>
25150      * <li>r : Roo.data.Record[]</li>
25151      * <li>options: Options object from the load call</li>
25152      * <li>success: Boolean success indicator</li></ul></li>
25153      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
25154      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
25155      * </ul>
25156      */
25157     load : function(options){
25158         options = options || {};
25159         if(this.fireEvent("beforeload", this, options) !== false){
25160             this.storeOptions(options);
25161             var p = Roo.apply(options.params || {}, this.baseParams);
25162             // if meta was not loaded from remote source.. try requesting it.
25163             if (!this.reader.metaFromRemote) {
25164                 p._requestMeta = 1;
25165             }
25166             if(this.sortInfo && this.remoteSort){
25167                 var pn = this.paramNames;
25168                 p[pn["sort"]] = this.sortInfo.field;
25169                 p[pn["dir"]] = this.sortInfo.direction;
25170             }
25171             if (this.multiSort) {
25172                 var pn = this.paramNames;
25173                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25174             }
25175             
25176             this.proxy.load(p, this.reader, this.loadRecords, this, options);
25177         }
25178     },
25179
25180     /**
25181      * Reloads the Record cache from the configured Proxy using the configured Reader and
25182      * the options from the last load operation performed.
25183      * @param {Object} options (optional) An object containing properties which may override the options
25184      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25185      * the most recently used options are reused).
25186      */
25187     reload : function(options){
25188         this.load(Roo.applyIf(options||{}, this.lastOptions));
25189     },
25190
25191     // private
25192     // Called as a callback by the Reader during a load operation.
25193     loadRecords : function(o, options, success){
25194          
25195         if(!o){
25196             if(success !== false){
25197                 this.fireEvent("load", this, [], options, o);
25198             }
25199             if(options.callback){
25200                 options.callback.call(options.scope || this, [], options, false);
25201             }
25202             return;
25203         }
25204         // if data returned failure - throw an exception.
25205         if (o.success === false) {
25206             // show a message if no listener is registered.
25207             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25208                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25209             }
25210             // loadmask wil be hooked into this..
25211             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25212             return;
25213         }
25214         var r = o.records, t = o.totalRecords || r.length;
25215         
25216         this.fireEvent("beforeloadadd", this, r, options, o);
25217         
25218         if(!options || options.add !== true){
25219             if(this.pruneModifiedRecords){
25220                 this.modified = [];
25221             }
25222             for(var i = 0, len = r.length; i < len; i++){
25223                 r[i].join(this);
25224             }
25225             if(this.snapshot){
25226                 this.data = this.snapshot;
25227                 delete this.snapshot;
25228             }
25229             this.data.clear();
25230             this.data.addAll(r);
25231             this.totalLength = t;
25232             this.applySort();
25233             this.fireEvent("datachanged", this);
25234         }else{
25235             this.totalLength = Math.max(t, this.data.length+r.length);
25236             this.add(r);
25237         }
25238         
25239         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25240                 
25241             var e = new Roo.data.Record({});
25242
25243             e.set(this.parent.displayField, this.parent.emptyTitle);
25244             e.set(this.parent.valueField, '');
25245
25246             this.insert(0, e);
25247         }
25248             
25249         this.fireEvent("load", this, r, options, o);
25250         if(options.callback){
25251             options.callback.call(options.scope || this, r, options, true);
25252         }
25253     },
25254
25255
25256     /**
25257      * Loads data from a passed data block. A Reader which understands the format of the data
25258      * must have been configured in the constructor.
25259      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25260      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25261      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25262      */
25263     loadData : function(o, append){
25264         var r = this.reader.readRecords(o);
25265         this.loadRecords(r, {add: append}, true);
25266     },
25267     
25268      /**
25269      * using 'cn' the nested child reader read the child array into it's child stores.
25270      * @param {Object} rec The record with a 'children array
25271      */
25272     loadDataFromChildren : function(rec)
25273     {
25274         this.loadData(this.reader.toLoadData(rec));
25275     },
25276     
25277
25278     /**
25279      * Gets the number of cached records.
25280      * <p>
25281      * <em>If using paging, this may not be the total size of the dataset. If the data object
25282      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25283      * the data set size</em>
25284      */
25285     getCount : function(){
25286         return this.data.length || 0;
25287     },
25288
25289     /**
25290      * Gets the total number of records in the dataset as returned by the server.
25291      * <p>
25292      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25293      * the dataset size</em>
25294      */
25295     getTotalCount : function(){
25296         return this.totalLength || 0;
25297     },
25298
25299     /**
25300      * Returns the sort state of the Store as an object with two properties:
25301      * <pre><code>
25302  field {String} The name of the field by which the Records are sorted
25303  direction {String} The sort order, "ASC" or "DESC"
25304      * </code></pre>
25305      */
25306     getSortState : function(){
25307         return this.sortInfo;
25308     },
25309
25310     // private
25311     applySort : function(){
25312         if(this.sortInfo && !this.remoteSort){
25313             var s = this.sortInfo, f = s.field;
25314             var st = this.fields.get(f).sortType;
25315             var fn = function(r1, r2){
25316                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25317                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25318             };
25319             this.data.sort(s.direction, fn);
25320             if(this.snapshot && this.snapshot != this.data){
25321                 this.snapshot.sort(s.direction, fn);
25322             }
25323         }
25324     },
25325
25326     /**
25327      * Sets the default sort column and order to be used by the next load operation.
25328      * @param {String} fieldName The name of the field to sort by.
25329      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25330      */
25331     setDefaultSort : function(field, dir){
25332         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25333     },
25334
25335     /**
25336      * Sort the Records.
25337      * If remote sorting is used, the sort is performed on the server, and the cache is
25338      * reloaded. If local sorting is used, the cache is sorted internally.
25339      * @param {String} fieldName The name of the field to sort by.
25340      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25341      */
25342     sort : function(fieldName, dir){
25343         var f = this.fields.get(fieldName);
25344         if(!dir){
25345             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25346             
25347             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25348                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25349             }else{
25350                 dir = f.sortDir;
25351             }
25352         }
25353         this.sortToggle[f.name] = dir;
25354         this.sortInfo = {field: f.name, direction: dir};
25355         if(!this.remoteSort){
25356             this.applySort();
25357             this.fireEvent("datachanged", this);
25358         }else{
25359             this.load(this.lastOptions);
25360         }
25361     },
25362
25363     /**
25364      * Calls the specified function for each of the Records in the cache.
25365      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25366      * Returning <em>false</em> aborts and exits the iteration.
25367      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25368      */
25369     each : function(fn, scope){
25370         this.data.each(fn, scope);
25371     },
25372
25373     /**
25374      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25375      * (e.g., during paging).
25376      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25377      */
25378     getModifiedRecords : function(){
25379         return this.modified;
25380     },
25381
25382     // private
25383     createFilterFn : function(property, value, anyMatch){
25384         if(!value.exec){ // not a regex
25385             value = String(value);
25386             if(value.length == 0){
25387                 return false;
25388             }
25389             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25390         }
25391         return function(r){
25392             return value.test(r.data[property]);
25393         };
25394     },
25395
25396     /**
25397      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25398      * @param {String} property A field on your records
25399      * @param {Number} start The record index to start at (defaults to 0)
25400      * @param {Number} end The last record index to include (defaults to length - 1)
25401      * @return {Number} The sum
25402      */
25403     sum : function(property, start, end){
25404         var rs = this.data.items, v = 0;
25405         start = start || 0;
25406         end = (end || end === 0) ? end : rs.length-1;
25407
25408         for(var i = start; i <= end; i++){
25409             v += (rs[i].data[property] || 0);
25410         }
25411         return v;
25412     },
25413
25414     /**
25415      * Filter the records by a specified property.
25416      * @param {String} field A field on your records
25417      * @param {String/RegExp} value Either a string that the field
25418      * should start with or a RegExp to test against the field
25419      * @param {Boolean} anyMatch True to match any part not just the beginning
25420      */
25421     filter : function(property, value, anyMatch){
25422         var fn = this.createFilterFn(property, value, anyMatch);
25423         return fn ? this.filterBy(fn) : this.clearFilter();
25424     },
25425
25426     /**
25427      * Filter by a function. The specified function will be called with each
25428      * record in this data source. If the function returns true the record is included,
25429      * otherwise it is filtered.
25430      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25431      * @param {Object} scope (optional) The scope of the function (defaults to this)
25432      */
25433     filterBy : function(fn, scope){
25434         this.snapshot = this.snapshot || this.data;
25435         this.data = this.queryBy(fn, scope||this);
25436         this.fireEvent("datachanged", this);
25437     },
25438
25439     /**
25440      * Query the records by a specified property.
25441      * @param {String} field A field on your records
25442      * @param {String/RegExp} value Either a string that the field
25443      * should start with or a RegExp to test against the field
25444      * @param {Boolean} anyMatch True to match any part not just the beginning
25445      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25446      */
25447     query : function(property, value, anyMatch){
25448         var fn = this.createFilterFn(property, value, anyMatch);
25449         return fn ? this.queryBy(fn) : this.data.clone();
25450     },
25451
25452     /**
25453      * Query by a function. The specified function will be called with each
25454      * record in this data source. If the function returns true the record is included
25455      * in the results.
25456      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25457      * @param {Object} scope (optional) The scope of the function (defaults to this)
25458       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25459      **/
25460     queryBy : function(fn, scope){
25461         var data = this.snapshot || this.data;
25462         return data.filterBy(fn, scope||this);
25463     },
25464
25465     /**
25466      * Collects unique values for a particular dataIndex from this store.
25467      * @param {String} dataIndex The property to collect
25468      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25469      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25470      * @return {Array} An array of the unique values
25471      **/
25472     collect : function(dataIndex, allowNull, bypassFilter){
25473         var d = (bypassFilter === true && this.snapshot) ?
25474                 this.snapshot.items : this.data.items;
25475         var v, sv, r = [], l = {};
25476         for(var i = 0, len = d.length; i < len; i++){
25477             v = d[i].data[dataIndex];
25478             sv = String(v);
25479             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25480                 l[sv] = true;
25481                 r[r.length] = v;
25482             }
25483         }
25484         return r;
25485     },
25486
25487     /**
25488      * Revert to a view of the Record cache with no filtering applied.
25489      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25490      */
25491     clearFilter : function(suppressEvent){
25492         if(this.snapshot && this.snapshot != this.data){
25493             this.data = this.snapshot;
25494             delete this.snapshot;
25495             if(suppressEvent !== true){
25496                 this.fireEvent("datachanged", this);
25497             }
25498         }
25499     },
25500
25501     // private
25502     afterEdit : function(record){
25503         if(this.modified.indexOf(record) == -1){
25504             this.modified.push(record);
25505         }
25506         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25507     },
25508     
25509     // private
25510     afterReject : function(record){
25511         this.modified.remove(record);
25512         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25513     },
25514
25515     // private
25516     afterCommit : function(record){
25517         this.modified.remove(record);
25518         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25519     },
25520
25521     /**
25522      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25523      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25524      */
25525     commitChanges : function(){
25526         var m = this.modified.slice(0);
25527         this.modified = [];
25528         for(var i = 0, len = m.length; i < len; i++){
25529             m[i].commit();
25530         }
25531     },
25532
25533     /**
25534      * Cancel outstanding changes on all changed records.
25535      */
25536     rejectChanges : function(){
25537         var m = this.modified.slice(0);
25538         this.modified = [];
25539         for(var i = 0, len = m.length; i < len; i++){
25540             m[i].reject();
25541         }
25542     },
25543
25544     onMetaChange : function(meta, rtype, o){
25545         this.recordType = rtype;
25546         this.fields = rtype.prototype.fields;
25547         delete this.snapshot;
25548         this.sortInfo = meta.sortInfo || this.sortInfo;
25549         this.modified = [];
25550         this.fireEvent('metachange', this, this.reader.meta);
25551     },
25552     
25553     moveIndex : function(data, type)
25554     {
25555         var index = this.indexOf(data);
25556         
25557         var newIndex = index + type;
25558         
25559         this.remove(data);
25560         
25561         this.insert(newIndex, data);
25562         
25563     }
25564 });/*
25565  * Based on:
25566  * Ext JS Library 1.1.1
25567  * Copyright(c) 2006-2007, Ext JS, LLC.
25568  *
25569  * Originally Released Under LGPL - original licence link has changed is not relivant.
25570  *
25571  * Fork - LGPL
25572  * <script type="text/javascript">
25573  */
25574
25575 /**
25576  * @class Roo.data.SimpleStore
25577  * @extends Roo.data.Store
25578  * Small helper class to make creating Stores from Array data easier.
25579  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25580  * @cfg {Array} fields An array of field definition objects, or field name strings.
25581  * @cfg {Object} an existing reader (eg. copied from another store)
25582  * @cfg {Array} data The multi-dimensional array of data
25583  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25584  * @cfg {Roo.data.Reader} reader  [not-required] 
25585  * @constructor
25586  * @param {Object} config
25587  */
25588 Roo.data.SimpleStore = function(config)
25589 {
25590     Roo.data.SimpleStore.superclass.constructor.call(this, {
25591         isLocal : true,
25592         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25593                 id: config.id
25594             },
25595             Roo.data.Record.create(config.fields)
25596         ),
25597         proxy : new Roo.data.MemoryProxy(config.data)
25598     });
25599     this.load();
25600 };
25601 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25602  * Based on:
25603  * Ext JS Library 1.1.1
25604  * Copyright(c) 2006-2007, Ext JS, LLC.
25605  *
25606  * Originally Released Under LGPL - original licence link has changed is not relivant.
25607  *
25608  * Fork - LGPL
25609  * <script type="text/javascript">
25610  */
25611
25612 /**
25613 /**
25614  * @extends Roo.data.Store
25615  * @class Roo.data.JsonStore
25616  * Small helper class to make creating Stores for JSON data easier. <br/>
25617 <pre><code>
25618 var store = new Roo.data.JsonStore({
25619     url: 'get-images.php',
25620     root: 'images',
25621     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25622 });
25623 </code></pre>
25624  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25625  * JsonReader and HttpProxy (unless inline data is provided).</b>
25626  * @cfg {Array} fields An array of field definition objects, or field name strings.
25627  * @constructor
25628  * @param {Object} config
25629  */
25630 Roo.data.JsonStore = function(c){
25631     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25632         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25633         reader: new Roo.data.JsonReader(c, c.fields)
25634     }));
25635 };
25636 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25637  * Based on:
25638  * Ext JS Library 1.1.1
25639  * Copyright(c) 2006-2007, Ext JS, LLC.
25640  *
25641  * Originally Released Under LGPL - original licence link has changed is not relivant.
25642  *
25643  * Fork - LGPL
25644  * <script type="text/javascript">
25645  */
25646
25647  
25648 Roo.data.Field = function(config){
25649     if(typeof config == "string"){
25650         config = {name: config};
25651     }
25652     Roo.apply(this, config);
25653     
25654     if(!this.type){
25655         this.type = "auto";
25656     }
25657     
25658     var st = Roo.data.SortTypes;
25659     // named sortTypes are supported, here we look them up
25660     if(typeof this.sortType == "string"){
25661         this.sortType = st[this.sortType];
25662     }
25663     
25664     // set default sortType for strings and dates
25665     if(!this.sortType){
25666         switch(this.type){
25667             case "string":
25668                 this.sortType = st.asUCString;
25669                 break;
25670             case "date":
25671                 this.sortType = st.asDate;
25672                 break;
25673             default:
25674                 this.sortType = st.none;
25675         }
25676     }
25677
25678     // define once
25679     var stripRe = /[\$,%]/g;
25680
25681     // prebuilt conversion function for this field, instead of
25682     // switching every time we're reading a value
25683     if(!this.convert){
25684         var cv, dateFormat = this.dateFormat;
25685         switch(this.type){
25686             case "":
25687             case "auto":
25688             case undefined:
25689                 cv = function(v){ return v; };
25690                 break;
25691             case "string":
25692                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25693                 break;
25694             case "int":
25695                 cv = function(v){
25696                     return v !== undefined && v !== null && v !== '' ?
25697                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25698                     };
25699                 break;
25700             case "float":
25701                 cv = function(v){
25702                     return v !== undefined && v !== null && v !== '' ?
25703                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25704                     };
25705                 break;
25706             case "bool":
25707             case "boolean":
25708                 cv = function(v){ return v === true || v === "true" || v == 1; };
25709                 break;
25710             case "date":
25711                 cv = function(v){
25712                     if(!v){
25713                         return '';
25714                     }
25715                     if(v instanceof Date){
25716                         return v;
25717                     }
25718                     if(dateFormat){
25719                         if(dateFormat == "timestamp"){
25720                             return new Date(v*1000);
25721                         }
25722                         return Date.parseDate(v, dateFormat);
25723                     }
25724                     var parsed = Date.parse(v);
25725                     return parsed ? new Date(parsed) : null;
25726                 };
25727              break;
25728             
25729         }
25730         this.convert = cv;
25731     }
25732 };
25733
25734 Roo.data.Field.prototype = {
25735     dateFormat: null,
25736     defaultValue: "",
25737     mapping: null,
25738     sortType : null,
25739     sortDir : "ASC"
25740 };/*
25741  * Based on:
25742  * Ext JS Library 1.1.1
25743  * Copyright(c) 2006-2007, Ext JS, LLC.
25744  *
25745  * Originally Released Under LGPL - original licence link has changed is not relivant.
25746  *
25747  * Fork - LGPL
25748  * <script type="text/javascript">
25749  */
25750  
25751 // Base class for reading structured data from a data source.  This class is intended to be
25752 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25753
25754 /**
25755  * @class Roo.data.DataReader
25756  * @abstract
25757  * Base class for reading structured data from a data source.  This class is intended to be
25758  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25759  */
25760
25761 Roo.data.DataReader = function(meta, recordType){
25762     
25763     this.meta = meta;
25764     
25765     this.recordType = recordType instanceof Array ? 
25766         Roo.data.Record.create(recordType) : recordType;
25767 };
25768
25769 Roo.data.DataReader.prototype = {
25770     
25771     
25772     readerType : 'Data',
25773      /**
25774      * Create an empty record
25775      * @param {Object} data (optional) - overlay some values
25776      * @return {Roo.data.Record} record created.
25777      */
25778     newRow :  function(d) {
25779         var da =  {};
25780         this.recordType.prototype.fields.each(function(c) {
25781             switch( c.type) {
25782                 case 'int' : da[c.name] = 0; break;
25783                 case 'date' : da[c.name] = new Date(); break;
25784                 case 'float' : da[c.name] = 0.0; break;
25785                 case 'boolean' : da[c.name] = false; break;
25786                 default : da[c.name] = ""; break;
25787             }
25788             
25789         });
25790         return new this.recordType(Roo.apply(da, d));
25791     }
25792     
25793     
25794 };/*
25795  * Based on:
25796  * Ext JS Library 1.1.1
25797  * Copyright(c) 2006-2007, Ext JS, LLC.
25798  *
25799  * Originally Released Under LGPL - original licence link has changed is not relivant.
25800  *
25801  * Fork - LGPL
25802  * <script type="text/javascript">
25803  */
25804
25805 /**
25806  * @class Roo.data.DataProxy
25807  * @extends Roo.util.Observable
25808  * @abstract
25809  * This class is an abstract base class for implementations which provide retrieval of
25810  * unformatted data objects.<br>
25811  * <p>
25812  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25813  * (of the appropriate type which knows how to parse the data object) to provide a block of
25814  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25815  * <p>
25816  * Custom implementations must implement the load method as described in
25817  * {@link Roo.data.HttpProxy#load}.
25818  */
25819 Roo.data.DataProxy = function(){
25820     this.addEvents({
25821         /**
25822          * @event beforeload
25823          * Fires before a network request is made to retrieve a data object.
25824          * @param {Object} This DataProxy object.
25825          * @param {Object} params The params parameter to the load function.
25826          */
25827         beforeload : true,
25828         /**
25829          * @event load
25830          * Fires before the load method's callback is called.
25831          * @param {Object} This DataProxy object.
25832          * @param {Object} o The data object.
25833          * @param {Object} arg The callback argument object passed to the load function.
25834          */
25835         load : true,
25836         /**
25837          * @event loadexception
25838          * Fires if an Exception occurs during data retrieval.
25839          * @param {Object} This DataProxy object.
25840          * @param {Object} o The data object.
25841          * @param {Object} arg The callback argument object passed to the load function.
25842          * @param {Object} e The Exception.
25843          */
25844         loadexception : true
25845     });
25846     Roo.data.DataProxy.superclass.constructor.call(this);
25847 };
25848
25849 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25850
25851     /**
25852      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25853      */
25854 /*
25855  * Based on:
25856  * Ext JS Library 1.1.1
25857  * Copyright(c) 2006-2007, Ext JS, LLC.
25858  *
25859  * Originally Released Under LGPL - original licence link has changed is not relivant.
25860  *
25861  * Fork - LGPL
25862  * <script type="text/javascript">
25863  */
25864 /**
25865  * @class Roo.data.MemoryProxy
25866  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25867  * to the Reader when its load method is called.
25868  * @constructor
25869  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25870  */
25871 Roo.data.MemoryProxy = function(data){
25872     if (data.data) {
25873         data = data.data;
25874     }
25875     Roo.data.MemoryProxy.superclass.constructor.call(this);
25876     this.data = data;
25877 };
25878
25879 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25880     
25881     /**
25882      * Load data from the requested source (in this case an in-memory
25883      * data object passed to the constructor), read the data object into
25884      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25885      * process that block using the passed callback.
25886      * @param {Object} params This parameter is not used by the MemoryProxy class.
25887      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25888      * object into a block of Roo.data.Records.
25889      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25890      * The function must be passed <ul>
25891      * <li>The Record block object</li>
25892      * <li>The "arg" argument from the load function</li>
25893      * <li>A boolean success indicator</li>
25894      * </ul>
25895      * @param {Object} scope The scope in which to call the callback
25896      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25897      */
25898     load : function(params, reader, callback, scope, arg){
25899         params = params || {};
25900         var result;
25901         try {
25902             result = reader.readRecords(params.data ? params.data :this.data);
25903         }catch(e){
25904             this.fireEvent("loadexception", this, arg, null, e);
25905             callback.call(scope, null, arg, false);
25906             return;
25907         }
25908         callback.call(scope, result, arg, true);
25909     },
25910     
25911     // private
25912     update : function(params, records){
25913         
25914     }
25915 });/*
25916  * Based on:
25917  * Ext JS Library 1.1.1
25918  * Copyright(c) 2006-2007, Ext JS, LLC.
25919  *
25920  * Originally Released Under LGPL - original licence link has changed is not relivant.
25921  *
25922  * Fork - LGPL
25923  * <script type="text/javascript">
25924  */
25925 /**
25926  * @class Roo.data.HttpProxy
25927  * @extends Roo.data.DataProxy
25928  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25929  * configured to reference a certain URL.<br><br>
25930  * <p>
25931  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25932  * from which the running page was served.<br><br>
25933  * <p>
25934  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25935  * <p>
25936  * Be aware that to enable the browser to parse an XML document, the server must set
25937  * the Content-Type header in the HTTP response to "text/xml".
25938  * @constructor
25939  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25940  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25941  * will be used to make the request.
25942  */
25943 Roo.data.HttpProxy = function(conn){
25944     Roo.data.HttpProxy.superclass.constructor.call(this);
25945     // is conn a conn config or a real conn?
25946     this.conn = conn;
25947     this.useAjax = !conn || !conn.events;
25948   
25949 };
25950
25951 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25952     // thse are take from connection...
25953     
25954     /**
25955      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25956      */
25957     /**
25958      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25959      * extra parameters to each request made by this object. (defaults to undefined)
25960      */
25961     /**
25962      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25963      *  to each request made by this object. (defaults to undefined)
25964      */
25965     /**
25966      * @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)
25967      */
25968     /**
25969      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25970      */
25971      /**
25972      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25973      * @type Boolean
25974      */
25975   
25976
25977     /**
25978      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25979      * @type Boolean
25980      */
25981     /**
25982      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25983      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25984      * a finer-grained basis than the DataProxy events.
25985      */
25986     getConnection : function(){
25987         return this.useAjax ? Roo.Ajax : this.conn;
25988     },
25989
25990     /**
25991      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25992      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25993      * process that block using the passed callback.
25994      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25995      * for the request to the remote server.
25996      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25997      * object into a block of Roo.data.Records.
25998      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25999      * The function must be passed <ul>
26000      * <li>The Record block object</li>
26001      * <li>The "arg" argument from the load function</li>
26002      * <li>A boolean success indicator</li>
26003      * </ul>
26004      * @param {Object} scope The scope in which to call the callback
26005      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26006      */
26007     load : function(params, reader, callback, scope, arg){
26008         if(this.fireEvent("beforeload", this, params) !== false){
26009             var  o = {
26010                 params : params || {},
26011                 request: {
26012                     callback : callback,
26013                     scope : scope,
26014                     arg : arg
26015                 },
26016                 reader: reader,
26017                 callback : this.loadResponse,
26018                 scope: this
26019             };
26020             if(this.useAjax){
26021                 Roo.applyIf(o, this.conn);
26022                 if(this.activeRequest){
26023                     Roo.Ajax.abort(this.activeRequest);
26024                 }
26025                 this.activeRequest = Roo.Ajax.request(o);
26026             }else{
26027                 this.conn.request(o);
26028             }
26029         }else{
26030             callback.call(scope||this, null, arg, false);
26031         }
26032     },
26033
26034     // private
26035     loadResponse : function(o, success, response){
26036         delete this.activeRequest;
26037         if(!success){
26038             this.fireEvent("loadexception", this, o, response);
26039             o.request.callback.call(o.request.scope, null, o.request.arg, false);
26040             return;
26041         }
26042         var result;
26043         try {
26044             result = o.reader.read(response);
26045         }catch(e){
26046             o.success = false;
26047             o.raw = { errorMsg : response.responseText };
26048             this.fireEvent("loadexception", this, o, response, e);
26049             o.request.callback.call(o.request.scope, o, o.request.arg, false);
26050             return;
26051         }
26052         
26053         this.fireEvent("load", this, o, o.request.arg);
26054         o.request.callback.call(o.request.scope, result, o.request.arg, true);
26055     },
26056
26057     // private
26058     update : function(dataSet){
26059
26060     },
26061
26062     // private
26063     updateResponse : function(dataSet){
26064
26065     }
26066 });/*
26067  * Based on:
26068  * Ext JS Library 1.1.1
26069  * Copyright(c) 2006-2007, Ext JS, LLC.
26070  *
26071  * Originally Released Under LGPL - original licence link has changed is not relivant.
26072  *
26073  * Fork - LGPL
26074  * <script type="text/javascript">
26075  */
26076
26077 /**
26078  * @class Roo.data.ScriptTagProxy
26079  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
26080  * other than the originating domain of the running page.<br><br>
26081  * <p>
26082  * <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
26083  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
26084  * <p>
26085  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
26086  * source code that is used as the source inside a &lt;script> tag.<br><br>
26087  * <p>
26088  * In order for the browser to process the returned data, the server must wrap the data object
26089  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
26090  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
26091  * depending on whether the callback name was passed:
26092  * <p>
26093  * <pre><code>
26094 boolean scriptTag = false;
26095 String cb = request.getParameter("callback");
26096 if (cb != null) {
26097     scriptTag = true;
26098     response.setContentType("text/javascript");
26099 } else {
26100     response.setContentType("application/x-json");
26101 }
26102 Writer out = response.getWriter();
26103 if (scriptTag) {
26104     out.write(cb + "(");
26105 }
26106 out.print(dataBlock.toJsonString());
26107 if (scriptTag) {
26108     out.write(");");
26109 }
26110 </pre></code>
26111  *
26112  * @constructor
26113  * @param {Object} config A configuration object.
26114  */
26115 Roo.data.ScriptTagProxy = function(config){
26116     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
26117     Roo.apply(this, config);
26118     this.head = document.getElementsByTagName("head")[0];
26119 };
26120
26121 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
26122
26123 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
26124     /**
26125      * @cfg {String} url The URL from which to request the data object.
26126      */
26127     /**
26128      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
26129      */
26130     timeout : 30000,
26131     /**
26132      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
26133      * the server the name of the callback function set up by the load call to process the returned data object.
26134      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
26135      * javascript output which calls this named function passing the data object as its only parameter.
26136      */
26137     callbackParam : "callback",
26138     /**
26139      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
26140      * name to the request.
26141      */
26142     nocache : true,
26143
26144     /**
26145      * Load data from the configured URL, read the data object into
26146      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
26147      * process that block using the passed callback.
26148      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26149      * for the request to the remote server.
26150      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26151      * object into a block of Roo.data.Records.
26152      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26153      * The function must be passed <ul>
26154      * <li>The Record block object</li>
26155      * <li>The "arg" argument from the load function</li>
26156      * <li>A boolean success indicator</li>
26157      * </ul>
26158      * @param {Object} scope The scope in which to call the callback
26159      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26160      */
26161     load : function(params, reader, callback, scope, arg){
26162         if(this.fireEvent("beforeload", this, params) !== false){
26163
26164             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26165
26166             var url = this.url;
26167             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26168             if(this.nocache){
26169                 url += "&_dc=" + (new Date().getTime());
26170             }
26171             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26172             var trans = {
26173                 id : transId,
26174                 cb : "stcCallback"+transId,
26175                 scriptId : "stcScript"+transId,
26176                 params : params,
26177                 arg : arg,
26178                 url : url,
26179                 callback : callback,
26180                 scope : scope,
26181                 reader : reader
26182             };
26183             var conn = this;
26184
26185             window[trans.cb] = function(o){
26186                 conn.handleResponse(o, trans);
26187             };
26188
26189             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26190
26191             if(this.autoAbort !== false){
26192                 this.abort();
26193             }
26194
26195             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26196
26197             var script = document.createElement("script");
26198             script.setAttribute("src", url);
26199             script.setAttribute("type", "text/javascript");
26200             script.setAttribute("id", trans.scriptId);
26201             this.head.appendChild(script);
26202
26203             this.trans = trans;
26204         }else{
26205             callback.call(scope||this, null, arg, false);
26206         }
26207     },
26208
26209     // private
26210     isLoading : function(){
26211         return this.trans ? true : false;
26212     },
26213
26214     /**
26215      * Abort the current server request.
26216      */
26217     abort : function(){
26218         if(this.isLoading()){
26219             this.destroyTrans(this.trans);
26220         }
26221     },
26222
26223     // private
26224     destroyTrans : function(trans, isLoaded){
26225         this.head.removeChild(document.getElementById(trans.scriptId));
26226         clearTimeout(trans.timeoutId);
26227         if(isLoaded){
26228             window[trans.cb] = undefined;
26229             try{
26230                 delete window[trans.cb];
26231             }catch(e){}
26232         }else{
26233             // if hasn't been loaded, wait for load to remove it to prevent script error
26234             window[trans.cb] = function(){
26235                 window[trans.cb] = undefined;
26236                 try{
26237                     delete window[trans.cb];
26238                 }catch(e){}
26239             };
26240         }
26241     },
26242
26243     // private
26244     handleResponse : function(o, trans){
26245         this.trans = false;
26246         this.destroyTrans(trans, true);
26247         var result;
26248         try {
26249             result = trans.reader.readRecords(o);
26250         }catch(e){
26251             this.fireEvent("loadexception", this, o, trans.arg, e);
26252             trans.callback.call(trans.scope||window, null, trans.arg, false);
26253             return;
26254         }
26255         this.fireEvent("load", this, o, trans.arg);
26256         trans.callback.call(trans.scope||window, result, trans.arg, true);
26257     },
26258
26259     // private
26260     handleFailure : function(trans){
26261         this.trans = false;
26262         this.destroyTrans(trans, false);
26263         this.fireEvent("loadexception", this, null, trans.arg);
26264         trans.callback.call(trans.scope||window, null, trans.arg, false);
26265     }
26266 });/*
26267  * Based on:
26268  * Ext JS Library 1.1.1
26269  * Copyright(c) 2006-2007, Ext JS, LLC.
26270  *
26271  * Originally Released Under LGPL - original licence link has changed is not relivant.
26272  *
26273  * Fork - LGPL
26274  * <script type="text/javascript">
26275  */
26276
26277 /**
26278  * @class Roo.data.JsonReader
26279  * @extends Roo.data.DataReader
26280  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26281  * based on mappings in a provided Roo.data.Record constructor.
26282  * 
26283  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26284  * in the reply previously. 
26285  * 
26286  * <p>
26287  * Example code:
26288  * <pre><code>
26289 var RecordDef = Roo.data.Record.create([
26290     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26291     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26292 ]);
26293 var myReader = new Roo.data.JsonReader({
26294     totalProperty: "results",    // The property which contains the total dataset size (optional)
26295     root: "rows",                // The property which contains an Array of row objects
26296     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26297 }, RecordDef);
26298 </code></pre>
26299  * <p>
26300  * This would consume a JSON file like this:
26301  * <pre><code>
26302 { 'results': 2, 'rows': [
26303     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26304     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26305 }
26306 </code></pre>
26307  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26308  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26309  * paged from the remote server.
26310  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26311  * @cfg {String} root name of the property which contains the Array of row objects.
26312  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26313  * @cfg {Array} fields Array of field definition objects
26314  * @constructor
26315  * Create a new JsonReader
26316  * @param {Object} meta Metadata configuration options
26317  * @param {Object} recordType Either an Array of field definition objects,
26318  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26319  */
26320 Roo.data.JsonReader = function(meta, recordType){
26321     
26322     meta = meta || {};
26323     // set some defaults:
26324     Roo.applyIf(meta, {
26325         totalProperty: 'total',
26326         successProperty : 'success',
26327         root : 'data',
26328         id : 'id'
26329     });
26330     
26331     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26332 };
26333 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26334     
26335     readerType : 'Json',
26336     
26337     /**
26338      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26339      * Used by Store query builder to append _requestMeta to params.
26340      * 
26341      */
26342     metaFromRemote : false,
26343     /**
26344      * This method is only used by a DataProxy which has retrieved data from a remote server.
26345      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26346      * @return {Object} data A data block which is used by an Roo.data.Store object as
26347      * a cache of Roo.data.Records.
26348      */
26349     read : function(response){
26350         var json = response.responseText;
26351        
26352         var o = /* eval:var:o */ eval("("+json+")");
26353         if(!o) {
26354             throw {message: "JsonReader.read: Json object not found"};
26355         }
26356         
26357         if(o.metaData){
26358             
26359             delete this.ef;
26360             this.metaFromRemote = true;
26361             this.meta = o.metaData;
26362             this.recordType = Roo.data.Record.create(o.metaData.fields);
26363             this.onMetaChange(this.meta, this.recordType, o);
26364         }
26365         return this.readRecords(o);
26366     },
26367
26368     // private function a store will implement
26369     onMetaChange : function(meta, recordType, o){
26370
26371     },
26372
26373     /**
26374          * @ignore
26375          */
26376     simpleAccess: function(obj, subsc) {
26377         return obj[subsc];
26378     },
26379
26380         /**
26381          * @ignore
26382          */
26383     getJsonAccessor: function(){
26384         var re = /[\[\.]/;
26385         return function(expr) {
26386             try {
26387                 return(re.test(expr))
26388                     ? new Function("obj", "return obj." + expr)
26389                     : function(obj){
26390                         return obj[expr];
26391                     };
26392             } catch(e){}
26393             return Roo.emptyFn;
26394         };
26395     }(),
26396
26397     /**
26398      * Create a data block containing Roo.data.Records from an XML document.
26399      * @param {Object} o An object which contains an Array of row objects in the property specified
26400      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26401      * which contains the total size of the dataset.
26402      * @return {Object} data A data block which is used by an Roo.data.Store object as
26403      * a cache of Roo.data.Records.
26404      */
26405     readRecords : function(o){
26406         /**
26407          * After any data loads, the raw JSON data is available for further custom processing.
26408          * @type Object
26409          */
26410         this.o = o;
26411         var s = this.meta, Record = this.recordType,
26412             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26413
26414 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26415         if (!this.ef) {
26416             if(s.totalProperty) {
26417                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26418                 }
26419                 if(s.successProperty) {
26420                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26421                 }
26422                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26423                 if (s.id) {
26424                         var g = this.getJsonAccessor(s.id);
26425                         this.getId = function(rec) {
26426                                 var r = g(rec);  
26427                                 return (r === undefined || r === "") ? null : r;
26428                         };
26429                 } else {
26430                         this.getId = function(){return null;};
26431                 }
26432             this.ef = [];
26433             for(var jj = 0; jj < fl; jj++){
26434                 f = fi[jj];
26435                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26436                 this.ef[jj] = this.getJsonAccessor(map);
26437             }
26438         }
26439
26440         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26441         if(s.totalProperty){
26442             var vt = parseInt(this.getTotal(o), 10);
26443             if(!isNaN(vt)){
26444                 totalRecords = vt;
26445             }
26446         }
26447         if(s.successProperty){
26448             var vs = this.getSuccess(o);
26449             if(vs === false || vs === 'false'){
26450                 success = false;
26451             }
26452         }
26453         var records = [];
26454         for(var i = 0; i < c; i++){
26455             var n = root[i];
26456             var values = {};
26457             var id = this.getId(n);
26458             for(var j = 0; j < fl; j++){
26459                 f = fi[j];
26460                                 var v = this.ef[j](n);
26461                                 if (!f.convert) {
26462                                         Roo.log('missing convert for ' + f.name);
26463                                         Roo.log(f);
26464                                         continue;
26465                                 }
26466                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26467             }
26468                         if (!Record) {
26469                                 return {
26470                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26471                                         success : false,
26472                                         records : [],
26473                                         totalRecords : 0
26474                                 };
26475                         }
26476             var record = new Record(values, id);
26477             record.json = n;
26478             records[i] = record;
26479         }
26480         return {
26481             raw : o,
26482             success : success,
26483             records : records,
26484             totalRecords : totalRecords
26485         };
26486     },
26487     // used when loading children.. @see loadDataFromChildren
26488     toLoadData: function(rec)
26489     {
26490         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26491         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26492         return { data : data, total : data.length };
26493         
26494     }
26495 });/*
26496  * Based on:
26497  * Ext JS Library 1.1.1
26498  * Copyright(c) 2006-2007, Ext JS, LLC.
26499  *
26500  * Originally Released Under LGPL - original licence link has changed is not relivant.
26501  *
26502  * Fork - LGPL
26503  * <script type="text/javascript">
26504  */
26505
26506 /**
26507  * @class Roo.data.XmlReader
26508  * @extends Roo.data.DataReader
26509  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26510  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26511  * <p>
26512  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26513  * header in the HTTP response must be set to "text/xml".</em>
26514  * <p>
26515  * Example code:
26516  * <pre><code>
26517 var RecordDef = Roo.data.Record.create([
26518    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26519    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26520 ]);
26521 var myReader = new Roo.data.XmlReader({
26522    totalRecords: "results", // The element which contains the total dataset size (optional)
26523    record: "row",           // The repeated element which contains row information
26524    id: "id"                 // The element within the row that provides an ID for the record (optional)
26525 }, RecordDef);
26526 </code></pre>
26527  * <p>
26528  * This would consume an XML file like this:
26529  * <pre><code>
26530 &lt;?xml?>
26531 &lt;dataset>
26532  &lt;results>2&lt;/results>
26533  &lt;row>
26534    &lt;id>1&lt;/id>
26535    &lt;name>Bill&lt;/name>
26536    &lt;occupation>Gardener&lt;/occupation>
26537  &lt;/row>
26538  &lt;row>
26539    &lt;id>2&lt;/id>
26540    &lt;name>Ben&lt;/name>
26541    &lt;occupation>Horticulturalist&lt;/occupation>
26542  &lt;/row>
26543 &lt;/dataset>
26544 </code></pre>
26545  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26546  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26547  * paged from the remote server.
26548  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26549  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26550  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26551  * a record identifier value.
26552  * @constructor
26553  * Create a new XmlReader
26554  * @param {Object} meta Metadata configuration options
26555  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26556  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26557  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26558  */
26559 Roo.data.XmlReader = function(meta, recordType){
26560     meta = meta || {};
26561     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26562 };
26563 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26564     
26565     readerType : 'Xml',
26566     
26567     /**
26568      * This method is only used by a DataProxy which has retrieved data from a remote server.
26569          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26570          * to contain a method called 'responseXML' that returns an XML document object.
26571      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26572      * a cache of Roo.data.Records.
26573      */
26574     read : function(response){
26575         var doc = response.responseXML;
26576         if(!doc) {
26577             throw {message: "XmlReader.read: XML Document not available"};
26578         }
26579         return this.readRecords(doc);
26580     },
26581
26582     /**
26583      * Create a data block containing Roo.data.Records from an XML document.
26584          * @param {Object} doc A parsed XML document.
26585      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26586      * a cache of Roo.data.Records.
26587      */
26588     readRecords : function(doc){
26589         /**
26590          * After any data loads/reads, the raw XML Document is available for further custom processing.
26591          * @type XMLDocument
26592          */
26593         this.xmlData = doc;
26594         var root = doc.documentElement || doc;
26595         var q = Roo.DomQuery;
26596         var recordType = this.recordType, fields = recordType.prototype.fields;
26597         var sid = this.meta.id;
26598         var totalRecords = 0, success = true;
26599         if(this.meta.totalRecords){
26600             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26601         }
26602         
26603         if(this.meta.success){
26604             var sv = q.selectValue(this.meta.success, root, true);
26605             success = sv !== false && sv !== 'false';
26606         }
26607         var records = [];
26608         var ns = q.select(this.meta.record, root);
26609         for(var i = 0, len = ns.length; i < len; i++) {
26610                 var n = ns[i];
26611                 var values = {};
26612                 var id = sid ? q.selectValue(sid, n) : undefined;
26613                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26614                     var f = fields.items[j];
26615                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26616                     v = f.convert(v);
26617                     values[f.name] = v;
26618                 }
26619                 var record = new recordType(values, id);
26620                 record.node = n;
26621                 records[records.length] = record;
26622             }
26623
26624             return {
26625                 success : success,
26626                 records : records,
26627                 totalRecords : totalRecords || records.length
26628             };
26629     }
26630 });/*
26631  * Based on:
26632  * Ext JS Library 1.1.1
26633  * Copyright(c) 2006-2007, Ext JS, LLC.
26634  *
26635  * Originally Released Under LGPL - original licence link has changed is not relivant.
26636  *
26637  * Fork - LGPL
26638  * <script type="text/javascript">
26639  */
26640
26641 /**
26642  * @class Roo.data.ArrayReader
26643  * @extends Roo.data.DataReader
26644  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26645  * Each element of that Array represents a row of data fields. The
26646  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26647  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26648  * <p>
26649  * Example code:.
26650  * <pre><code>
26651 var RecordDef = Roo.data.Record.create([
26652     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26653     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26654 ]);
26655 var myReader = new Roo.data.ArrayReader({
26656     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26657 }, RecordDef);
26658 </code></pre>
26659  * <p>
26660  * This would consume an Array like this:
26661  * <pre><code>
26662 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26663   </code></pre>
26664  
26665  * @constructor
26666  * Create a new JsonReader
26667  * @param {Object} meta Metadata configuration options.
26668  * @param {Object|Array} recordType Either an Array of field definition objects
26669  * 
26670  * @cfg {Array} fields Array of field definition objects
26671  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26672  * as specified to {@link Roo.data.Record#create},
26673  * or an {@link Roo.data.Record} object
26674  *
26675  * 
26676  * created using {@link Roo.data.Record#create}.
26677  */
26678 Roo.data.ArrayReader = function(meta, recordType)
26679 {    
26680     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26681 };
26682
26683 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26684     
26685       /**
26686      * Create a data block containing Roo.data.Records from an XML document.
26687      * @param {Object} o An Array of row objects which represents the dataset.
26688      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26689      * a cache of Roo.data.Records.
26690      */
26691     readRecords : function(o)
26692     {
26693         var sid = this.meta ? this.meta.id : null;
26694         var recordType = this.recordType, fields = recordType.prototype.fields;
26695         var records = [];
26696         var root = o;
26697         for(var i = 0; i < root.length; i++){
26698             var n = root[i];
26699             var values = {};
26700             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26701             for(var j = 0, jlen = fields.length; j < jlen; j++){
26702                 var f = fields.items[j];
26703                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26704                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26705                 v = f.convert(v);
26706                 values[f.name] = v;
26707             }
26708             var record = new recordType(values, id);
26709             record.json = n;
26710             records[records.length] = record;
26711         }
26712         return {
26713             records : records,
26714             totalRecords : records.length
26715         };
26716     },
26717     // used when loading children.. @see loadDataFromChildren
26718     toLoadData: function(rec)
26719     {
26720         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26721         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26722         
26723     }
26724     
26725     
26726 });/*
26727  * Based on:
26728  * Ext JS Library 1.1.1
26729  * Copyright(c) 2006-2007, Ext JS, LLC.
26730  *
26731  * Originally Released Under LGPL - original licence link has changed is not relivant.
26732  *
26733  * Fork - LGPL
26734  * <script type="text/javascript">
26735  */
26736
26737
26738 /**
26739  * @class Roo.data.Tree
26740  * @extends Roo.util.Observable
26741  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26742  * in the tree have most standard DOM functionality.
26743  * @constructor
26744  * @param {Node} root (optional) The root node
26745  */
26746 Roo.data.Tree = function(root){
26747    this.nodeHash = {};
26748    /**
26749     * The root node for this tree
26750     * @type Node
26751     */
26752    this.root = null;
26753    if(root){
26754        this.setRootNode(root);
26755    }
26756    this.addEvents({
26757        /**
26758         * @event append
26759         * Fires when a new child node is appended to a node in this tree.
26760         * @param {Tree} tree The owner tree
26761         * @param {Node} parent The parent node
26762         * @param {Node} node The newly appended node
26763         * @param {Number} index The index of the newly appended node
26764         */
26765        "append" : true,
26766        /**
26767         * @event remove
26768         * Fires when a child node is removed from a node in this tree.
26769         * @param {Tree} tree The owner tree
26770         * @param {Node} parent The parent node
26771         * @param {Node} node The child node removed
26772         */
26773        "remove" : true,
26774        /**
26775         * @event move
26776         * Fires when a node is moved to a new location in the tree
26777         * @param {Tree} tree The owner tree
26778         * @param {Node} node The node moved
26779         * @param {Node} oldParent The old parent of this node
26780         * @param {Node} newParent The new parent of this node
26781         * @param {Number} index The index it was moved to
26782         */
26783        "move" : true,
26784        /**
26785         * @event insert
26786         * Fires when a new child node is inserted in a node in this tree.
26787         * @param {Tree} tree The owner tree
26788         * @param {Node} parent The parent node
26789         * @param {Node} node The child node inserted
26790         * @param {Node} refNode The child node the node was inserted before
26791         */
26792        "insert" : true,
26793        /**
26794         * @event beforeappend
26795         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26796         * @param {Tree} tree The owner tree
26797         * @param {Node} parent The parent node
26798         * @param {Node} node The child node to be appended
26799         */
26800        "beforeappend" : true,
26801        /**
26802         * @event beforeremove
26803         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26804         * @param {Tree} tree The owner tree
26805         * @param {Node} parent The parent node
26806         * @param {Node} node The child node to be removed
26807         */
26808        "beforeremove" : true,
26809        /**
26810         * @event beforemove
26811         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26812         * @param {Tree} tree The owner tree
26813         * @param {Node} node The node being moved
26814         * @param {Node} oldParent The parent of the node
26815         * @param {Node} newParent The new parent the node is moving to
26816         * @param {Number} index The index it is being moved to
26817         */
26818        "beforemove" : true,
26819        /**
26820         * @event beforeinsert
26821         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26822         * @param {Tree} tree The owner tree
26823         * @param {Node} parent The parent node
26824         * @param {Node} node The child node to be inserted
26825         * @param {Node} refNode The child node the node is being inserted before
26826         */
26827        "beforeinsert" : true
26828    });
26829
26830     Roo.data.Tree.superclass.constructor.call(this);
26831 };
26832
26833 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26834     pathSeparator: "/",
26835
26836     proxyNodeEvent : function(){
26837         return this.fireEvent.apply(this, arguments);
26838     },
26839
26840     /**
26841      * Returns the root node for this tree.
26842      * @return {Node}
26843      */
26844     getRootNode : function(){
26845         return this.root;
26846     },
26847
26848     /**
26849      * Sets the root node for this tree.
26850      * @param {Node} node
26851      * @return {Node}
26852      */
26853     setRootNode : function(node){
26854         this.root = node;
26855         node.ownerTree = this;
26856         node.isRoot = true;
26857         this.registerNode(node);
26858         return node;
26859     },
26860
26861     /**
26862      * Gets a node in this tree by its id.
26863      * @param {String} id
26864      * @return {Node}
26865      */
26866     getNodeById : function(id){
26867         return this.nodeHash[id];
26868     },
26869
26870     registerNode : function(node){
26871         this.nodeHash[node.id] = node;
26872     },
26873
26874     unregisterNode : function(node){
26875         delete this.nodeHash[node.id];
26876     },
26877
26878     toString : function(){
26879         return "[Tree"+(this.id?" "+this.id:"")+"]";
26880     }
26881 });
26882
26883 /**
26884  * @class Roo.data.Node
26885  * @extends Roo.util.Observable
26886  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26887  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26888  * @constructor
26889  * @param {Object} attributes The attributes/config for the node
26890  */
26891 Roo.data.Node = function(attributes){
26892     /**
26893      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26894      * @type {Object}
26895      */
26896     this.attributes = attributes || {};
26897     this.leaf = this.attributes.leaf;
26898     /**
26899      * The node id. @type String
26900      */
26901     this.id = this.attributes.id;
26902     if(!this.id){
26903         this.id = Roo.id(null, "ynode-");
26904         this.attributes.id = this.id;
26905     }
26906      
26907     
26908     /**
26909      * All child nodes of this node. @type Array
26910      */
26911     this.childNodes = [];
26912     if(!this.childNodes.indexOf){ // indexOf is a must
26913         this.childNodes.indexOf = function(o){
26914             for(var i = 0, len = this.length; i < len; i++){
26915                 if(this[i] == o) {
26916                     return i;
26917                 }
26918             }
26919             return -1;
26920         };
26921     }
26922     /**
26923      * The parent node for this node. @type Node
26924      */
26925     this.parentNode = null;
26926     /**
26927      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26928      */
26929     this.firstChild = null;
26930     /**
26931      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26932      */
26933     this.lastChild = null;
26934     /**
26935      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26936      */
26937     this.previousSibling = null;
26938     /**
26939      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26940      */
26941     this.nextSibling = null;
26942
26943     this.addEvents({
26944        /**
26945         * @event append
26946         * Fires when a new child node is appended
26947         * @param {Tree} tree The owner tree
26948         * @param {Node} this This node
26949         * @param {Node} node The newly appended node
26950         * @param {Number} index The index of the newly appended node
26951         */
26952        "append" : true,
26953        /**
26954         * @event remove
26955         * Fires when a child node is removed
26956         * @param {Tree} tree The owner tree
26957         * @param {Node} this This node
26958         * @param {Node} node The removed node
26959         */
26960        "remove" : true,
26961        /**
26962         * @event move
26963         * Fires when this node is moved to a new location in the tree
26964         * @param {Tree} tree The owner tree
26965         * @param {Node} this This node
26966         * @param {Node} oldParent The old parent of this node
26967         * @param {Node} newParent The new parent of this node
26968         * @param {Number} index The index it was moved to
26969         */
26970        "move" : true,
26971        /**
26972         * @event insert
26973         * Fires when a new child node is inserted.
26974         * @param {Tree} tree The owner tree
26975         * @param {Node} this This node
26976         * @param {Node} node The child node inserted
26977         * @param {Node} refNode The child node the node was inserted before
26978         */
26979        "insert" : true,
26980        /**
26981         * @event beforeappend
26982         * Fires before a new child is appended, return false to cancel the append.
26983         * @param {Tree} tree The owner tree
26984         * @param {Node} this This node
26985         * @param {Node} node The child node to be appended
26986         */
26987        "beforeappend" : true,
26988        /**
26989         * @event beforeremove
26990         * Fires before a child is removed, return false to cancel the remove.
26991         * @param {Tree} tree The owner tree
26992         * @param {Node} this This node
26993         * @param {Node} node The child node to be removed
26994         */
26995        "beforeremove" : true,
26996        /**
26997         * @event beforemove
26998         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26999         * @param {Tree} tree The owner tree
27000         * @param {Node} this This node
27001         * @param {Node} oldParent The parent of this node
27002         * @param {Node} newParent The new parent this node is moving to
27003         * @param {Number} index The index it is being moved to
27004         */
27005        "beforemove" : true,
27006        /**
27007         * @event beforeinsert
27008         * Fires before a new child is inserted, return false to cancel the insert.
27009         * @param {Tree} tree The owner tree
27010         * @param {Node} this This node
27011         * @param {Node} node The child node to be inserted
27012         * @param {Node} refNode The child node the node is being inserted before
27013         */
27014        "beforeinsert" : true
27015    });
27016     this.listeners = this.attributes.listeners;
27017     Roo.data.Node.superclass.constructor.call(this);
27018 };
27019
27020 Roo.extend(Roo.data.Node, Roo.util.Observable, {
27021     fireEvent : function(evtName){
27022         // first do standard event for this node
27023         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
27024             return false;
27025         }
27026         // then bubble it up to the tree if the event wasn't cancelled
27027         var ot = this.getOwnerTree();
27028         if(ot){
27029             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
27030                 return false;
27031             }
27032         }
27033         return true;
27034     },
27035
27036     /**
27037      * Returns true if this node is a leaf
27038      * @return {Boolean}
27039      */
27040     isLeaf : function(){
27041         return this.leaf === true;
27042     },
27043
27044     // private
27045     setFirstChild : function(node){
27046         this.firstChild = node;
27047     },
27048
27049     //private
27050     setLastChild : function(node){
27051         this.lastChild = node;
27052     },
27053
27054
27055     /**
27056      * Returns true if this node is the last child of its parent
27057      * @return {Boolean}
27058      */
27059     isLast : function(){
27060        return (!this.parentNode ? true : this.parentNode.lastChild == this);
27061     },
27062
27063     /**
27064      * Returns true if this node is the first child of its parent
27065      * @return {Boolean}
27066      */
27067     isFirst : function(){
27068        return (!this.parentNode ? true : this.parentNode.firstChild == this);
27069     },
27070
27071     hasChildNodes : function(){
27072         return !this.isLeaf() && this.childNodes.length > 0;
27073     },
27074
27075     /**
27076      * Insert node(s) as the last child node of this node.
27077      * @param {Node/Array} node The node or Array of nodes to append
27078      * @return {Node} The appended node if single append, or null if an array was passed
27079      */
27080     appendChild : function(node){
27081         var multi = false;
27082         if(node instanceof Array){
27083             multi = node;
27084         }else if(arguments.length > 1){
27085             multi = arguments;
27086         }
27087         
27088         // if passed an array or multiple args do them one by one
27089         if(multi){
27090             for(var i = 0, len = multi.length; i < len; i++) {
27091                 this.appendChild(multi[i]);
27092             }
27093         }else{
27094             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
27095                 return false;
27096             }
27097             var index = this.childNodes.length;
27098             var oldParent = node.parentNode;
27099             // it's a move, make sure we move it cleanly
27100             if(oldParent){
27101                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
27102                     return false;
27103                 }
27104                 oldParent.removeChild(node);
27105             }
27106             
27107             index = this.childNodes.length;
27108             if(index == 0){
27109                 this.setFirstChild(node);
27110             }
27111             this.childNodes.push(node);
27112             node.parentNode = this;
27113             var ps = this.childNodes[index-1];
27114             if(ps){
27115                 node.previousSibling = ps;
27116                 ps.nextSibling = node;
27117             }else{
27118                 node.previousSibling = null;
27119             }
27120             node.nextSibling = null;
27121             this.setLastChild(node);
27122             node.setOwnerTree(this.getOwnerTree());
27123             this.fireEvent("append", this.ownerTree, this, node, index);
27124             if(this.ownerTree) {
27125                 this.ownerTree.fireEvent("appendnode", this, node, index);
27126             }
27127             if(oldParent){
27128                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
27129             }
27130             return node;
27131         }
27132     },
27133
27134     /**
27135      * Removes a child node from this node.
27136      * @param {Node} node The node to remove
27137      * @return {Node} The removed node
27138      */
27139     removeChild : function(node){
27140         var index = this.childNodes.indexOf(node);
27141         if(index == -1){
27142             return false;
27143         }
27144         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
27145             return false;
27146         }
27147
27148         // remove it from childNodes collection
27149         this.childNodes.splice(index, 1);
27150
27151         // update siblings
27152         if(node.previousSibling){
27153             node.previousSibling.nextSibling = node.nextSibling;
27154         }
27155         if(node.nextSibling){
27156             node.nextSibling.previousSibling = node.previousSibling;
27157         }
27158
27159         // update child refs
27160         if(this.firstChild == node){
27161             this.setFirstChild(node.nextSibling);
27162         }
27163         if(this.lastChild == node){
27164             this.setLastChild(node.previousSibling);
27165         }
27166
27167         node.setOwnerTree(null);
27168         // clear any references from the node
27169         node.parentNode = null;
27170         node.previousSibling = null;
27171         node.nextSibling = null;
27172         this.fireEvent("remove", this.ownerTree, this, node);
27173         return node;
27174     },
27175
27176     /**
27177      * Inserts the first node before the second node in this nodes childNodes collection.
27178      * @param {Node} node The node to insert
27179      * @param {Node} refNode The node to insert before (if null the node is appended)
27180      * @return {Node} The inserted node
27181      */
27182     insertBefore : function(node, refNode){
27183         if(!refNode){ // like standard Dom, refNode can be null for append
27184             return this.appendChild(node);
27185         }
27186         // nothing to do
27187         if(node == refNode){
27188             return false;
27189         }
27190
27191         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27192             return false;
27193         }
27194         var index = this.childNodes.indexOf(refNode);
27195         var oldParent = node.parentNode;
27196         var refIndex = index;
27197
27198         // when moving internally, indexes will change after remove
27199         if(oldParent == this && this.childNodes.indexOf(node) < index){
27200             refIndex--;
27201         }
27202
27203         // it's a move, make sure we move it cleanly
27204         if(oldParent){
27205             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27206                 return false;
27207             }
27208             oldParent.removeChild(node);
27209         }
27210         if(refIndex == 0){
27211             this.setFirstChild(node);
27212         }
27213         this.childNodes.splice(refIndex, 0, node);
27214         node.parentNode = this;
27215         var ps = this.childNodes[refIndex-1];
27216         if(ps){
27217             node.previousSibling = ps;
27218             ps.nextSibling = node;
27219         }else{
27220             node.previousSibling = null;
27221         }
27222         node.nextSibling = refNode;
27223         refNode.previousSibling = node;
27224         node.setOwnerTree(this.getOwnerTree());
27225         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27226         if(oldParent){
27227             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27228         }
27229         return node;
27230     },
27231
27232     /**
27233      * Returns the child node at the specified index.
27234      * @param {Number} index
27235      * @return {Node}
27236      */
27237     item : function(index){
27238         return this.childNodes[index];
27239     },
27240
27241     /**
27242      * Replaces one child node in this node with another.
27243      * @param {Node} newChild The replacement node
27244      * @param {Node} oldChild The node to replace
27245      * @return {Node} The replaced node
27246      */
27247     replaceChild : function(newChild, oldChild){
27248         this.insertBefore(newChild, oldChild);
27249         this.removeChild(oldChild);
27250         return oldChild;
27251     },
27252
27253     /**
27254      * Returns the index of a child node
27255      * @param {Node} node
27256      * @return {Number} The index of the node or -1 if it was not found
27257      */
27258     indexOf : function(child){
27259         return this.childNodes.indexOf(child);
27260     },
27261
27262     /**
27263      * Returns the tree this node is in.
27264      * @return {Tree}
27265      */
27266     getOwnerTree : function(){
27267         // if it doesn't have one, look for one
27268         if(!this.ownerTree){
27269             var p = this;
27270             while(p){
27271                 if(p.ownerTree){
27272                     this.ownerTree = p.ownerTree;
27273                     break;
27274                 }
27275                 p = p.parentNode;
27276             }
27277         }
27278         return this.ownerTree;
27279     },
27280
27281     /**
27282      * Returns depth of this node (the root node has a depth of 0)
27283      * @return {Number}
27284      */
27285     getDepth : function(){
27286         var depth = 0;
27287         var p = this;
27288         while(p.parentNode){
27289             ++depth;
27290             p = p.parentNode;
27291         }
27292         return depth;
27293     },
27294
27295     // private
27296     setOwnerTree : function(tree){
27297         // if it's move, we need to update everyone
27298         if(tree != this.ownerTree){
27299             if(this.ownerTree){
27300                 this.ownerTree.unregisterNode(this);
27301             }
27302             this.ownerTree = tree;
27303             var cs = this.childNodes;
27304             for(var i = 0, len = cs.length; i < len; i++) {
27305                 cs[i].setOwnerTree(tree);
27306             }
27307             if(tree){
27308                 tree.registerNode(this);
27309             }
27310         }
27311     },
27312
27313     /**
27314      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27315      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27316      * @return {String} The path
27317      */
27318     getPath : function(attr){
27319         attr = attr || "id";
27320         var p = this.parentNode;
27321         var b = [this.attributes[attr]];
27322         while(p){
27323             b.unshift(p.attributes[attr]);
27324             p = p.parentNode;
27325         }
27326         var sep = this.getOwnerTree().pathSeparator;
27327         return sep + b.join(sep);
27328     },
27329
27330     /**
27331      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27332      * function call will be the scope provided or the current node. The arguments to the function
27333      * will be the args provided or the current node. If the function returns false at any point,
27334      * the bubble is stopped.
27335      * @param {Function} fn The function to call
27336      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27337      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27338      */
27339     bubble : function(fn, scope, args){
27340         var p = this;
27341         while(p){
27342             if(fn.call(scope || p, args || p) === false){
27343                 break;
27344             }
27345             p = p.parentNode;
27346         }
27347     },
27348
27349     /**
27350      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27351      * function call will be the scope provided or the current node. The arguments to the function
27352      * will be the args provided or the current node. If the function returns false at any point,
27353      * the cascade is stopped on that branch.
27354      * @param {Function} fn The function to call
27355      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27356      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27357      */
27358     cascade : function(fn, scope, args){
27359         if(fn.call(scope || this, args || this) !== false){
27360             var cs = this.childNodes;
27361             for(var i = 0, len = cs.length; i < len; i++) {
27362                 cs[i].cascade(fn, scope, args);
27363             }
27364         }
27365     },
27366
27367     /**
27368      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27369      * function call will be the scope provided or the current node. The arguments to the function
27370      * will be the args provided or the current node. If the function returns false at any point,
27371      * the iteration stops.
27372      * @param {Function} fn The function to call
27373      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27374      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27375      */
27376     eachChild : function(fn, scope, args){
27377         var cs = this.childNodes;
27378         for(var i = 0, len = cs.length; i < len; i++) {
27379                 if(fn.call(scope || this, args || cs[i]) === false){
27380                     break;
27381                 }
27382         }
27383     },
27384
27385     /**
27386      * Finds the first child that has the attribute with the specified value.
27387      * @param {String} attribute The attribute name
27388      * @param {Mixed} value The value to search for
27389      * @return {Node} The found child or null if none was found
27390      */
27391     findChild : function(attribute, value){
27392         var cs = this.childNodes;
27393         for(var i = 0, len = cs.length; i < len; i++) {
27394                 if(cs[i].attributes[attribute] == value){
27395                     return cs[i];
27396                 }
27397         }
27398         return null;
27399     },
27400
27401     /**
27402      * Finds the first child by a custom function. The child matches if the function passed
27403      * returns true.
27404      * @param {Function} fn
27405      * @param {Object} scope (optional)
27406      * @return {Node} The found child or null if none was found
27407      */
27408     findChildBy : function(fn, scope){
27409         var cs = this.childNodes;
27410         for(var i = 0, len = cs.length; i < len; i++) {
27411                 if(fn.call(scope||cs[i], cs[i]) === true){
27412                     return cs[i];
27413                 }
27414         }
27415         return null;
27416     },
27417
27418     /**
27419      * Sorts this nodes children using the supplied sort function
27420      * @param {Function} fn
27421      * @param {Object} scope (optional)
27422      */
27423     sort : function(fn, scope){
27424         var cs = this.childNodes;
27425         var len = cs.length;
27426         if(len > 0){
27427             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27428             cs.sort(sortFn);
27429             for(var i = 0; i < len; i++){
27430                 var n = cs[i];
27431                 n.previousSibling = cs[i-1];
27432                 n.nextSibling = cs[i+1];
27433                 if(i == 0){
27434                     this.setFirstChild(n);
27435                 }
27436                 if(i == len-1){
27437                     this.setLastChild(n);
27438                 }
27439             }
27440         }
27441     },
27442
27443     /**
27444      * Returns true if this node is an ancestor (at any point) of the passed node.
27445      * @param {Node} node
27446      * @return {Boolean}
27447      */
27448     contains : function(node){
27449         return node.isAncestor(this);
27450     },
27451
27452     /**
27453      * Returns true if the passed node is an ancestor (at any point) of this node.
27454      * @param {Node} node
27455      * @return {Boolean}
27456      */
27457     isAncestor : function(node){
27458         var p = this.parentNode;
27459         while(p){
27460             if(p == node){
27461                 return true;
27462             }
27463             p = p.parentNode;
27464         }
27465         return false;
27466     },
27467
27468     toString : function(){
27469         return "[Node"+(this.id?" "+this.id:"")+"]";
27470     }
27471 });/*
27472  * Based on:
27473  * Ext JS Library 1.1.1
27474  * Copyright(c) 2006-2007, Ext JS, LLC.
27475  *
27476  * Originally Released Under LGPL - original licence link has changed is not relivant.
27477  *
27478  * Fork - LGPL
27479  * <script type="text/javascript">
27480  */
27481
27482
27483 /**
27484  * @class Roo.Shadow
27485  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27486  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27487  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27488  * @constructor
27489  * Create a new Shadow
27490  * @param {Object} config The config object
27491  */
27492 Roo.Shadow = function(config){
27493     Roo.apply(this, config);
27494     if(typeof this.mode != "string"){
27495         this.mode = this.defaultMode;
27496     }
27497     var o = this.offset, a = {h: 0};
27498     var rad = Math.floor(this.offset/2);
27499     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27500         case "drop":
27501             a.w = 0;
27502             a.l = a.t = o;
27503             a.t -= 1;
27504             if(Roo.isIE){
27505                 a.l -= this.offset + rad;
27506                 a.t -= this.offset + rad;
27507                 a.w -= rad;
27508                 a.h -= rad;
27509                 a.t += 1;
27510             }
27511         break;
27512         case "sides":
27513             a.w = (o*2);
27514             a.l = -o;
27515             a.t = o-1;
27516             if(Roo.isIE){
27517                 a.l -= (this.offset - rad);
27518                 a.t -= this.offset + rad;
27519                 a.l += 1;
27520                 a.w -= (this.offset - rad)*2;
27521                 a.w -= rad + 1;
27522                 a.h -= 1;
27523             }
27524         break;
27525         case "frame":
27526             a.w = a.h = (o*2);
27527             a.l = a.t = -o;
27528             a.t += 1;
27529             a.h -= 2;
27530             if(Roo.isIE){
27531                 a.l -= (this.offset - rad);
27532                 a.t -= (this.offset - rad);
27533                 a.l += 1;
27534                 a.w -= (this.offset + rad + 1);
27535                 a.h -= (this.offset + rad);
27536                 a.h += 1;
27537             }
27538         break;
27539     };
27540
27541     this.adjusts = a;
27542 };
27543
27544 Roo.Shadow.prototype = {
27545     /**
27546      * @cfg {String} mode
27547      * The shadow display mode.  Supports the following options:<br />
27548      * sides: Shadow displays on both sides and bottom only<br />
27549      * frame: Shadow displays equally on all four sides<br />
27550      * drop: Traditional bottom-right drop shadow (default)
27551      */
27552     mode: false,
27553     /**
27554      * @cfg {String} offset
27555      * The number of pixels to offset the shadow from the element (defaults to 4)
27556      */
27557     offset: 4,
27558
27559     // private
27560     defaultMode: "drop",
27561
27562     /**
27563      * Displays the shadow under the target element
27564      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27565      */
27566     show : function(target){
27567         target = Roo.get(target);
27568         if(!this.el){
27569             this.el = Roo.Shadow.Pool.pull();
27570             if(this.el.dom.nextSibling != target.dom){
27571                 this.el.insertBefore(target);
27572             }
27573         }
27574         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27575         if(Roo.isIE){
27576             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27577         }
27578         this.realign(
27579             target.getLeft(true),
27580             target.getTop(true),
27581             target.getWidth(),
27582             target.getHeight()
27583         );
27584         this.el.dom.style.display = "block";
27585     },
27586
27587     /**
27588      * Returns true if the shadow is visible, else false
27589      */
27590     isVisible : function(){
27591         return this.el ? true : false;  
27592     },
27593
27594     /**
27595      * Direct alignment when values are already available. Show must be called at least once before
27596      * calling this method to ensure it is initialized.
27597      * @param {Number} left The target element left position
27598      * @param {Number} top The target element top position
27599      * @param {Number} width The target element width
27600      * @param {Number} height The target element height
27601      */
27602     realign : function(l, t, w, h){
27603         if(!this.el){
27604             return;
27605         }
27606         var a = this.adjusts, d = this.el.dom, s = d.style;
27607         var iea = 0;
27608         s.left = (l+a.l)+"px";
27609         s.top = (t+a.t)+"px";
27610         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27611  
27612         if(s.width != sws || s.height != shs){
27613             s.width = sws;
27614             s.height = shs;
27615             if(!Roo.isIE){
27616                 var cn = d.childNodes;
27617                 var sww = Math.max(0, (sw-12))+"px";
27618                 cn[0].childNodes[1].style.width = sww;
27619                 cn[1].childNodes[1].style.width = sww;
27620                 cn[2].childNodes[1].style.width = sww;
27621                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27622             }
27623         }
27624     },
27625
27626     /**
27627      * Hides this shadow
27628      */
27629     hide : function(){
27630         if(this.el){
27631             this.el.dom.style.display = "none";
27632             Roo.Shadow.Pool.push(this.el);
27633             delete this.el;
27634         }
27635     },
27636
27637     /**
27638      * Adjust the z-index of this shadow
27639      * @param {Number} zindex The new z-index
27640      */
27641     setZIndex : function(z){
27642         this.zIndex = z;
27643         if(this.el){
27644             this.el.setStyle("z-index", z);
27645         }
27646     }
27647 };
27648
27649 // Private utility class that manages the internal Shadow cache
27650 Roo.Shadow.Pool = function(){
27651     var p = [];
27652     var markup = Roo.isIE ?
27653                  '<div class="x-ie-shadow"></div>' :
27654                  '<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>';
27655     return {
27656         pull : function(){
27657             var sh = p.shift();
27658             if(!sh){
27659                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27660                 sh.autoBoxAdjust = false;
27661             }
27662             return sh;
27663         },
27664
27665         push : function(sh){
27666             p.push(sh);
27667         }
27668     };
27669 }();/*
27670  * Based on:
27671  * Ext JS Library 1.1.1
27672  * Copyright(c) 2006-2007, Ext JS, LLC.
27673  *
27674  * Originally Released Under LGPL - original licence link has changed is not relivant.
27675  *
27676  * Fork - LGPL
27677  * <script type="text/javascript">
27678  */
27679
27680
27681 /**
27682  * @class Roo.SplitBar
27683  * @extends Roo.util.Observable
27684  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27685  * <br><br>
27686  * Usage:
27687  * <pre><code>
27688 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27689                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27690 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27691 split.minSize = 100;
27692 split.maxSize = 600;
27693 split.animate = true;
27694 split.on('moved', splitterMoved);
27695 </code></pre>
27696  * @constructor
27697  * Create a new SplitBar
27698  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27699  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27700  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27701  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27702                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27703                         position of the SplitBar).
27704  */
27705 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27706     
27707     /** @private */
27708     this.el = Roo.get(dragElement, true);
27709     this.el.dom.unselectable = "on";
27710     /** @private */
27711     this.resizingEl = Roo.get(resizingElement, true);
27712
27713     /**
27714      * @private
27715      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27716      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27717      * @type Number
27718      */
27719     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27720     
27721     /**
27722      * The minimum size of the resizing element. (Defaults to 0)
27723      * @type Number
27724      */
27725     this.minSize = 0;
27726     
27727     /**
27728      * The maximum size of the resizing element. (Defaults to 2000)
27729      * @type Number
27730      */
27731     this.maxSize = 2000;
27732     
27733     /**
27734      * Whether to animate the transition to the new size
27735      * @type Boolean
27736      */
27737     this.animate = false;
27738     
27739     /**
27740      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27741      * @type Boolean
27742      */
27743     this.useShim = false;
27744     
27745     /** @private */
27746     this.shim = null;
27747     
27748     if(!existingProxy){
27749         /** @private */
27750         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27751     }else{
27752         this.proxy = Roo.get(existingProxy).dom;
27753     }
27754     /** @private */
27755     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27756     
27757     /** @private */
27758     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27759     
27760     /** @private */
27761     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27762     
27763     /** @private */
27764     this.dragSpecs = {};
27765     
27766     /**
27767      * @private The adapter to use to positon and resize elements
27768      */
27769     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27770     this.adapter.init(this);
27771     
27772     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27773         /** @private */
27774         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27775         this.el.addClass("x-splitbar-h");
27776     }else{
27777         /** @private */
27778         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27779         this.el.addClass("x-splitbar-v");
27780     }
27781     
27782     this.addEvents({
27783         /**
27784          * @event resize
27785          * Fires when the splitter is moved (alias for {@link #event-moved})
27786          * @param {Roo.SplitBar} this
27787          * @param {Number} newSize the new width or height
27788          */
27789         "resize" : true,
27790         /**
27791          * @event moved
27792          * Fires when the splitter is moved
27793          * @param {Roo.SplitBar} this
27794          * @param {Number} newSize the new width or height
27795          */
27796         "moved" : true,
27797         /**
27798          * @event beforeresize
27799          * Fires before the splitter is dragged
27800          * @param {Roo.SplitBar} this
27801          */
27802         "beforeresize" : true,
27803
27804         "beforeapply" : true
27805     });
27806
27807     Roo.util.Observable.call(this);
27808 };
27809
27810 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27811     onStartProxyDrag : function(x, y){
27812         this.fireEvent("beforeresize", this);
27813         if(!this.overlay){
27814             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27815             o.unselectable();
27816             o.enableDisplayMode("block");
27817             // all splitbars share the same overlay
27818             Roo.SplitBar.prototype.overlay = o;
27819         }
27820         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27821         this.overlay.show();
27822         Roo.get(this.proxy).setDisplayed("block");
27823         var size = this.adapter.getElementSize(this);
27824         this.activeMinSize = this.getMinimumSize();;
27825         this.activeMaxSize = this.getMaximumSize();;
27826         var c1 = size - this.activeMinSize;
27827         var c2 = Math.max(this.activeMaxSize - size, 0);
27828         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27829             this.dd.resetConstraints();
27830             this.dd.setXConstraint(
27831                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27832                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27833             );
27834             this.dd.setYConstraint(0, 0);
27835         }else{
27836             this.dd.resetConstraints();
27837             this.dd.setXConstraint(0, 0);
27838             this.dd.setYConstraint(
27839                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27840                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27841             );
27842          }
27843         this.dragSpecs.startSize = size;
27844         this.dragSpecs.startPoint = [x, y];
27845         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27846     },
27847     
27848     /** 
27849      * @private Called after the drag operation by the DDProxy
27850      */
27851     onEndProxyDrag : function(e){
27852         Roo.get(this.proxy).setDisplayed(false);
27853         var endPoint = Roo.lib.Event.getXY(e);
27854         if(this.overlay){
27855             this.overlay.hide();
27856         }
27857         var newSize;
27858         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27859             newSize = this.dragSpecs.startSize + 
27860                 (this.placement == Roo.SplitBar.LEFT ?
27861                     endPoint[0] - this.dragSpecs.startPoint[0] :
27862                     this.dragSpecs.startPoint[0] - endPoint[0]
27863                 );
27864         }else{
27865             newSize = this.dragSpecs.startSize + 
27866                 (this.placement == Roo.SplitBar.TOP ?
27867                     endPoint[1] - this.dragSpecs.startPoint[1] :
27868                     this.dragSpecs.startPoint[1] - endPoint[1]
27869                 );
27870         }
27871         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27872         if(newSize != this.dragSpecs.startSize){
27873             if(this.fireEvent('beforeapply', this, newSize) !== false){
27874                 this.adapter.setElementSize(this, newSize);
27875                 this.fireEvent("moved", this, newSize);
27876                 this.fireEvent("resize", this, newSize);
27877             }
27878         }
27879     },
27880     
27881     /**
27882      * Get the adapter this SplitBar uses
27883      * @return The adapter object
27884      */
27885     getAdapter : function(){
27886         return this.adapter;
27887     },
27888     
27889     /**
27890      * Set the adapter this SplitBar uses
27891      * @param {Object} adapter A SplitBar adapter object
27892      */
27893     setAdapter : function(adapter){
27894         this.adapter = adapter;
27895         this.adapter.init(this);
27896     },
27897     
27898     /**
27899      * Gets the minimum size for the resizing element
27900      * @return {Number} The minimum size
27901      */
27902     getMinimumSize : function(){
27903         return this.minSize;
27904     },
27905     
27906     /**
27907      * Sets the minimum size for the resizing element
27908      * @param {Number} minSize The minimum size
27909      */
27910     setMinimumSize : function(minSize){
27911         this.minSize = minSize;
27912     },
27913     
27914     /**
27915      * Gets the maximum size for the resizing element
27916      * @return {Number} The maximum size
27917      */
27918     getMaximumSize : function(){
27919         return this.maxSize;
27920     },
27921     
27922     /**
27923      * Sets the maximum size for the resizing element
27924      * @param {Number} maxSize The maximum size
27925      */
27926     setMaximumSize : function(maxSize){
27927         this.maxSize = maxSize;
27928     },
27929     
27930     /**
27931      * Sets the initialize size for the resizing element
27932      * @param {Number} size The initial size
27933      */
27934     setCurrentSize : function(size){
27935         var oldAnimate = this.animate;
27936         this.animate = false;
27937         this.adapter.setElementSize(this, size);
27938         this.animate = oldAnimate;
27939     },
27940     
27941     /**
27942      * Destroy this splitbar. 
27943      * @param {Boolean} removeEl True to remove the element
27944      */
27945     destroy : function(removeEl){
27946         if(this.shim){
27947             this.shim.remove();
27948         }
27949         this.dd.unreg();
27950         this.proxy.parentNode.removeChild(this.proxy);
27951         if(removeEl){
27952             this.el.remove();
27953         }
27954     }
27955 });
27956
27957 /**
27958  * @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.
27959  */
27960 Roo.SplitBar.createProxy = function(dir){
27961     var proxy = new Roo.Element(document.createElement("div"));
27962     proxy.unselectable();
27963     var cls = 'x-splitbar-proxy';
27964     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27965     document.body.appendChild(proxy.dom);
27966     return proxy.dom;
27967 };
27968
27969 /** 
27970  * @class Roo.SplitBar.BasicLayoutAdapter
27971  * Default Adapter. It assumes the splitter and resizing element are not positioned
27972  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27973  */
27974 Roo.SplitBar.BasicLayoutAdapter = function(){
27975 };
27976
27977 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27978     // do nothing for now
27979     init : function(s){
27980     
27981     },
27982     /**
27983      * Called before drag operations to get the current size of the resizing element. 
27984      * @param {Roo.SplitBar} s The SplitBar using this adapter
27985      */
27986      getElementSize : function(s){
27987         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27988             return s.resizingEl.getWidth();
27989         }else{
27990             return s.resizingEl.getHeight();
27991         }
27992     },
27993     
27994     /**
27995      * Called after drag operations to set the size of the resizing element.
27996      * @param {Roo.SplitBar} s The SplitBar using this adapter
27997      * @param {Number} newSize The new size to set
27998      * @param {Function} onComplete A function to be invoked when resizing is complete
27999      */
28000     setElementSize : function(s, newSize, onComplete){
28001         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28002             if(!s.animate){
28003                 s.resizingEl.setWidth(newSize);
28004                 if(onComplete){
28005                     onComplete(s, newSize);
28006                 }
28007             }else{
28008                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
28009             }
28010         }else{
28011             
28012             if(!s.animate){
28013                 s.resizingEl.setHeight(newSize);
28014                 if(onComplete){
28015                     onComplete(s, newSize);
28016                 }
28017             }else{
28018                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
28019             }
28020         }
28021     }
28022 };
28023
28024 /** 
28025  *@class Roo.SplitBar.AbsoluteLayoutAdapter
28026  * @extends Roo.SplitBar.BasicLayoutAdapter
28027  * Adapter that  moves the splitter element to align with the resized sizing element. 
28028  * Used with an absolute positioned SplitBar.
28029  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
28030  * document.body, make sure you assign an id to the body element.
28031  */
28032 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
28033     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
28034     this.container = Roo.get(container);
28035 };
28036
28037 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
28038     init : function(s){
28039         this.basic.init(s);
28040     },
28041     
28042     getElementSize : function(s){
28043         return this.basic.getElementSize(s);
28044     },
28045     
28046     setElementSize : function(s, newSize, onComplete){
28047         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
28048     },
28049     
28050     moveSplitter : function(s){
28051         var yes = Roo.SplitBar;
28052         switch(s.placement){
28053             case yes.LEFT:
28054                 s.el.setX(s.resizingEl.getRight());
28055                 break;
28056             case yes.RIGHT:
28057                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
28058                 break;
28059             case yes.TOP:
28060                 s.el.setY(s.resizingEl.getBottom());
28061                 break;
28062             case yes.BOTTOM:
28063                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
28064                 break;
28065         }
28066     }
28067 };
28068
28069 /**
28070  * Orientation constant - Create a vertical SplitBar
28071  * @static
28072  * @type Number
28073  */
28074 Roo.SplitBar.VERTICAL = 1;
28075
28076 /**
28077  * Orientation constant - Create a horizontal SplitBar
28078  * @static
28079  * @type Number
28080  */
28081 Roo.SplitBar.HORIZONTAL = 2;
28082
28083 /**
28084  * Placement constant - The resizing element is to the left of the splitter element
28085  * @static
28086  * @type Number
28087  */
28088 Roo.SplitBar.LEFT = 1;
28089
28090 /**
28091  * Placement constant - The resizing element is to the right of the splitter element
28092  * @static
28093  * @type Number
28094  */
28095 Roo.SplitBar.RIGHT = 2;
28096
28097 /**
28098  * Placement constant - The resizing element is positioned above the splitter element
28099  * @static
28100  * @type Number
28101  */
28102 Roo.SplitBar.TOP = 3;
28103
28104 /**
28105  * Placement constant - The resizing element is positioned under splitter element
28106  * @static
28107  * @type Number
28108  */
28109 Roo.SplitBar.BOTTOM = 4;
28110 /*
28111  * Based on:
28112  * Ext JS Library 1.1.1
28113  * Copyright(c) 2006-2007, Ext JS, LLC.
28114  *
28115  * Originally Released Under LGPL - original licence link has changed is not relivant.
28116  *
28117  * Fork - LGPL
28118  * <script type="text/javascript">
28119  */
28120
28121 /**
28122  * @class Roo.View
28123  * @extends Roo.util.Observable
28124  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
28125  * This class also supports single and multi selection modes. <br>
28126  * Create a data model bound view:
28127  <pre><code>
28128  var store = new Roo.data.Store(...);
28129
28130  var view = new Roo.View({
28131     el : "my-element",
28132     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
28133  
28134     singleSelect: true,
28135     selectedClass: "ydataview-selected",
28136     store: store
28137  });
28138
28139  // listen for node click?
28140  view.on("click", function(vw, index, node, e){
28141  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28142  });
28143
28144  // load XML data
28145  dataModel.load("foobar.xml");
28146  </code></pre>
28147  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
28148  * <br><br>
28149  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
28150  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
28151  * 
28152  * Note: old style constructor is still suported (container, template, config)
28153  * 
28154  * @constructor
28155  * Create a new View
28156  * @param {Object} config The config object
28157  * 
28158  */
28159 Roo.View = function(config, depreciated_tpl, depreciated_config){
28160     
28161     this.parent = false;
28162     
28163     if (typeof(depreciated_tpl) == 'undefined') {
28164         // new way.. - universal constructor.
28165         Roo.apply(this, config);
28166         this.el  = Roo.get(this.el);
28167     } else {
28168         // old format..
28169         this.el  = Roo.get(config);
28170         this.tpl = depreciated_tpl;
28171         Roo.apply(this, depreciated_config);
28172     }
28173     this.wrapEl  = this.el.wrap().wrap();
28174     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28175     
28176     
28177     if(typeof(this.tpl) == "string"){
28178         this.tpl = new Roo.Template(this.tpl);
28179     } else {
28180         // support xtype ctors..
28181         this.tpl = new Roo.factory(this.tpl, Roo);
28182     }
28183     
28184     
28185     this.tpl.compile();
28186     
28187     /** @private */
28188     this.addEvents({
28189         /**
28190          * @event beforeclick
28191          * Fires before a click is processed. Returns false to cancel the default action.
28192          * @param {Roo.View} this
28193          * @param {Number} index The index of the target node
28194          * @param {HTMLElement} node The target node
28195          * @param {Roo.EventObject} e The raw event object
28196          */
28197             "beforeclick" : true,
28198         /**
28199          * @event click
28200          * Fires when a template node is clicked.
28201          * @param {Roo.View} this
28202          * @param {Number} index The index of the target node
28203          * @param {HTMLElement} node The target node
28204          * @param {Roo.EventObject} e The raw event object
28205          */
28206             "click" : true,
28207         /**
28208          * @event dblclick
28209          * Fires when a template node is double clicked.
28210          * @param {Roo.View} this
28211          * @param {Number} index The index of the target node
28212          * @param {HTMLElement} node The target node
28213          * @param {Roo.EventObject} e The raw event object
28214          */
28215             "dblclick" : true,
28216         /**
28217          * @event contextmenu
28218          * Fires when a template node is right clicked.
28219          * @param {Roo.View} this
28220          * @param {Number} index The index of the target node
28221          * @param {HTMLElement} node The target node
28222          * @param {Roo.EventObject} e The raw event object
28223          */
28224             "contextmenu" : true,
28225         /**
28226          * @event selectionchange
28227          * Fires when the selected nodes change.
28228          * @param {Roo.View} this
28229          * @param {Array} selections Array of the selected nodes
28230          */
28231             "selectionchange" : true,
28232     
28233         /**
28234          * @event beforeselect
28235          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28236          * @param {Roo.View} this
28237          * @param {HTMLElement} node The node to be selected
28238          * @param {Array} selections Array of currently selected nodes
28239          */
28240             "beforeselect" : true,
28241         /**
28242          * @event preparedata
28243          * Fires on every row to render, to allow you to change the data.
28244          * @param {Roo.View} this
28245          * @param {Object} data to be rendered (change this)
28246          */
28247           "preparedata" : true
28248           
28249           
28250         });
28251
28252
28253
28254     this.el.on({
28255         "click": this.onClick,
28256         "dblclick": this.onDblClick,
28257         "contextmenu": this.onContextMenu,
28258         scope:this
28259     });
28260
28261     this.selections = [];
28262     this.nodes = [];
28263     this.cmp = new Roo.CompositeElementLite([]);
28264     if(this.store){
28265         this.store = Roo.factory(this.store, Roo.data);
28266         this.setStore(this.store, true);
28267     }
28268     
28269     if ( this.footer && this.footer.xtype) {
28270            
28271          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28272         
28273         this.footer.dataSource = this.store;
28274         this.footer.container = fctr;
28275         this.footer = Roo.factory(this.footer, Roo);
28276         fctr.insertFirst(this.el);
28277         
28278         // this is a bit insane - as the paging toolbar seems to detach the el..
28279 //        dom.parentNode.parentNode.parentNode
28280          // they get detached?
28281     }
28282     
28283     
28284     Roo.View.superclass.constructor.call(this);
28285     
28286     
28287 };
28288
28289 Roo.extend(Roo.View, Roo.util.Observable, {
28290     
28291      /**
28292      * @cfg {Roo.data.Store} store Data store to load data from.
28293      */
28294     store : false,
28295     
28296     /**
28297      * @cfg {String|Roo.Element} el The container element.
28298      */
28299     el : '',
28300     
28301     /**
28302      * @cfg {String|Roo.Template} tpl The template used by this View 
28303      */
28304     tpl : false,
28305     /**
28306      * @cfg {String} dataName the named area of the template to use as the data area
28307      *                          Works with domtemplates roo-name="name"
28308      */
28309     dataName: false,
28310     /**
28311      * @cfg {String} selectedClass The css class to add to selected nodes
28312      */
28313     selectedClass : "x-view-selected",
28314      /**
28315      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28316      */
28317     emptyText : "",
28318     
28319     /**
28320      * @cfg {String} text to display on mask (default Loading)
28321      */
28322     mask : false,
28323     /**
28324      * @cfg {Boolean} multiSelect Allow multiple selection
28325      */
28326     multiSelect : false,
28327     /**
28328      * @cfg {Boolean} singleSelect Allow single selection
28329      */
28330     singleSelect:  false,
28331     
28332     /**
28333      * @cfg {Boolean} toggleSelect - selecting 
28334      */
28335     toggleSelect : false,
28336     
28337     /**
28338      * @cfg {Boolean} tickable - selecting 
28339      */
28340     tickable : false,
28341     
28342     /**
28343      * Returns the element this view is bound to.
28344      * @return {Roo.Element}
28345      */
28346     getEl : function(){
28347         return this.wrapEl;
28348     },
28349     
28350     
28351
28352     /**
28353      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28354      */
28355     refresh : function(){
28356         //Roo.log('refresh');
28357         var t = this.tpl;
28358         
28359         // if we are using something like 'domtemplate', then
28360         // the what gets used is:
28361         // t.applySubtemplate(NAME, data, wrapping data..)
28362         // the outer template then get' applied with
28363         //     the store 'extra data'
28364         // and the body get's added to the
28365         //      roo-name="data" node?
28366         //      <span class='roo-tpl-{name}'></span> ?????
28367         
28368         
28369         
28370         this.clearSelections();
28371         this.el.update("");
28372         var html = [];
28373         var records = this.store.getRange();
28374         if(records.length < 1) {
28375             
28376             // is this valid??  = should it render a template??
28377             
28378             this.el.update(this.emptyText);
28379             return;
28380         }
28381         var el = this.el;
28382         if (this.dataName) {
28383             this.el.update(t.apply(this.store.meta)); //????
28384             el = this.el.child('.roo-tpl-' + this.dataName);
28385         }
28386         
28387         for(var i = 0, len = records.length; i < len; i++){
28388             var data = this.prepareData(records[i].data, i, records[i]);
28389             this.fireEvent("preparedata", this, data, i, records[i]);
28390             
28391             var d = Roo.apply({}, data);
28392             
28393             if(this.tickable){
28394                 Roo.apply(d, {'roo-id' : Roo.id()});
28395                 
28396                 var _this = this;
28397             
28398                 Roo.each(this.parent.item, function(item){
28399                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28400                         return;
28401                     }
28402                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28403                 });
28404             }
28405             
28406             html[html.length] = Roo.util.Format.trim(
28407                 this.dataName ?
28408                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28409                     t.apply(d)
28410             );
28411         }
28412         
28413         
28414         
28415         el.update(html.join(""));
28416         this.nodes = el.dom.childNodes;
28417         this.updateIndexes(0);
28418     },
28419     
28420
28421     /**
28422      * Function to override to reformat the data that is sent to
28423      * the template for each node.
28424      * DEPRICATED - use the preparedata event handler.
28425      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28426      * a JSON object for an UpdateManager bound view).
28427      */
28428     prepareData : function(data, index, record)
28429     {
28430         this.fireEvent("preparedata", this, data, index, record);
28431         return data;
28432     },
28433
28434     onUpdate : function(ds, record){
28435         // Roo.log('on update');   
28436         this.clearSelections();
28437         var index = this.store.indexOf(record);
28438         var n = this.nodes[index];
28439         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28440         n.parentNode.removeChild(n);
28441         this.updateIndexes(index, index);
28442     },
28443
28444     
28445     
28446 // --------- FIXME     
28447     onAdd : function(ds, records, index)
28448     {
28449         //Roo.log(['on Add', ds, records, index] );        
28450         this.clearSelections();
28451         if(this.nodes.length == 0){
28452             this.refresh();
28453             return;
28454         }
28455         var n = this.nodes[index];
28456         for(var i = 0, len = records.length; i < len; i++){
28457             var d = this.prepareData(records[i].data, i, records[i]);
28458             if(n){
28459                 this.tpl.insertBefore(n, d);
28460             }else{
28461                 
28462                 this.tpl.append(this.el, d);
28463             }
28464         }
28465         this.updateIndexes(index);
28466     },
28467
28468     onRemove : function(ds, record, index){
28469        // Roo.log('onRemove');
28470         this.clearSelections();
28471         var el = this.dataName  ?
28472             this.el.child('.roo-tpl-' + this.dataName) :
28473             this.el; 
28474         
28475         el.dom.removeChild(this.nodes[index]);
28476         this.updateIndexes(index);
28477     },
28478
28479     /**
28480      * Refresh an individual node.
28481      * @param {Number} index
28482      */
28483     refreshNode : function(index){
28484         this.onUpdate(this.store, this.store.getAt(index));
28485     },
28486
28487     updateIndexes : function(startIndex, endIndex){
28488         var ns = this.nodes;
28489         startIndex = startIndex || 0;
28490         endIndex = endIndex || ns.length - 1;
28491         for(var i = startIndex; i <= endIndex; i++){
28492             ns[i].nodeIndex = i;
28493         }
28494     },
28495
28496     /**
28497      * Changes the data store this view uses and refresh the view.
28498      * @param {Store} store
28499      */
28500     setStore : function(store, initial){
28501         if(!initial && this.store){
28502             this.store.un("datachanged", this.refresh);
28503             this.store.un("add", this.onAdd);
28504             this.store.un("remove", this.onRemove);
28505             this.store.un("update", this.onUpdate);
28506             this.store.un("clear", this.refresh);
28507             this.store.un("beforeload", this.onBeforeLoad);
28508             this.store.un("load", this.onLoad);
28509             this.store.un("loadexception", this.onLoad);
28510         }
28511         if(store){
28512           
28513             store.on("datachanged", this.refresh, this);
28514             store.on("add", this.onAdd, this);
28515             store.on("remove", this.onRemove, this);
28516             store.on("update", this.onUpdate, this);
28517             store.on("clear", this.refresh, this);
28518             store.on("beforeload", this.onBeforeLoad, this);
28519             store.on("load", this.onLoad, this);
28520             store.on("loadexception", this.onLoad, this);
28521         }
28522         
28523         if(store){
28524             this.refresh();
28525         }
28526     },
28527     /**
28528      * onbeforeLoad - masks the loading area.
28529      *
28530      */
28531     onBeforeLoad : function(store,opts)
28532     {
28533          //Roo.log('onBeforeLoad');   
28534         if (!opts.add) {
28535             this.el.update("");
28536         }
28537         this.el.mask(this.mask ? this.mask : "Loading" ); 
28538     },
28539     onLoad : function ()
28540     {
28541         this.el.unmask();
28542     },
28543     
28544
28545     /**
28546      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28547      * @param {HTMLElement} node
28548      * @return {HTMLElement} The template node
28549      */
28550     findItemFromChild : function(node){
28551         var el = this.dataName  ?
28552             this.el.child('.roo-tpl-' + this.dataName,true) :
28553             this.el.dom; 
28554         
28555         if(!node || node.parentNode == el){
28556                     return node;
28557             }
28558             var p = node.parentNode;
28559             while(p && p != el){
28560             if(p.parentNode == el){
28561                 return p;
28562             }
28563             p = p.parentNode;
28564         }
28565             return null;
28566     },
28567
28568     /** @ignore */
28569     onClick : function(e){
28570         var item = this.findItemFromChild(e.getTarget());
28571         if(item){
28572             var index = this.indexOf(item);
28573             if(this.onItemClick(item, index, e) !== false){
28574                 this.fireEvent("click", this, index, item, e);
28575             }
28576         }else{
28577             this.clearSelections();
28578         }
28579     },
28580
28581     /** @ignore */
28582     onContextMenu : function(e){
28583         var item = this.findItemFromChild(e.getTarget());
28584         if(item){
28585             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28586         }
28587     },
28588
28589     /** @ignore */
28590     onDblClick : function(e){
28591         var item = this.findItemFromChild(e.getTarget());
28592         if(item){
28593             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28594         }
28595     },
28596
28597     onItemClick : function(item, index, e)
28598     {
28599         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28600             return false;
28601         }
28602         if (this.toggleSelect) {
28603             var m = this.isSelected(item) ? 'unselect' : 'select';
28604             //Roo.log(m);
28605             var _t = this;
28606             _t[m](item, true, false);
28607             return true;
28608         }
28609         if(this.multiSelect || this.singleSelect){
28610             if(this.multiSelect && e.shiftKey && this.lastSelection){
28611                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28612             }else{
28613                 this.select(item, this.multiSelect && e.ctrlKey);
28614                 this.lastSelection = item;
28615             }
28616             
28617             if(!this.tickable){
28618                 e.preventDefault();
28619             }
28620             
28621         }
28622         return true;
28623     },
28624
28625     /**
28626      * Get the number of selected nodes.
28627      * @return {Number}
28628      */
28629     getSelectionCount : function(){
28630         return this.selections.length;
28631     },
28632
28633     /**
28634      * Get the currently selected nodes.
28635      * @return {Array} An array of HTMLElements
28636      */
28637     getSelectedNodes : function(){
28638         return this.selections;
28639     },
28640
28641     /**
28642      * Get the indexes of the selected nodes.
28643      * @return {Array}
28644      */
28645     getSelectedIndexes : function(){
28646         var indexes = [], s = this.selections;
28647         for(var i = 0, len = s.length; i < len; i++){
28648             indexes.push(s[i].nodeIndex);
28649         }
28650         return indexes;
28651     },
28652
28653     /**
28654      * Clear all selections
28655      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28656      */
28657     clearSelections : function(suppressEvent){
28658         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28659             this.cmp.elements = this.selections;
28660             this.cmp.removeClass(this.selectedClass);
28661             this.selections = [];
28662             if(!suppressEvent){
28663                 this.fireEvent("selectionchange", this, this.selections);
28664             }
28665         }
28666     },
28667
28668     /**
28669      * Returns true if the passed node is selected
28670      * @param {HTMLElement/Number} node The node or node index
28671      * @return {Boolean}
28672      */
28673     isSelected : function(node){
28674         var s = this.selections;
28675         if(s.length < 1){
28676             return false;
28677         }
28678         node = this.getNode(node);
28679         return s.indexOf(node) !== -1;
28680     },
28681
28682     /**
28683      * Selects nodes.
28684      * @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
28685      * @param {Boolean} keepExisting (optional) true to keep existing selections
28686      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28687      */
28688     select : function(nodeInfo, keepExisting, suppressEvent){
28689         if(nodeInfo instanceof Array){
28690             if(!keepExisting){
28691                 this.clearSelections(true);
28692             }
28693             for(var i = 0, len = nodeInfo.length; i < len; i++){
28694                 this.select(nodeInfo[i], true, true);
28695             }
28696             return;
28697         } 
28698         var node = this.getNode(nodeInfo);
28699         if(!node || this.isSelected(node)){
28700             return; // already selected.
28701         }
28702         if(!keepExisting){
28703             this.clearSelections(true);
28704         }
28705         
28706         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28707             Roo.fly(node).addClass(this.selectedClass);
28708             this.selections.push(node);
28709             if(!suppressEvent){
28710                 this.fireEvent("selectionchange", this, this.selections);
28711             }
28712         }
28713         
28714         
28715     },
28716       /**
28717      * Unselects nodes.
28718      * @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
28719      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28720      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28721      */
28722     unselect : function(nodeInfo, keepExisting, suppressEvent)
28723     {
28724         if(nodeInfo instanceof Array){
28725             Roo.each(this.selections, function(s) {
28726                 this.unselect(s, nodeInfo);
28727             }, this);
28728             return;
28729         }
28730         var node = this.getNode(nodeInfo);
28731         if(!node || !this.isSelected(node)){
28732             //Roo.log("not selected");
28733             return; // not selected.
28734         }
28735         // fireevent???
28736         var ns = [];
28737         Roo.each(this.selections, function(s) {
28738             if (s == node ) {
28739                 Roo.fly(node).removeClass(this.selectedClass);
28740
28741                 return;
28742             }
28743             ns.push(s);
28744         },this);
28745         
28746         this.selections= ns;
28747         this.fireEvent("selectionchange", this, this.selections);
28748     },
28749
28750     /**
28751      * Gets a template node.
28752      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28753      * @return {HTMLElement} The node or null if it wasn't found
28754      */
28755     getNode : function(nodeInfo){
28756         if(typeof nodeInfo == "string"){
28757             return document.getElementById(nodeInfo);
28758         }else if(typeof nodeInfo == "number"){
28759             return this.nodes[nodeInfo];
28760         }
28761         return nodeInfo;
28762     },
28763
28764     /**
28765      * Gets a range template nodes.
28766      * @param {Number} startIndex
28767      * @param {Number} endIndex
28768      * @return {Array} An array of nodes
28769      */
28770     getNodes : function(start, end){
28771         var ns = this.nodes;
28772         start = start || 0;
28773         end = typeof end == "undefined" ? ns.length - 1 : end;
28774         var nodes = [];
28775         if(start <= end){
28776             for(var i = start; i <= end; i++){
28777                 nodes.push(ns[i]);
28778             }
28779         } else{
28780             for(var i = start; i >= end; i--){
28781                 nodes.push(ns[i]);
28782             }
28783         }
28784         return nodes;
28785     },
28786
28787     /**
28788      * Finds the index of the passed node
28789      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28790      * @return {Number} The index of the node or -1
28791      */
28792     indexOf : function(node){
28793         node = this.getNode(node);
28794         if(typeof node.nodeIndex == "number"){
28795             return node.nodeIndex;
28796         }
28797         var ns = this.nodes;
28798         for(var i = 0, len = ns.length; i < len; i++){
28799             if(ns[i] == node){
28800                 return i;
28801             }
28802         }
28803         return -1;
28804     }
28805 });
28806 /*
28807  * Based on:
28808  * Ext JS Library 1.1.1
28809  * Copyright(c) 2006-2007, Ext JS, LLC.
28810  *
28811  * Originally Released Under LGPL - original licence link has changed is not relivant.
28812  *
28813  * Fork - LGPL
28814  * <script type="text/javascript">
28815  */
28816
28817 /**
28818  * @class Roo.JsonView
28819  * @extends Roo.View
28820  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28821 <pre><code>
28822 var view = new Roo.JsonView({
28823     container: "my-element",
28824     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28825     multiSelect: true, 
28826     jsonRoot: "data" 
28827 });
28828
28829 // listen for node click?
28830 view.on("click", function(vw, index, node, e){
28831     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28832 });
28833
28834 // direct load of JSON data
28835 view.load("foobar.php");
28836
28837 // Example from my blog list
28838 var tpl = new Roo.Template(
28839     '&lt;div class="entry"&gt;' +
28840     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28841     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28842     "&lt;/div&gt;&lt;hr /&gt;"
28843 );
28844
28845 var moreView = new Roo.JsonView({
28846     container :  "entry-list", 
28847     template : tpl,
28848     jsonRoot: "posts"
28849 });
28850 moreView.on("beforerender", this.sortEntries, this);
28851 moreView.load({
28852     url: "/blog/get-posts.php",
28853     params: "allposts=true",
28854     text: "Loading Blog Entries..."
28855 });
28856 </code></pre>
28857
28858 * Note: old code is supported with arguments : (container, template, config)
28859
28860
28861  * @constructor
28862  * Create a new JsonView
28863  * 
28864  * @param {Object} config The config object
28865  * 
28866  */
28867 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28868     
28869     
28870     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28871
28872     var um = this.el.getUpdateManager();
28873     um.setRenderer(this);
28874     um.on("update", this.onLoad, this);
28875     um.on("failure", this.onLoadException, this);
28876
28877     /**
28878      * @event beforerender
28879      * Fires before rendering of the downloaded JSON data.
28880      * @param {Roo.JsonView} this
28881      * @param {Object} data The JSON data loaded
28882      */
28883     /**
28884      * @event load
28885      * Fires when data is loaded.
28886      * @param {Roo.JsonView} this
28887      * @param {Object} data The JSON data loaded
28888      * @param {Object} response The raw Connect response object
28889      */
28890     /**
28891      * @event loadexception
28892      * Fires when loading fails.
28893      * @param {Roo.JsonView} this
28894      * @param {Object} response The raw Connect response object
28895      */
28896     this.addEvents({
28897         'beforerender' : true,
28898         'load' : true,
28899         'loadexception' : true
28900     });
28901 };
28902 Roo.extend(Roo.JsonView, Roo.View, {
28903     /**
28904      * @type {String} The root property in the loaded JSON object that contains the data
28905      */
28906     jsonRoot : "",
28907
28908     /**
28909      * Refreshes the view.
28910      */
28911     refresh : function(){
28912         this.clearSelections();
28913         this.el.update("");
28914         var html = [];
28915         var o = this.jsonData;
28916         if(o && o.length > 0){
28917             for(var i = 0, len = o.length; i < len; i++){
28918                 var data = this.prepareData(o[i], i, o);
28919                 html[html.length] = this.tpl.apply(data);
28920             }
28921         }else{
28922             html.push(this.emptyText);
28923         }
28924         this.el.update(html.join(""));
28925         this.nodes = this.el.dom.childNodes;
28926         this.updateIndexes(0);
28927     },
28928
28929     /**
28930      * 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.
28931      * @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:
28932      <pre><code>
28933      view.load({
28934          url: "your-url.php",
28935          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28936          callback: yourFunction,
28937          scope: yourObject, //(optional scope)
28938          discardUrl: false,
28939          nocache: false,
28940          text: "Loading...",
28941          timeout: 30,
28942          scripts: false
28943      });
28944      </code></pre>
28945      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28946      * 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.
28947      * @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}
28948      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28949      * @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.
28950      */
28951     load : function(){
28952         var um = this.el.getUpdateManager();
28953         um.update.apply(um, arguments);
28954     },
28955
28956     // note - render is a standard framework call...
28957     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28958     render : function(el, response){
28959         
28960         this.clearSelections();
28961         this.el.update("");
28962         var o;
28963         try{
28964             if (response != '') {
28965                 o = Roo.util.JSON.decode(response.responseText);
28966                 if(this.jsonRoot){
28967                     
28968                     o = o[this.jsonRoot];
28969                 }
28970             }
28971         } catch(e){
28972         }
28973         /**
28974          * The current JSON data or null
28975          */
28976         this.jsonData = o;
28977         this.beforeRender();
28978         this.refresh();
28979     },
28980
28981 /**
28982  * Get the number of records in the current JSON dataset
28983  * @return {Number}
28984  */
28985     getCount : function(){
28986         return this.jsonData ? this.jsonData.length : 0;
28987     },
28988
28989 /**
28990  * Returns the JSON object for the specified node(s)
28991  * @param {HTMLElement/Array} node The node or an array of nodes
28992  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28993  * you get the JSON object for the node
28994  */
28995     getNodeData : function(node){
28996         if(node instanceof Array){
28997             var data = [];
28998             for(var i = 0, len = node.length; i < len; i++){
28999                 data.push(this.getNodeData(node[i]));
29000             }
29001             return data;
29002         }
29003         return this.jsonData[this.indexOf(node)] || null;
29004     },
29005
29006     beforeRender : function(){
29007         this.snapshot = this.jsonData;
29008         if(this.sortInfo){
29009             this.sort.apply(this, this.sortInfo);
29010         }
29011         this.fireEvent("beforerender", this, this.jsonData);
29012     },
29013
29014     onLoad : function(el, o){
29015         this.fireEvent("load", this, this.jsonData, o);
29016     },
29017
29018     onLoadException : function(el, o){
29019         this.fireEvent("loadexception", this, o);
29020     },
29021
29022 /**
29023  * Filter the data by a specific property.
29024  * @param {String} property A property on your JSON objects
29025  * @param {String/RegExp} value Either string that the property values
29026  * should start with, or a RegExp to test against the property
29027  */
29028     filter : function(property, value){
29029         if(this.jsonData){
29030             var data = [];
29031             var ss = this.snapshot;
29032             if(typeof value == "string"){
29033                 var vlen = value.length;
29034                 if(vlen == 0){
29035                     this.clearFilter();
29036                     return;
29037                 }
29038                 value = value.toLowerCase();
29039                 for(var i = 0, len = ss.length; i < len; i++){
29040                     var o = ss[i];
29041                     if(o[property].substr(0, vlen).toLowerCase() == value){
29042                         data.push(o);
29043                     }
29044                 }
29045             } else if(value.exec){ // regex?
29046                 for(var i = 0, len = ss.length; i < len; i++){
29047                     var o = ss[i];
29048                     if(value.test(o[property])){
29049                         data.push(o);
29050                     }
29051                 }
29052             } else{
29053                 return;
29054             }
29055             this.jsonData = data;
29056             this.refresh();
29057         }
29058     },
29059
29060 /**
29061  * Filter by a function. The passed function will be called with each
29062  * object in the current dataset. If the function returns true the value is kept,
29063  * otherwise it is filtered.
29064  * @param {Function} fn
29065  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
29066  */
29067     filterBy : function(fn, scope){
29068         if(this.jsonData){
29069             var data = [];
29070             var ss = this.snapshot;
29071             for(var i = 0, len = ss.length; i < len; i++){
29072                 var o = ss[i];
29073                 if(fn.call(scope || this, o)){
29074                     data.push(o);
29075                 }
29076             }
29077             this.jsonData = data;
29078             this.refresh();
29079         }
29080     },
29081
29082 /**
29083  * Clears the current filter.
29084  */
29085     clearFilter : function(){
29086         if(this.snapshot && this.jsonData != this.snapshot){
29087             this.jsonData = this.snapshot;
29088             this.refresh();
29089         }
29090     },
29091
29092
29093 /**
29094  * Sorts the data for this view and refreshes it.
29095  * @param {String} property A property on your JSON objects to sort on
29096  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
29097  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
29098  */
29099     sort : function(property, dir, sortType){
29100         this.sortInfo = Array.prototype.slice.call(arguments, 0);
29101         if(this.jsonData){
29102             var p = property;
29103             var dsc = dir && dir.toLowerCase() == "desc";
29104             var f = function(o1, o2){
29105                 var v1 = sortType ? sortType(o1[p]) : o1[p];
29106                 var v2 = sortType ? sortType(o2[p]) : o2[p];
29107                 ;
29108                 if(v1 < v2){
29109                     return dsc ? +1 : -1;
29110                 } else if(v1 > v2){
29111                     return dsc ? -1 : +1;
29112                 } else{
29113                     return 0;
29114                 }
29115             };
29116             this.jsonData.sort(f);
29117             this.refresh();
29118             if(this.jsonData != this.snapshot){
29119                 this.snapshot.sort(f);
29120             }
29121         }
29122     }
29123 });/*
29124  * Based on:
29125  * Ext JS Library 1.1.1
29126  * Copyright(c) 2006-2007, Ext JS, LLC.
29127  *
29128  * Originally Released Under LGPL - original licence link has changed is not relivant.
29129  *
29130  * Fork - LGPL
29131  * <script type="text/javascript">
29132  */
29133  
29134
29135 /**
29136  * @class Roo.ColorPalette
29137  * @extends Roo.Component
29138  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
29139  * Here's an example of typical usage:
29140  * <pre><code>
29141 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
29142 cp.render('my-div');
29143
29144 cp.on('select', function(palette, selColor){
29145     // do something with selColor
29146 });
29147 </code></pre>
29148  * @constructor
29149  * Create a new ColorPalette
29150  * @param {Object} config The config object
29151  */
29152 Roo.ColorPalette = function(config){
29153     Roo.ColorPalette.superclass.constructor.call(this, config);
29154     this.addEvents({
29155         /**
29156              * @event select
29157              * Fires when a color is selected
29158              * @param {ColorPalette} this
29159              * @param {String} color The 6-digit color hex code (without the # symbol)
29160              */
29161         select: true
29162     });
29163
29164     if(this.handler){
29165         this.on("select", this.handler, this.scope, true);
29166     }
29167 };
29168 Roo.extend(Roo.ColorPalette, Roo.Component, {
29169     /**
29170      * @cfg {String} itemCls
29171      * The CSS class to apply to the containing element (defaults to "x-color-palette")
29172      */
29173     itemCls : "x-color-palette",
29174     /**
29175      * @cfg {String} value
29176      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
29177      * the hex codes are case-sensitive.
29178      */
29179     value : null,
29180     clickEvent:'click',
29181     // private
29182     ctype: "Roo.ColorPalette",
29183
29184     /**
29185      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29186      */
29187     allowReselect : false,
29188
29189     /**
29190      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
29191      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
29192      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29193      * of colors with the width setting until the box is symmetrical.</p>
29194      * <p>You can override individual colors if needed:</p>
29195      * <pre><code>
29196 var cp = new Roo.ColorPalette();
29197 cp.colors[0] = "FF0000";  // change the first box to red
29198 </code></pre>
29199
29200 Or you can provide a custom array of your own for complete control:
29201 <pre><code>
29202 var cp = new Roo.ColorPalette();
29203 cp.colors = ["000000", "993300", "333300"];
29204 </code></pre>
29205      * @type Array
29206      */
29207     colors : [
29208         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29209         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29210         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29211         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29212         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29213     ],
29214
29215     // private
29216     onRender : function(container, position){
29217         var t = new Roo.MasterTemplate(
29218             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29219         );
29220         var c = this.colors;
29221         for(var i = 0, len = c.length; i < len; i++){
29222             t.add([c[i]]);
29223         }
29224         var el = document.createElement("div");
29225         el.className = this.itemCls;
29226         t.overwrite(el);
29227         container.dom.insertBefore(el, position);
29228         this.el = Roo.get(el);
29229         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29230         if(this.clickEvent != 'click'){
29231             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29232         }
29233     },
29234
29235     // private
29236     afterRender : function(){
29237         Roo.ColorPalette.superclass.afterRender.call(this);
29238         if(this.value){
29239             var s = this.value;
29240             this.value = null;
29241             this.select(s);
29242         }
29243     },
29244
29245     // private
29246     handleClick : function(e, t){
29247         e.preventDefault();
29248         if(!this.disabled){
29249             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29250             this.select(c.toUpperCase());
29251         }
29252     },
29253
29254     /**
29255      * Selects the specified color in the palette (fires the select event)
29256      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29257      */
29258     select : function(color){
29259         color = color.replace("#", "");
29260         if(color != this.value || this.allowReselect){
29261             var el = this.el;
29262             if(this.value){
29263                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29264             }
29265             el.child("a.color-"+color).addClass("x-color-palette-sel");
29266             this.value = color;
29267             this.fireEvent("select", this, color);
29268         }
29269     }
29270 });/*
29271  * Based on:
29272  * Ext JS Library 1.1.1
29273  * Copyright(c) 2006-2007, Ext JS, LLC.
29274  *
29275  * Originally Released Under LGPL - original licence link has changed is not relivant.
29276  *
29277  * Fork - LGPL
29278  * <script type="text/javascript">
29279  */
29280  
29281 /**
29282  * @class Roo.DatePicker
29283  * @extends Roo.Component
29284  * Simple date picker class.
29285  * @constructor
29286  * Create a new DatePicker
29287  * @param {Object} config The config object
29288  */
29289 Roo.DatePicker = function(config){
29290     Roo.DatePicker.superclass.constructor.call(this, config);
29291
29292     this.value = config && config.value ?
29293                  config.value.clearTime() : new Date().clearTime();
29294
29295     this.addEvents({
29296         /**
29297              * @event select
29298              * Fires when a date is selected
29299              * @param {DatePicker} this
29300              * @param {Date} date The selected date
29301              */
29302         'select': true,
29303         /**
29304              * @event monthchange
29305              * Fires when the displayed month changes 
29306              * @param {DatePicker} this
29307              * @param {Date} date The selected month
29308              */
29309         'monthchange': true
29310     });
29311
29312     if(this.handler){
29313         this.on("select", this.handler,  this.scope || this);
29314     }
29315     // build the disabledDatesRE
29316     if(!this.disabledDatesRE && this.disabledDates){
29317         var dd = this.disabledDates;
29318         var re = "(?:";
29319         for(var i = 0; i < dd.length; i++){
29320             re += dd[i];
29321             if(i != dd.length-1) {
29322                 re += "|";
29323             }
29324         }
29325         this.disabledDatesRE = new RegExp(re + ")");
29326     }
29327 };
29328
29329 Roo.extend(Roo.DatePicker, Roo.Component, {
29330     /**
29331      * @cfg {String} todayText
29332      * The text to display on the button that selects the current date (defaults to "Today")
29333      */
29334     todayText : "Today",
29335     /**
29336      * @cfg {String} okText
29337      * The text to display on the ok button
29338      */
29339     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29340     /**
29341      * @cfg {String} cancelText
29342      * The text to display on the cancel button
29343      */
29344     cancelText : "Cancel",
29345     /**
29346      * @cfg {String} todayTip
29347      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29348      */
29349     todayTip : "{0} (Spacebar)",
29350     /**
29351      * @cfg {Date} minDate
29352      * Minimum allowable date (JavaScript date object, defaults to null)
29353      */
29354     minDate : null,
29355     /**
29356      * @cfg {Date} maxDate
29357      * Maximum allowable date (JavaScript date object, defaults to null)
29358      */
29359     maxDate : null,
29360     /**
29361      * @cfg {String} minText
29362      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29363      */
29364     minText : "This date is before the minimum date",
29365     /**
29366      * @cfg {String} maxText
29367      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29368      */
29369     maxText : "This date is after the maximum date",
29370     /**
29371      * @cfg {String} format
29372      * The default date format string which can be overriden for localization support.  The format must be
29373      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29374      */
29375     format : "m/d/y",
29376     /**
29377      * @cfg {Array} disabledDays
29378      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29379      */
29380     disabledDays : null,
29381     /**
29382      * @cfg {String} disabledDaysText
29383      * The tooltip to display when the date falls on a disabled day (defaults to "")
29384      */
29385     disabledDaysText : "",
29386     /**
29387      * @cfg {RegExp} disabledDatesRE
29388      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29389      */
29390     disabledDatesRE : null,
29391     /**
29392      * @cfg {String} disabledDatesText
29393      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29394      */
29395     disabledDatesText : "",
29396     /**
29397      * @cfg {Boolean} constrainToViewport
29398      * True to constrain the date picker to the viewport (defaults to true)
29399      */
29400     constrainToViewport : true,
29401     /**
29402      * @cfg {Array} monthNames
29403      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29404      */
29405     monthNames : Date.monthNames,
29406     /**
29407      * @cfg {Array} dayNames
29408      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29409      */
29410     dayNames : Date.dayNames,
29411     /**
29412      * @cfg {String} nextText
29413      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29414      */
29415     nextText: 'Next Month (Control+Right)',
29416     /**
29417      * @cfg {String} prevText
29418      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29419      */
29420     prevText: 'Previous Month (Control+Left)',
29421     /**
29422      * @cfg {String} monthYearText
29423      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29424      */
29425     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29426     /**
29427      * @cfg {Number} startDay
29428      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29429      */
29430     startDay : 0,
29431     /**
29432      * @cfg {Bool} showClear
29433      * Show a clear button (usefull for date form elements that can be blank.)
29434      */
29435     
29436     showClear: false,
29437     
29438     /**
29439      * Sets the value of the date field
29440      * @param {Date} value The date to set
29441      */
29442     setValue : function(value){
29443         var old = this.value;
29444         
29445         if (typeof(value) == 'string') {
29446          
29447             value = Date.parseDate(value, this.format);
29448         }
29449         if (!value) {
29450             value = new Date();
29451         }
29452         
29453         this.value = value.clearTime(true);
29454         if(this.el){
29455             this.update(this.value);
29456         }
29457     },
29458
29459     /**
29460      * Gets the current selected value of the date field
29461      * @return {Date} The selected date
29462      */
29463     getValue : function(){
29464         return this.value;
29465     },
29466
29467     // private
29468     focus : function(){
29469         if(this.el){
29470             this.update(this.activeDate);
29471         }
29472     },
29473
29474     // privateval
29475     onRender : function(container, position){
29476         
29477         var m = [
29478              '<table cellspacing="0">',
29479                 '<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>',
29480                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29481         var dn = this.dayNames;
29482         for(var i = 0; i < 7; i++){
29483             var d = this.startDay+i;
29484             if(d > 6){
29485                 d = d-7;
29486             }
29487             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29488         }
29489         m[m.length] = "</tr></thead><tbody><tr>";
29490         for(var i = 0; i < 42; i++) {
29491             if(i % 7 == 0 && i != 0){
29492                 m[m.length] = "</tr><tr>";
29493             }
29494             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29495         }
29496         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29497             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29498
29499         var el = document.createElement("div");
29500         el.className = "x-date-picker";
29501         el.innerHTML = m.join("");
29502
29503         container.dom.insertBefore(el, position);
29504
29505         this.el = Roo.get(el);
29506         this.eventEl = Roo.get(el.firstChild);
29507
29508         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29509             handler: this.showPrevMonth,
29510             scope: this,
29511             preventDefault:true,
29512             stopDefault:true
29513         });
29514
29515         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29516             handler: this.showNextMonth,
29517             scope: this,
29518             preventDefault:true,
29519             stopDefault:true
29520         });
29521
29522         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29523
29524         this.monthPicker = this.el.down('div.x-date-mp');
29525         this.monthPicker.enableDisplayMode('block');
29526         
29527         var kn = new Roo.KeyNav(this.eventEl, {
29528             "left" : function(e){
29529                 e.ctrlKey ?
29530                     this.showPrevMonth() :
29531                     this.update(this.activeDate.add("d", -1));
29532             },
29533
29534             "right" : function(e){
29535                 e.ctrlKey ?
29536                     this.showNextMonth() :
29537                     this.update(this.activeDate.add("d", 1));
29538             },
29539
29540             "up" : function(e){
29541                 e.ctrlKey ?
29542                     this.showNextYear() :
29543                     this.update(this.activeDate.add("d", -7));
29544             },
29545
29546             "down" : function(e){
29547                 e.ctrlKey ?
29548                     this.showPrevYear() :
29549                     this.update(this.activeDate.add("d", 7));
29550             },
29551
29552             "pageUp" : function(e){
29553                 this.showNextMonth();
29554             },
29555
29556             "pageDown" : function(e){
29557                 this.showPrevMonth();
29558             },
29559
29560             "enter" : function(e){
29561                 e.stopPropagation();
29562                 return true;
29563             },
29564
29565             scope : this
29566         });
29567
29568         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29569
29570         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29571
29572         this.el.unselectable();
29573         
29574         this.cells = this.el.select("table.x-date-inner tbody td");
29575         this.textNodes = this.el.query("table.x-date-inner tbody span");
29576
29577         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29578             text: "&#160;",
29579             tooltip: this.monthYearText
29580         });
29581
29582         this.mbtn.on('click', this.showMonthPicker, this);
29583         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29584
29585
29586         var today = (new Date()).dateFormat(this.format);
29587         
29588         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29589         if (this.showClear) {
29590             baseTb.add( new Roo.Toolbar.Fill());
29591         }
29592         baseTb.add({
29593             text: String.format(this.todayText, today),
29594             tooltip: String.format(this.todayTip, today),
29595             handler: this.selectToday,
29596             scope: this
29597         });
29598         
29599         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29600             
29601         //});
29602         if (this.showClear) {
29603             
29604             baseTb.add( new Roo.Toolbar.Fill());
29605             baseTb.add({
29606                 text: '&#160;',
29607                 cls: 'x-btn-icon x-btn-clear',
29608                 handler: function() {
29609                     //this.value = '';
29610                     this.fireEvent("select", this, '');
29611                 },
29612                 scope: this
29613             });
29614         }
29615         
29616         
29617         if(Roo.isIE){
29618             this.el.repaint();
29619         }
29620         this.update(this.value);
29621     },
29622
29623     createMonthPicker : function(){
29624         if(!this.monthPicker.dom.firstChild){
29625             var buf = ['<table border="0" cellspacing="0">'];
29626             for(var i = 0; i < 6; i++){
29627                 buf.push(
29628                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29629                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29630                     i == 0 ?
29631                     '<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>' :
29632                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29633                 );
29634             }
29635             buf.push(
29636                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29637                     this.okText,
29638                     '</button><button type="button" class="x-date-mp-cancel">',
29639                     this.cancelText,
29640                     '</button></td></tr>',
29641                 '</table>'
29642             );
29643             this.monthPicker.update(buf.join(''));
29644             this.monthPicker.on('click', this.onMonthClick, this);
29645             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29646
29647             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29648             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29649
29650             this.mpMonths.each(function(m, a, i){
29651                 i += 1;
29652                 if((i%2) == 0){
29653                     m.dom.xmonth = 5 + Math.round(i * .5);
29654                 }else{
29655                     m.dom.xmonth = Math.round((i-1) * .5);
29656                 }
29657             });
29658         }
29659     },
29660
29661     showMonthPicker : function(){
29662         this.createMonthPicker();
29663         var size = this.el.getSize();
29664         this.monthPicker.setSize(size);
29665         this.monthPicker.child('table').setSize(size);
29666
29667         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29668         this.updateMPMonth(this.mpSelMonth);
29669         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29670         this.updateMPYear(this.mpSelYear);
29671
29672         this.monthPicker.slideIn('t', {duration:.2});
29673     },
29674
29675     updateMPYear : function(y){
29676         this.mpyear = y;
29677         var ys = this.mpYears.elements;
29678         for(var i = 1; i <= 10; i++){
29679             var td = ys[i-1], y2;
29680             if((i%2) == 0){
29681                 y2 = y + Math.round(i * .5);
29682                 td.firstChild.innerHTML = y2;
29683                 td.xyear = y2;
29684             }else{
29685                 y2 = y - (5-Math.round(i * .5));
29686                 td.firstChild.innerHTML = y2;
29687                 td.xyear = y2;
29688             }
29689             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29690         }
29691     },
29692
29693     updateMPMonth : function(sm){
29694         this.mpMonths.each(function(m, a, i){
29695             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29696         });
29697     },
29698
29699     selectMPMonth: function(m){
29700         
29701     },
29702
29703     onMonthClick : function(e, t){
29704         e.stopEvent();
29705         var el = new Roo.Element(t), pn;
29706         if(el.is('button.x-date-mp-cancel')){
29707             this.hideMonthPicker();
29708         }
29709         else if(el.is('button.x-date-mp-ok')){
29710             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29711             this.hideMonthPicker();
29712         }
29713         else if(pn = el.up('td.x-date-mp-month', 2)){
29714             this.mpMonths.removeClass('x-date-mp-sel');
29715             pn.addClass('x-date-mp-sel');
29716             this.mpSelMonth = pn.dom.xmonth;
29717         }
29718         else if(pn = el.up('td.x-date-mp-year', 2)){
29719             this.mpYears.removeClass('x-date-mp-sel');
29720             pn.addClass('x-date-mp-sel');
29721             this.mpSelYear = pn.dom.xyear;
29722         }
29723         else if(el.is('a.x-date-mp-prev')){
29724             this.updateMPYear(this.mpyear-10);
29725         }
29726         else if(el.is('a.x-date-mp-next')){
29727             this.updateMPYear(this.mpyear+10);
29728         }
29729     },
29730
29731     onMonthDblClick : function(e, t){
29732         e.stopEvent();
29733         var el = new Roo.Element(t), pn;
29734         if(pn = el.up('td.x-date-mp-month', 2)){
29735             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29736             this.hideMonthPicker();
29737         }
29738         else if(pn = el.up('td.x-date-mp-year', 2)){
29739             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29740             this.hideMonthPicker();
29741         }
29742     },
29743
29744     hideMonthPicker : function(disableAnim){
29745         if(this.monthPicker){
29746             if(disableAnim === true){
29747                 this.monthPicker.hide();
29748             }else{
29749                 this.monthPicker.slideOut('t', {duration:.2});
29750             }
29751         }
29752     },
29753
29754     // private
29755     showPrevMonth : function(e){
29756         this.update(this.activeDate.add("mo", -1));
29757     },
29758
29759     // private
29760     showNextMonth : function(e){
29761         this.update(this.activeDate.add("mo", 1));
29762     },
29763
29764     // private
29765     showPrevYear : function(){
29766         this.update(this.activeDate.add("y", -1));
29767     },
29768
29769     // private
29770     showNextYear : function(){
29771         this.update(this.activeDate.add("y", 1));
29772     },
29773
29774     // private
29775     handleMouseWheel : function(e){
29776         var delta = e.getWheelDelta();
29777         if(delta > 0){
29778             this.showPrevMonth();
29779             e.stopEvent();
29780         } else if(delta < 0){
29781             this.showNextMonth();
29782             e.stopEvent();
29783         }
29784     },
29785
29786     // private
29787     handleDateClick : function(e, t){
29788         e.stopEvent();
29789         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29790             this.setValue(new Date(t.dateValue));
29791             this.fireEvent("select", this, this.value);
29792         }
29793     },
29794
29795     // private
29796     selectToday : function(){
29797         this.setValue(new Date().clearTime());
29798         this.fireEvent("select", this, this.value);
29799     },
29800
29801     // private
29802     update : function(date)
29803     {
29804         var vd = this.activeDate;
29805         this.activeDate = date;
29806         if(vd && this.el){
29807             var t = date.getTime();
29808             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29809                 this.cells.removeClass("x-date-selected");
29810                 this.cells.each(function(c){
29811                    if(c.dom.firstChild.dateValue == t){
29812                        c.addClass("x-date-selected");
29813                        setTimeout(function(){
29814                             try{c.dom.firstChild.focus();}catch(e){}
29815                        }, 50);
29816                        return false;
29817                    }
29818                 });
29819                 return;
29820             }
29821         }
29822         
29823         var days = date.getDaysInMonth();
29824         var firstOfMonth = date.getFirstDateOfMonth();
29825         var startingPos = firstOfMonth.getDay()-this.startDay;
29826
29827         if(startingPos <= this.startDay){
29828             startingPos += 7;
29829         }
29830
29831         var pm = date.add("mo", -1);
29832         var prevStart = pm.getDaysInMonth()-startingPos;
29833
29834         var cells = this.cells.elements;
29835         var textEls = this.textNodes;
29836         days += startingPos;
29837
29838         // convert everything to numbers so it's fast
29839         var day = 86400000;
29840         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29841         var today = new Date().clearTime().getTime();
29842         var sel = date.clearTime().getTime();
29843         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29844         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29845         var ddMatch = this.disabledDatesRE;
29846         var ddText = this.disabledDatesText;
29847         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29848         var ddaysText = this.disabledDaysText;
29849         var format = this.format;
29850
29851         var setCellClass = function(cal, cell){
29852             cell.title = "";
29853             var t = d.getTime();
29854             cell.firstChild.dateValue = t;
29855             if(t == today){
29856                 cell.className += " x-date-today";
29857                 cell.title = cal.todayText;
29858             }
29859             if(t == sel){
29860                 cell.className += " x-date-selected";
29861                 setTimeout(function(){
29862                     try{cell.firstChild.focus();}catch(e){}
29863                 }, 50);
29864             }
29865             // disabling
29866             if(t < min) {
29867                 cell.className = " x-date-disabled";
29868                 cell.title = cal.minText;
29869                 return;
29870             }
29871             if(t > max) {
29872                 cell.className = " x-date-disabled";
29873                 cell.title = cal.maxText;
29874                 return;
29875             }
29876             if(ddays){
29877                 if(ddays.indexOf(d.getDay()) != -1){
29878                     cell.title = ddaysText;
29879                     cell.className = " x-date-disabled";
29880                 }
29881             }
29882             if(ddMatch && format){
29883                 var fvalue = d.dateFormat(format);
29884                 if(ddMatch.test(fvalue)){
29885                     cell.title = ddText.replace("%0", fvalue);
29886                     cell.className = " x-date-disabled";
29887                 }
29888             }
29889         };
29890
29891         var i = 0;
29892         for(; i < startingPos; i++) {
29893             textEls[i].innerHTML = (++prevStart);
29894             d.setDate(d.getDate()+1);
29895             cells[i].className = "x-date-prevday";
29896             setCellClass(this, cells[i]);
29897         }
29898         for(; i < days; i++){
29899             intDay = i - startingPos + 1;
29900             textEls[i].innerHTML = (intDay);
29901             d.setDate(d.getDate()+1);
29902             cells[i].className = "x-date-active";
29903             setCellClass(this, cells[i]);
29904         }
29905         var extraDays = 0;
29906         for(; i < 42; i++) {
29907              textEls[i].innerHTML = (++extraDays);
29908              d.setDate(d.getDate()+1);
29909              cells[i].className = "x-date-nextday";
29910              setCellClass(this, cells[i]);
29911         }
29912
29913         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29914         this.fireEvent('monthchange', this, date);
29915         
29916         if(!this.internalRender){
29917             var main = this.el.dom.firstChild;
29918             var w = main.offsetWidth;
29919             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29920             Roo.fly(main).setWidth(w);
29921             this.internalRender = true;
29922             // opera does not respect the auto grow header center column
29923             // then, after it gets a width opera refuses to recalculate
29924             // without a second pass
29925             if(Roo.isOpera && !this.secondPass){
29926                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29927                 this.secondPass = true;
29928                 this.update.defer(10, this, [date]);
29929             }
29930         }
29931         
29932         
29933     }
29934 });        /*
29935  * Based on:
29936  * Ext JS Library 1.1.1
29937  * Copyright(c) 2006-2007, Ext JS, LLC.
29938  *
29939  * Originally Released Under LGPL - original licence link has changed is not relivant.
29940  *
29941  * Fork - LGPL
29942  * <script type="text/javascript">
29943  */
29944 /**
29945  * @class Roo.TabPanel
29946  * @extends Roo.util.Observable
29947  * A lightweight tab container.
29948  * <br><br>
29949  * Usage:
29950  * <pre><code>
29951 // basic tabs 1, built from existing content
29952 var tabs = new Roo.TabPanel("tabs1");
29953 tabs.addTab("script", "View Script");
29954 tabs.addTab("markup", "View Markup");
29955 tabs.activate("script");
29956
29957 // more advanced tabs, built from javascript
29958 var jtabs = new Roo.TabPanel("jtabs");
29959 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29960
29961 // set up the UpdateManager
29962 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29963 var updater = tab2.getUpdateManager();
29964 updater.setDefaultUrl("ajax1.htm");
29965 tab2.on('activate', updater.refresh, updater, true);
29966
29967 // Use setUrl for Ajax loading
29968 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29969 tab3.setUrl("ajax2.htm", null, true);
29970
29971 // Disabled tab
29972 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29973 tab4.disable();
29974
29975 jtabs.activate("jtabs-1");
29976  * </code></pre>
29977  * @constructor
29978  * Create a new TabPanel.
29979  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29980  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29981  */
29982 Roo.TabPanel = function(container, config){
29983     /**
29984     * The container element for this TabPanel.
29985     * @type Roo.Element
29986     */
29987     this.el = Roo.get(container, true);
29988     if(config){
29989         if(typeof config == "boolean"){
29990             this.tabPosition = config ? "bottom" : "top";
29991         }else{
29992             Roo.apply(this, config);
29993         }
29994     }
29995     if(this.tabPosition == "bottom"){
29996         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29997         this.el.addClass("x-tabs-bottom");
29998     }
29999     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
30000     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
30001     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
30002     if(Roo.isIE){
30003         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
30004     }
30005     if(this.tabPosition != "bottom"){
30006         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
30007          * @type Roo.Element
30008          */
30009         this.bodyEl = Roo.get(this.createBody(this.el.dom));
30010         this.el.addClass("x-tabs-top");
30011     }
30012     this.items = [];
30013
30014     this.bodyEl.setStyle("position", "relative");
30015
30016     this.active = null;
30017     this.activateDelegate = this.activate.createDelegate(this);
30018
30019     this.addEvents({
30020         /**
30021          * @event tabchange
30022          * Fires when the active tab changes
30023          * @param {Roo.TabPanel} this
30024          * @param {Roo.TabPanelItem} activePanel The new active tab
30025          */
30026         "tabchange": true,
30027         /**
30028          * @event beforetabchange
30029          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
30030          * @param {Roo.TabPanel} this
30031          * @param {Object} e Set cancel to true on this object to cancel the tab change
30032          * @param {Roo.TabPanelItem} tab The tab being changed to
30033          */
30034         "beforetabchange" : true
30035     });
30036
30037     Roo.EventManager.onWindowResize(this.onResize, this);
30038     this.cpad = this.el.getPadding("lr");
30039     this.hiddenCount = 0;
30040
30041
30042     // toolbar on the tabbar support...
30043     if (this.toolbar) {
30044         var tcfg = this.toolbar;
30045         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
30046         this.toolbar = new Roo.Toolbar(tcfg);
30047         if (Roo.isSafari) {
30048             var tbl = tcfg.container.child('table', true);
30049             tbl.setAttribute('width', '100%');
30050         }
30051         
30052     }
30053    
30054
30055
30056     Roo.TabPanel.superclass.constructor.call(this);
30057 };
30058
30059 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
30060     /*
30061      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
30062      */
30063     tabPosition : "top",
30064     /*
30065      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
30066      */
30067     currentTabWidth : 0,
30068     /*
30069      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
30070      */
30071     minTabWidth : 40,
30072     /*
30073      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
30074      */
30075     maxTabWidth : 250,
30076     /*
30077      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
30078      */
30079     preferredTabWidth : 175,
30080     /*
30081      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
30082      */
30083     resizeTabs : false,
30084     /*
30085      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
30086      */
30087     monitorResize : true,
30088     /*
30089      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
30090      */
30091     toolbar : false,
30092
30093     /**
30094      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
30095      * @param {String} id The id of the div to use <b>or create</b>
30096      * @param {String} text The text for the tab
30097      * @param {String} content (optional) Content to put in the TabPanelItem body
30098      * @param {Boolean} closable (optional) True to create a close icon on the tab
30099      * @return {Roo.TabPanelItem} The created TabPanelItem
30100      */
30101     addTab : function(id, text, content, closable){
30102         var item = new Roo.TabPanelItem(this, id, text, closable);
30103         this.addTabItem(item);
30104         if(content){
30105             item.setContent(content);
30106         }
30107         return item;
30108     },
30109
30110     /**
30111      * Returns the {@link Roo.TabPanelItem} with the specified id/index
30112      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
30113      * @return {Roo.TabPanelItem}
30114      */
30115     getTab : function(id){
30116         return this.items[id];
30117     },
30118
30119     /**
30120      * Hides the {@link Roo.TabPanelItem} with the specified id/index
30121      * @param {String/Number} id The id or index of the TabPanelItem to hide.
30122      */
30123     hideTab : function(id){
30124         var t = this.items[id];
30125         if(!t.isHidden()){
30126            t.setHidden(true);
30127            this.hiddenCount++;
30128            this.autoSizeTabs();
30129         }
30130     },
30131
30132     /**
30133      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
30134      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
30135      */
30136     unhideTab : function(id){
30137         var t = this.items[id];
30138         if(t.isHidden()){
30139            t.setHidden(false);
30140            this.hiddenCount--;
30141            this.autoSizeTabs();
30142         }
30143     },
30144
30145     /**
30146      * Adds an existing {@link Roo.TabPanelItem}.
30147      * @param {Roo.TabPanelItem} item The TabPanelItem to add
30148      */
30149     addTabItem : function(item){
30150         this.items[item.id] = item;
30151         this.items.push(item);
30152         if(this.resizeTabs){
30153            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
30154            this.autoSizeTabs();
30155         }else{
30156             item.autoSize();
30157         }
30158     },
30159
30160     /**
30161      * Removes a {@link Roo.TabPanelItem}.
30162      * @param {String/Number} id The id or index of the TabPanelItem to remove.
30163      */
30164     removeTab : function(id){
30165         var items = this.items;
30166         var tab = items[id];
30167         if(!tab) { return; }
30168         var index = items.indexOf(tab);
30169         if(this.active == tab && items.length > 1){
30170             var newTab = this.getNextAvailable(index);
30171             if(newTab) {
30172                 newTab.activate();
30173             }
30174         }
30175         this.stripEl.dom.removeChild(tab.pnode.dom);
30176         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30177             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30178         }
30179         items.splice(index, 1);
30180         delete this.items[tab.id];
30181         tab.fireEvent("close", tab);
30182         tab.purgeListeners();
30183         this.autoSizeTabs();
30184     },
30185
30186     getNextAvailable : function(start){
30187         var items = this.items;
30188         var index = start;
30189         // look for a next tab that will slide over to
30190         // replace the one being removed
30191         while(index < items.length){
30192             var item = items[++index];
30193             if(item && !item.isHidden()){
30194                 return item;
30195             }
30196         }
30197         // if one isn't found select the previous tab (on the left)
30198         index = start;
30199         while(index >= 0){
30200             var item = items[--index];
30201             if(item && !item.isHidden()){
30202                 return item;
30203             }
30204         }
30205         return null;
30206     },
30207
30208     /**
30209      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30210      * @param {String/Number} id The id or index of the TabPanelItem to disable.
30211      */
30212     disableTab : function(id){
30213         var tab = this.items[id];
30214         if(tab && this.active != tab){
30215             tab.disable();
30216         }
30217     },
30218
30219     /**
30220      * Enables a {@link Roo.TabPanelItem} that is disabled.
30221      * @param {String/Number} id The id or index of the TabPanelItem to enable.
30222      */
30223     enableTab : function(id){
30224         var tab = this.items[id];
30225         tab.enable();
30226     },
30227
30228     /**
30229      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30230      * @param {String/Number} id The id or index of the TabPanelItem to activate.
30231      * @return {Roo.TabPanelItem} The TabPanelItem.
30232      */
30233     activate : function(id){
30234         var tab = this.items[id];
30235         if(!tab){
30236             return null;
30237         }
30238         if(tab == this.active || tab.disabled){
30239             return tab;
30240         }
30241         var e = {};
30242         this.fireEvent("beforetabchange", this, e, tab);
30243         if(e.cancel !== true && !tab.disabled){
30244             if(this.active){
30245                 this.active.hide();
30246             }
30247             this.active = this.items[id];
30248             this.active.show();
30249             this.fireEvent("tabchange", this, this.active);
30250         }
30251         return tab;
30252     },
30253
30254     /**
30255      * Gets the active {@link Roo.TabPanelItem}.
30256      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30257      */
30258     getActiveTab : function(){
30259         return this.active;
30260     },
30261
30262     /**
30263      * Updates the tab body element to fit the height of the container element
30264      * for overflow scrolling
30265      * @param {Number} targetHeight (optional) Override the starting height from the elements height
30266      */
30267     syncHeight : function(targetHeight){
30268         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30269         var bm = this.bodyEl.getMargins();
30270         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30271         this.bodyEl.setHeight(newHeight);
30272         return newHeight;
30273     },
30274
30275     onResize : function(){
30276         if(this.monitorResize){
30277             this.autoSizeTabs();
30278         }
30279     },
30280
30281     /**
30282      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30283      */
30284     beginUpdate : function(){
30285         this.updating = true;
30286     },
30287
30288     /**
30289      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30290      */
30291     endUpdate : function(){
30292         this.updating = false;
30293         this.autoSizeTabs();
30294     },
30295
30296     /**
30297      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30298      */
30299     autoSizeTabs : function(){
30300         var count = this.items.length;
30301         var vcount = count - this.hiddenCount;
30302         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30303             return;
30304         }
30305         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30306         var availWidth = Math.floor(w / vcount);
30307         var b = this.stripBody;
30308         if(b.getWidth() > w){
30309             var tabs = this.items;
30310             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30311             if(availWidth < this.minTabWidth){
30312                 /*if(!this.sleft){    // incomplete scrolling code
30313                     this.createScrollButtons();
30314                 }
30315                 this.showScroll();
30316                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30317             }
30318         }else{
30319             if(this.currentTabWidth < this.preferredTabWidth){
30320                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30321             }
30322         }
30323     },
30324
30325     /**
30326      * Returns the number of tabs in this TabPanel.
30327      * @return {Number}
30328      */
30329      getCount : function(){
30330          return this.items.length;
30331      },
30332
30333     /**
30334      * Resizes all the tabs to the passed width
30335      * @param {Number} The new width
30336      */
30337     setTabWidth : function(width){
30338         this.currentTabWidth = width;
30339         for(var i = 0, len = this.items.length; i < len; i++) {
30340                 if(!this.items[i].isHidden()) {
30341                 this.items[i].setWidth(width);
30342             }
30343         }
30344     },
30345
30346     /**
30347      * Destroys this TabPanel
30348      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30349      */
30350     destroy : function(removeEl){
30351         Roo.EventManager.removeResizeListener(this.onResize, this);
30352         for(var i = 0, len = this.items.length; i < len; i++){
30353             this.items[i].purgeListeners();
30354         }
30355         if(removeEl === true){
30356             this.el.update("");
30357             this.el.remove();
30358         }
30359     }
30360 });
30361
30362 /**
30363  * @class Roo.TabPanelItem
30364  * @extends Roo.util.Observable
30365  * Represents an individual item (tab plus body) in a TabPanel.
30366  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30367  * @param {String} id The id of this TabPanelItem
30368  * @param {String} text The text for the tab of this TabPanelItem
30369  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30370  */
30371 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30372     /**
30373      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30374      * @type Roo.TabPanel
30375      */
30376     this.tabPanel = tabPanel;
30377     /**
30378      * The id for this TabPanelItem
30379      * @type String
30380      */
30381     this.id = id;
30382     /** @private */
30383     this.disabled = false;
30384     /** @private */
30385     this.text = text;
30386     /** @private */
30387     this.loaded = false;
30388     this.closable = closable;
30389
30390     /**
30391      * The body element for this TabPanelItem.
30392      * @type Roo.Element
30393      */
30394     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30395     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30396     this.bodyEl.setStyle("display", "block");
30397     this.bodyEl.setStyle("zoom", "1");
30398     this.hideAction();
30399
30400     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30401     /** @private */
30402     this.el = Roo.get(els.el, true);
30403     this.inner = Roo.get(els.inner, true);
30404     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30405     this.pnode = Roo.get(els.el.parentNode, true);
30406     this.el.on("mousedown", this.onTabMouseDown, this);
30407     this.el.on("click", this.onTabClick, this);
30408     /** @private */
30409     if(closable){
30410         var c = Roo.get(els.close, true);
30411         c.dom.title = this.closeText;
30412         c.addClassOnOver("close-over");
30413         c.on("click", this.closeClick, this);
30414      }
30415
30416     this.addEvents({
30417          /**
30418          * @event activate
30419          * Fires when this tab becomes the active tab.
30420          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30421          * @param {Roo.TabPanelItem} this
30422          */
30423         "activate": true,
30424         /**
30425          * @event beforeclose
30426          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30427          * @param {Roo.TabPanelItem} this
30428          * @param {Object} e Set cancel to true on this object to cancel the close.
30429          */
30430         "beforeclose": true,
30431         /**
30432          * @event close
30433          * Fires when this tab is closed.
30434          * @param {Roo.TabPanelItem} this
30435          */
30436          "close": true,
30437         /**
30438          * @event deactivate
30439          * Fires when this tab is no longer the active tab.
30440          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30441          * @param {Roo.TabPanelItem} this
30442          */
30443          "deactivate" : true
30444     });
30445     this.hidden = false;
30446
30447     Roo.TabPanelItem.superclass.constructor.call(this);
30448 };
30449
30450 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30451     purgeListeners : function(){
30452        Roo.util.Observable.prototype.purgeListeners.call(this);
30453        this.el.removeAllListeners();
30454     },
30455     /**
30456      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30457      */
30458     show : function(){
30459         this.pnode.addClass("on");
30460         this.showAction();
30461         if(Roo.isOpera){
30462             this.tabPanel.stripWrap.repaint();
30463         }
30464         this.fireEvent("activate", this.tabPanel, this);
30465     },
30466
30467     /**
30468      * Returns true if this tab is the active tab.
30469      * @return {Boolean}
30470      */
30471     isActive : function(){
30472         return this.tabPanel.getActiveTab() == this;
30473     },
30474
30475     /**
30476      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30477      */
30478     hide : function(){
30479         this.pnode.removeClass("on");
30480         this.hideAction();
30481         this.fireEvent("deactivate", this.tabPanel, this);
30482     },
30483
30484     hideAction : function(){
30485         this.bodyEl.hide();
30486         this.bodyEl.setStyle("position", "absolute");
30487         this.bodyEl.setLeft("-20000px");
30488         this.bodyEl.setTop("-20000px");
30489     },
30490
30491     showAction : function(){
30492         this.bodyEl.setStyle("position", "relative");
30493         this.bodyEl.setTop("");
30494         this.bodyEl.setLeft("");
30495         this.bodyEl.show();
30496     },
30497
30498     /**
30499      * Set the tooltip for the tab.
30500      * @param {String} tooltip The tab's tooltip
30501      */
30502     setTooltip : function(text){
30503         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30504             this.textEl.dom.qtip = text;
30505             this.textEl.dom.removeAttribute('title');
30506         }else{
30507             this.textEl.dom.title = text;
30508         }
30509     },
30510
30511     onTabClick : function(e){
30512         e.preventDefault();
30513         this.tabPanel.activate(this.id);
30514     },
30515
30516     onTabMouseDown : function(e){
30517         e.preventDefault();
30518         this.tabPanel.activate(this.id);
30519     },
30520
30521     getWidth : function(){
30522         return this.inner.getWidth();
30523     },
30524
30525     setWidth : function(width){
30526         var iwidth = width - this.pnode.getPadding("lr");
30527         this.inner.setWidth(iwidth);
30528         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30529         this.pnode.setWidth(width);
30530     },
30531
30532     /**
30533      * Show or hide the tab
30534      * @param {Boolean} hidden True to hide or false to show.
30535      */
30536     setHidden : function(hidden){
30537         this.hidden = hidden;
30538         this.pnode.setStyle("display", hidden ? "none" : "");
30539     },
30540
30541     /**
30542      * Returns true if this tab is "hidden"
30543      * @return {Boolean}
30544      */
30545     isHidden : function(){
30546         return this.hidden;
30547     },
30548
30549     /**
30550      * Returns the text for this tab
30551      * @return {String}
30552      */
30553     getText : function(){
30554         return this.text;
30555     },
30556
30557     autoSize : function(){
30558         //this.el.beginMeasure();
30559         this.textEl.setWidth(1);
30560         /*
30561          *  #2804 [new] Tabs in Roojs
30562          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30563          */
30564         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30565         //this.el.endMeasure();
30566     },
30567
30568     /**
30569      * Sets the text for the tab (Note: this also sets the tooltip text)
30570      * @param {String} text The tab's text and tooltip
30571      */
30572     setText : function(text){
30573         this.text = text;
30574         this.textEl.update(text);
30575         this.setTooltip(text);
30576         if(!this.tabPanel.resizeTabs){
30577             this.autoSize();
30578         }
30579     },
30580     /**
30581      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30582      */
30583     activate : function(){
30584         this.tabPanel.activate(this.id);
30585     },
30586
30587     /**
30588      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30589      */
30590     disable : function(){
30591         if(this.tabPanel.active != this){
30592             this.disabled = true;
30593             this.pnode.addClass("disabled");
30594         }
30595     },
30596
30597     /**
30598      * Enables this TabPanelItem if it was previously disabled.
30599      */
30600     enable : function(){
30601         this.disabled = false;
30602         this.pnode.removeClass("disabled");
30603     },
30604
30605     /**
30606      * Sets the content for this TabPanelItem.
30607      * @param {String} content The content
30608      * @param {Boolean} loadScripts true to look for and load scripts
30609      */
30610     setContent : function(content, loadScripts){
30611         this.bodyEl.update(content, loadScripts);
30612     },
30613
30614     /**
30615      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30616      * @return {Roo.UpdateManager} The UpdateManager
30617      */
30618     getUpdateManager : function(){
30619         return this.bodyEl.getUpdateManager();
30620     },
30621
30622     /**
30623      * Set a URL to be used to load the content for this TabPanelItem.
30624      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30625      * @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)
30626      * @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)
30627      * @return {Roo.UpdateManager} The UpdateManager
30628      */
30629     setUrl : function(url, params, loadOnce){
30630         if(this.refreshDelegate){
30631             this.un('activate', this.refreshDelegate);
30632         }
30633         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30634         this.on("activate", this.refreshDelegate);
30635         return this.bodyEl.getUpdateManager();
30636     },
30637
30638     /** @private */
30639     _handleRefresh : function(url, params, loadOnce){
30640         if(!loadOnce || !this.loaded){
30641             var updater = this.bodyEl.getUpdateManager();
30642             updater.update(url, params, this._setLoaded.createDelegate(this));
30643         }
30644     },
30645
30646     /**
30647      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30648      *   Will fail silently if the setUrl method has not been called.
30649      *   This does not activate the panel, just updates its content.
30650      */
30651     refresh : function(){
30652         if(this.refreshDelegate){
30653            this.loaded = false;
30654            this.refreshDelegate();
30655         }
30656     },
30657
30658     /** @private */
30659     _setLoaded : function(){
30660         this.loaded = true;
30661     },
30662
30663     /** @private */
30664     closeClick : function(e){
30665         var o = {};
30666         e.stopEvent();
30667         this.fireEvent("beforeclose", this, o);
30668         if(o.cancel !== true){
30669             this.tabPanel.removeTab(this.id);
30670         }
30671     },
30672     /**
30673      * The text displayed in the tooltip for the close icon.
30674      * @type String
30675      */
30676     closeText : "Close this tab"
30677 });
30678
30679 /** @private */
30680 Roo.TabPanel.prototype.createStrip = function(container){
30681     var strip = document.createElement("div");
30682     strip.className = "x-tabs-wrap";
30683     container.appendChild(strip);
30684     return strip;
30685 };
30686 /** @private */
30687 Roo.TabPanel.prototype.createStripList = function(strip){
30688     // div wrapper for retard IE
30689     // returns the "tr" element.
30690     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30691         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30692         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30693     return strip.firstChild.firstChild.firstChild.firstChild;
30694 };
30695 /** @private */
30696 Roo.TabPanel.prototype.createBody = function(container){
30697     var body = document.createElement("div");
30698     Roo.id(body, "tab-body");
30699     Roo.fly(body).addClass("x-tabs-body");
30700     container.appendChild(body);
30701     return body;
30702 };
30703 /** @private */
30704 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30705     var body = Roo.getDom(id);
30706     if(!body){
30707         body = document.createElement("div");
30708         body.id = id;
30709     }
30710     Roo.fly(body).addClass("x-tabs-item-body");
30711     bodyEl.insertBefore(body, bodyEl.firstChild);
30712     return body;
30713 };
30714 /** @private */
30715 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30716     var td = document.createElement("td");
30717     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30718     //stripEl.appendChild(td);
30719     if(closable){
30720         td.className = "x-tabs-closable";
30721         if(!this.closeTpl){
30722             this.closeTpl = new Roo.Template(
30723                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30724                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30725                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30726             );
30727         }
30728         var el = this.closeTpl.overwrite(td, {"text": text});
30729         var close = el.getElementsByTagName("div")[0];
30730         var inner = el.getElementsByTagName("em")[0];
30731         return {"el": el, "close": close, "inner": inner};
30732     } else {
30733         if(!this.tabTpl){
30734             this.tabTpl = new Roo.Template(
30735                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30736                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30737             );
30738         }
30739         var el = this.tabTpl.overwrite(td, {"text": text});
30740         var inner = el.getElementsByTagName("em")[0];
30741         return {"el": el, "inner": inner};
30742     }
30743 };/*
30744  * Based on:
30745  * Ext JS Library 1.1.1
30746  * Copyright(c) 2006-2007, Ext JS, LLC.
30747  *
30748  * Originally Released Under LGPL - original licence link has changed is not relivant.
30749  *
30750  * Fork - LGPL
30751  * <script type="text/javascript">
30752  */
30753
30754 /**
30755  * @class Roo.Button
30756  * @extends Roo.util.Observable
30757  * Simple Button class
30758  * @cfg {String} text The button text
30759  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30760  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30761  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30762  * @cfg {Object} scope The scope of the handler
30763  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30764  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30765  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30766  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30767  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30768  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30769    applies if enableToggle = true)
30770  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30771  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30772   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30773  * @constructor
30774  * Create a new button
30775  * @param {Object} config The config object
30776  */
30777 Roo.Button = function(renderTo, config)
30778 {
30779     if (!config) {
30780         config = renderTo;
30781         renderTo = config.renderTo || false;
30782     }
30783     
30784     Roo.apply(this, config);
30785     this.addEvents({
30786         /**
30787              * @event click
30788              * Fires when this button is clicked
30789              * @param {Button} this
30790              * @param {EventObject} e The click event
30791              */
30792             "click" : true,
30793         /**
30794              * @event toggle
30795              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30796              * @param {Button} this
30797              * @param {Boolean} pressed
30798              */
30799             "toggle" : true,
30800         /**
30801              * @event mouseover
30802              * Fires when the mouse hovers over the button
30803              * @param {Button} this
30804              * @param {Event} e The event object
30805              */
30806         'mouseover' : true,
30807         /**
30808              * @event mouseout
30809              * Fires when the mouse exits the button
30810              * @param {Button} this
30811              * @param {Event} e The event object
30812              */
30813         'mouseout': true,
30814          /**
30815              * @event render
30816              * Fires when the button is rendered
30817              * @param {Button} this
30818              */
30819         'render': true
30820     });
30821     if(this.menu){
30822         this.menu = Roo.menu.MenuMgr.get(this.menu);
30823     }
30824     // register listeners first!!  - so render can be captured..
30825     Roo.util.Observable.call(this);
30826     if(renderTo){
30827         this.render(renderTo);
30828     }
30829     
30830   
30831 };
30832
30833 Roo.extend(Roo.Button, Roo.util.Observable, {
30834     /**
30835      * 
30836      */
30837     
30838     /**
30839      * Read-only. True if this button is hidden
30840      * @type Boolean
30841      */
30842     hidden : false,
30843     /**
30844      * Read-only. True if this button is disabled
30845      * @type Boolean
30846      */
30847     disabled : false,
30848     /**
30849      * Read-only. True if this button is pressed (only if enableToggle = true)
30850      * @type Boolean
30851      */
30852     pressed : false,
30853
30854     /**
30855      * @cfg {Number} tabIndex 
30856      * The DOM tabIndex for this button (defaults to undefined)
30857      */
30858     tabIndex : undefined,
30859
30860     /**
30861      * @cfg {Boolean} enableToggle
30862      * True to enable pressed/not pressed toggling (defaults to false)
30863      */
30864     enableToggle: false,
30865     /**
30866      * @cfg {Roo.menu.Menu} menu
30867      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30868      */
30869     menu : undefined,
30870     /**
30871      * @cfg {String} menuAlign
30872      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30873      */
30874     menuAlign : "tl-bl?",
30875
30876     /**
30877      * @cfg {String} iconCls
30878      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30879      */
30880     iconCls : undefined,
30881     /**
30882      * @cfg {String} type
30883      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30884      */
30885     type : 'button',
30886
30887     // private
30888     menuClassTarget: 'tr',
30889
30890     /**
30891      * @cfg {String} clickEvent
30892      * The type of event to map to the button's event handler (defaults to 'click')
30893      */
30894     clickEvent : 'click',
30895
30896     /**
30897      * @cfg {Boolean} handleMouseEvents
30898      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30899      */
30900     handleMouseEvents : true,
30901
30902     /**
30903      * @cfg {String} tooltipType
30904      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30905      */
30906     tooltipType : 'qtip',
30907
30908     /**
30909      * @cfg {String} cls
30910      * A CSS class to apply to the button's main element.
30911      */
30912     
30913     /**
30914      * @cfg {Roo.Template} template (Optional)
30915      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30916      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30917      * require code modifications if required elements (e.g. a button) aren't present.
30918      */
30919
30920     // private
30921     render : function(renderTo){
30922         var btn;
30923         if(this.hideParent){
30924             this.parentEl = Roo.get(renderTo);
30925         }
30926         if(!this.dhconfig){
30927             if(!this.template){
30928                 if(!Roo.Button.buttonTemplate){
30929                     // hideous table template
30930                     Roo.Button.buttonTemplate = new Roo.Template(
30931                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30932                         '<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>',
30933                         "</tr></tbody></table>");
30934                 }
30935                 this.template = Roo.Button.buttonTemplate;
30936             }
30937             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30938             var btnEl = btn.child("button:first");
30939             btnEl.on('focus', this.onFocus, this);
30940             btnEl.on('blur', this.onBlur, this);
30941             if(this.cls){
30942                 btn.addClass(this.cls);
30943             }
30944             if(this.icon){
30945                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30946             }
30947             if(this.iconCls){
30948                 btnEl.addClass(this.iconCls);
30949                 if(!this.cls){
30950                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30951                 }
30952             }
30953             if(this.tabIndex !== undefined){
30954                 btnEl.dom.tabIndex = this.tabIndex;
30955             }
30956             if(this.tooltip){
30957                 if(typeof this.tooltip == 'object'){
30958                     Roo.QuickTips.tips(Roo.apply({
30959                           target: btnEl.id
30960                     }, this.tooltip));
30961                 } else {
30962                     btnEl.dom[this.tooltipType] = this.tooltip;
30963                 }
30964             }
30965         }else{
30966             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30967         }
30968         this.el = btn;
30969         if(this.id){
30970             this.el.dom.id = this.el.id = this.id;
30971         }
30972         if(this.menu){
30973             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30974             this.menu.on("show", this.onMenuShow, this);
30975             this.menu.on("hide", this.onMenuHide, this);
30976         }
30977         btn.addClass("x-btn");
30978         if(Roo.isIE && !Roo.isIE7){
30979             this.autoWidth.defer(1, this);
30980         }else{
30981             this.autoWidth();
30982         }
30983         if(this.handleMouseEvents){
30984             btn.on("mouseover", this.onMouseOver, this);
30985             btn.on("mouseout", this.onMouseOut, this);
30986             btn.on("mousedown", this.onMouseDown, this);
30987         }
30988         btn.on(this.clickEvent, this.onClick, this);
30989         //btn.on("mouseup", this.onMouseUp, this);
30990         if(this.hidden){
30991             this.hide();
30992         }
30993         if(this.disabled){
30994             this.disable();
30995         }
30996         Roo.ButtonToggleMgr.register(this);
30997         if(this.pressed){
30998             this.el.addClass("x-btn-pressed");
30999         }
31000         if(this.repeat){
31001             var repeater = new Roo.util.ClickRepeater(btn,
31002                 typeof this.repeat == "object" ? this.repeat : {}
31003             );
31004             repeater.on("click", this.onClick,  this);
31005         }
31006         
31007         this.fireEvent('render', this);
31008         
31009     },
31010     /**
31011      * Returns the button's underlying element
31012      * @return {Roo.Element} The element
31013      */
31014     getEl : function(){
31015         return this.el;  
31016     },
31017     
31018     /**
31019      * Destroys this Button and removes any listeners.
31020      */
31021     destroy : function(){
31022         Roo.ButtonToggleMgr.unregister(this);
31023         this.el.removeAllListeners();
31024         this.purgeListeners();
31025         this.el.remove();
31026     },
31027
31028     // private
31029     autoWidth : function(){
31030         if(this.el){
31031             this.el.setWidth("auto");
31032             if(Roo.isIE7 && Roo.isStrict){
31033                 var ib = this.el.child('button');
31034                 if(ib && ib.getWidth() > 20){
31035                     ib.clip();
31036                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31037                 }
31038             }
31039             if(this.minWidth){
31040                 if(this.hidden){
31041                     this.el.beginMeasure();
31042                 }
31043                 if(this.el.getWidth() < this.minWidth){
31044                     this.el.setWidth(this.minWidth);
31045                 }
31046                 if(this.hidden){
31047                     this.el.endMeasure();
31048                 }
31049             }
31050         }
31051     },
31052
31053     /**
31054      * Assigns this button's click handler
31055      * @param {Function} handler The function to call when the button is clicked
31056      * @param {Object} scope (optional) Scope for the function passed in
31057      */
31058     setHandler : function(handler, scope){
31059         this.handler = handler;
31060         this.scope = scope;  
31061     },
31062     
31063     /**
31064      * Sets this button's text
31065      * @param {String} text The button text
31066      */
31067     setText : function(text){
31068         this.text = text;
31069         if(this.el){
31070             this.el.child("td.x-btn-center button.x-btn-text").update(text);
31071         }
31072         this.autoWidth();
31073     },
31074     
31075     /**
31076      * Gets the text for this button
31077      * @return {String} The button text
31078      */
31079     getText : function(){
31080         return this.text;  
31081     },
31082     
31083     /**
31084      * Show this button
31085      */
31086     show: function(){
31087         this.hidden = false;
31088         if(this.el){
31089             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
31090         }
31091     },
31092     
31093     /**
31094      * Hide this button
31095      */
31096     hide: function(){
31097         this.hidden = true;
31098         if(this.el){
31099             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
31100         }
31101     },
31102     
31103     /**
31104      * Convenience function for boolean show/hide
31105      * @param {Boolean} visible True to show, false to hide
31106      */
31107     setVisible: function(visible){
31108         if(visible) {
31109             this.show();
31110         }else{
31111             this.hide();
31112         }
31113     },
31114     
31115     /**
31116      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
31117      * @param {Boolean} state (optional) Force a particular state
31118      */
31119     toggle : function(state){
31120         state = state === undefined ? !this.pressed : state;
31121         if(state != this.pressed){
31122             if(state){
31123                 this.el.addClass("x-btn-pressed");
31124                 this.pressed = true;
31125                 this.fireEvent("toggle", this, true);
31126             }else{
31127                 this.el.removeClass("x-btn-pressed");
31128                 this.pressed = false;
31129                 this.fireEvent("toggle", this, false);
31130             }
31131             if(this.toggleHandler){
31132                 this.toggleHandler.call(this.scope || this, this, state);
31133             }
31134         }
31135     },
31136     
31137     /**
31138      * Focus the button
31139      */
31140     focus : function(){
31141         this.el.child('button:first').focus();
31142     },
31143     
31144     /**
31145      * Disable this button
31146      */
31147     disable : function(){
31148         if(this.el){
31149             this.el.addClass("x-btn-disabled");
31150         }
31151         this.disabled = true;
31152     },
31153     
31154     /**
31155      * Enable this button
31156      */
31157     enable : function(){
31158         if(this.el){
31159             this.el.removeClass("x-btn-disabled");
31160         }
31161         this.disabled = false;
31162     },
31163
31164     /**
31165      * Convenience function for boolean enable/disable
31166      * @param {Boolean} enabled True to enable, false to disable
31167      */
31168     setDisabled : function(v){
31169         this[v !== true ? "enable" : "disable"]();
31170     },
31171
31172     // private
31173     onClick : function(e)
31174     {
31175         if(e){
31176             e.preventDefault();
31177         }
31178         if(e.button != 0){
31179             return;
31180         }
31181         if(!this.disabled){
31182             if(this.enableToggle){
31183                 this.toggle();
31184             }
31185             if(this.menu && !this.menu.isVisible()){
31186                 this.menu.show(this.el, this.menuAlign);
31187             }
31188             this.fireEvent("click", this, e);
31189             if(this.handler){
31190                 this.el.removeClass("x-btn-over");
31191                 this.handler.call(this.scope || this, this, e);
31192             }
31193         }
31194     },
31195     // private
31196     onMouseOver : function(e){
31197         if(!this.disabled){
31198             this.el.addClass("x-btn-over");
31199             this.fireEvent('mouseover', this, e);
31200         }
31201     },
31202     // private
31203     onMouseOut : function(e){
31204         if(!e.within(this.el,  true)){
31205             this.el.removeClass("x-btn-over");
31206             this.fireEvent('mouseout', this, e);
31207         }
31208     },
31209     // private
31210     onFocus : function(e){
31211         if(!this.disabled){
31212             this.el.addClass("x-btn-focus");
31213         }
31214     },
31215     // private
31216     onBlur : function(e){
31217         this.el.removeClass("x-btn-focus");
31218     },
31219     // private
31220     onMouseDown : function(e){
31221         if(!this.disabled && e.button == 0){
31222             this.el.addClass("x-btn-click");
31223             Roo.get(document).on('mouseup', this.onMouseUp, this);
31224         }
31225     },
31226     // private
31227     onMouseUp : function(e){
31228         if(e.button == 0){
31229             this.el.removeClass("x-btn-click");
31230             Roo.get(document).un('mouseup', this.onMouseUp, this);
31231         }
31232     },
31233     // private
31234     onMenuShow : function(e){
31235         this.el.addClass("x-btn-menu-active");
31236     },
31237     // private
31238     onMenuHide : function(e){
31239         this.el.removeClass("x-btn-menu-active");
31240     }   
31241 });
31242
31243 // Private utility class used by Button
31244 Roo.ButtonToggleMgr = function(){
31245    var groups = {};
31246    
31247    function toggleGroup(btn, state){
31248        if(state){
31249            var g = groups[btn.toggleGroup];
31250            for(var i = 0, l = g.length; i < l; i++){
31251                if(g[i] != btn){
31252                    g[i].toggle(false);
31253                }
31254            }
31255        }
31256    }
31257    
31258    return {
31259        register : function(btn){
31260            if(!btn.toggleGroup){
31261                return;
31262            }
31263            var g = groups[btn.toggleGroup];
31264            if(!g){
31265                g = groups[btn.toggleGroup] = [];
31266            }
31267            g.push(btn);
31268            btn.on("toggle", toggleGroup);
31269        },
31270        
31271        unregister : function(btn){
31272            if(!btn.toggleGroup){
31273                return;
31274            }
31275            var g = groups[btn.toggleGroup];
31276            if(g){
31277                g.remove(btn);
31278                btn.un("toggle", toggleGroup);
31279            }
31280        }
31281    };
31282 }();/*
31283  * Based on:
31284  * Ext JS Library 1.1.1
31285  * Copyright(c) 2006-2007, Ext JS, LLC.
31286  *
31287  * Originally Released Under LGPL - original licence link has changed is not relivant.
31288  *
31289  * Fork - LGPL
31290  * <script type="text/javascript">
31291  */
31292  
31293 /**
31294  * @class Roo.SplitButton
31295  * @extends Roo.Button
31296  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31297  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31298  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31299  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31300  * @cfg {String} arrowTooltip The title attribute of the arrow
31301  * @constructor
31302  * Create a new menu button
31303  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31304  * @param {Object} config The config object
31305  */
31306 Roo.SplitButton = function(renderTo, config){
31307     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31308     /**
31309      * @event arrowclick
31310      * Fires when this button's arrow is clicked
31311      * @param {SplitButton} this
31312      * @param {EventObject} e The click event
31313      */
31314     this.addEvents({"arrowclick":true});
31315 };
31316
31317 Roo.extend(Roo.SplitButton, Roo.Button, {
31318     render : function(renderTo){
31319         // this is one sweet looking template!
31320         var tpl = new Roo.Template(
31321             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31322             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31323             '<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>',
31324             "</tbody></table></td><td>",
31325             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31326             '<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>',
31327             "</tbody></table></td></tr></table>"
31328         );
31329         var btn = tpl.append(renderTo, [this.text, this.type], true);
31330         var btnEl = btn.child("button");
31331         if(this.cls){
31332             btn.addClass(this.cls);
31333         }
31334         if(this.icon){
31335             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31336         }
31337         if(this.iconCls){
31338             btnEl.addClass(this.iconCls);
31339             if(!this.cls){
31340                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31341             }
31342         }
31343         this.el = btn;
31344         if(this.handleMouseEvents){
31345             btn.on("mouseover", this.onMouseOver, this);
31346             btn.on("mouseout", this.onMouseOut, this);
31347             btn.on("mousedown", this.onMouseDown, this);
31348             btn.on("mouseup", this.onMouseUp, this);
31349         }
31350         btn.on(this.clickEvent, this.onClick, this);
31351         if(this.tooltip){
31352             if(typeof this.tooltip == 'object'){
31353                 Roo.QuickTips.tips(Roo.apply({
31354                       target: btnEl.id
31355                 }, this.tooltip));
31356             } else {
31357                 btnEl.dom[this.tooltipType] = this.tooltip;
31358             }
31359         }
31360         if(this.arrowTooltip){
31361             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31362         }
31363         if(this.hidden){
31364             this.hide();
31365         }
31366         if(this.disabled){
31367             this.disable();
31368         }
31369         if(this.pressed){
31370             this.el.addClass("x-btn-pressed");
31371         }
31372         if(Roo.isIE && !Roo.isIE7){
31373             this.autoWidth.defer(1, this);
31374         }else{
31375             this.autoWidth();
31376         }
31377         if(this.menu){
31378             this.menu.on("show", this.onMenuShow, this);
31379             this.menu.on("hide", this.onMenuHide, this);
31380         }
31381         this.fireEvent('render', this);
31382     },
31383
31384     // private
31385     autoWidth : function(){
31386         if(this.el){
31387             var tbl = this.el.child("table:first");
31388             var tbl2 = this.el.child("table:last");
31389             this.el.setWidth("auto");
31390             tbl.setWidth("auto");
31391             if(Roo.isIE7 && Roo.isStrict){
31392                 var ib = this.el.child('button:first');
31393                 if(ib && ib.getWidth() > 20){
31394                     ib.clip();
31395                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31396                 }
31397             }
31398             if(this.minWidth){
31399                 if(this.hidden){
31400                     this.el.beginMeasure();
31401                 }
31402                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31403                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31404                 }
31405                 if(this.hidden){
31406                     this.el.endMeasure();
31407                 }
31408             }
31409             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31410         } 
31411     },
31412     /**
31413      * Sets this button's click handler
31414      * @param {Function} handler The function to call when the button is clicked
31415      * @param {Object} scope (optional) Scope for the function passed above
31416      */
31417     setHandler : function(handler, scope){
31418         this.handler = handler;
31419         this.scope = scope;  
31420     },
31421     
31422     /**
31423      * Sets this button's arrow click handler
31424      * @param {Function} handler The function to call when the arrow is clicked
31425      * @param {Object} scope (optional) Scope for the function passed above
31426      */
31427     setArrowHandler : function(handler, scope){
31428         this.arrowHandler = handler;
31429         this.scope = scope;  
31430     },
31431     
31432     /**
31433      * Focus the button
31434      */
31435     focus : function(){
31436         if(this.el){
31437             this.el.child("button:first").focus();
31438         }
31439     },
31440
31441     // private
31442     onClick : function(e){
31443         e.preventDefault();
31444         if(!this.disabled){
31445             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31446                 if(this.menu && !this.menu.isVisible()){
31447                     this.menu.show(this.el, this.menuAlign);
31448                 }
31449                 this.fireEvent("arrowclick", this, e);
31450                 if(this.arrowHandler){
31451                     this.arrowHandler.call(this.scope || this, this, e);
31452                 }
31453             }else{
31454                 this.fireEvent("click", this, e);
31455                 if(this.handler){
31456                     this.handler.call(this.scope || this, this, e);
31457                 }
31458             }
31459         }
31460     },
31461     // private
31462     onMouseDown : function(e){
31463         if(!this.disabled){
31464             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31465         }
31466     },
31467     // private
31468     onMouseUp : function(e){
31469         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31470     }   
31471 });
31472
31473
31474 // backwards compat
31475 Roo.MenuButton = Roo.SplitButton;/*
31476  * Based on:
31477  * Ext JS Library 1.1.1
31478  * Copyright(c) 2006-2007, Ext JS, LLC.
31479  *
31480  * Originally Released Under LGPL - original licence link has changed is not relivant.
31481  *
31482  * Fork - LGPL
31483  * <script type="text/javascript">
31484  */
31485
31486 /**
31487  * @class Roo.Toolbar
31488  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31489  * Basic Toolbar class.
31490  * @constructor
31491  * Creates a new Toolbar
31492  * @param {Object} container The config object
31493  */ 
31494 Roo.Toolbar = function(container, buttons, config)
31495 {
31496     /// old consturctor format still supported..
31497     if(container instanceof Array){ // omit the container for later rendering
31498         buttons = container;
31499         config = buttons;
31500         container = null;
31501     }
31502     if (typeof(container) == 'object' && container.xtype) {
31503         config = container;
31504         container = config.container;
31505         buttons = config.buttons || []; // not really - use items!!
31506     }
31507     var xitems = [];
31508     if (config && config.items) {
31509         xitems = config.items;
31510         delete config.items;
31511     }
31512     Roo.apply(this, config);
31513     this.buttons = buttons;
31514     
31515     if(container){
31516         this.render(container);
31517     }
31518     this.xitems = xitems;
31519     Roo.each(xitems, function(b) {
31520         this.add(b);
31521     }, this);
31522     
31523 };
31524
31525 Roo.Toolbar.prototype = {
31526     /**
31527      * @cfg {Array} items
31528      * array of button configs or elements to add (will be converted to a MixedCollection)
31529      */
31530     items: false,
31531     /**
31532      * @cfg {String/HTMLElement/Element} container
31533      * The id or element that will contain the toolbar
31534      */
31535     // private
31536     render : function(ct){
31537         this.el = Roo.get(ct);
31538         if(this.cls){
31539             this.el.addClass(this.cls);
31540         }
31541         // using a table allows for vertical alignment
31542         // 100% width is needed by Safari...
31543         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31544         this.tr = this.el.child("tr", true);
31545         var autoId = 0;
31546         this.items = new Roo.util.MixedCollection(false, function(o){
31547             return o.id || ("item" + (++autoId));
31548         });
31549         if(this.buttons){
31550             this.add.apply(this, this.buttons);
31551             delete this.buttons;
31552         }
31553     },
31554
31555     /**
31556      * Adds element(s) to the toolbar -- this function takes a variable number of 
31557      * arguments of mixed type and adds them to the toolbar.
31558      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31559      * <ul>
31560      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31561      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31562      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31563      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31564      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31565      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31566      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31567      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31568      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31569      * </ul>
31570      * @param {Mixed} arg2
31571      * @param {Mixed} etc.
31572      */
31573     add : function(){
31574         var a = arguments, l = a.length;
31575         for(var i = 0; i < l; i++){
31576             this._add(a[i]);
31577         }
31578     },
31579     // private..
31580     _add : function(el) {
31581         
31582         if (el.xtype) {
31583             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31584         }
31585         
31586         if (el.applyTo){ // some kind of form field
31587             return this.addField(el);
31588         } 
31589         if (el.render){ // some kind of Toolbar.Item
31590             return this.addItem(el);
31591         }
31592         if (typeof el == "string"){ // string
31593             if(el == "separator" || el == "-"){
31594                 return this.addSeparator();
31595             }
31596             if (el == " "){
31597                 return this.addSpacer();
31598             }
31599             if(el == "->"){
31600                 return this.addFill();
31601             }
31602             return this.addText(el);
31603             
31604         }
31605         if(el.tagName){ // element
31606             return this.addElement(el);
31607         }
31608         if(typeof el == "object"){ // must be button config?
31609             return this.addButton(el);
31610         }
31611         // and now what?!?!
31612         return false;
31613         
31614     },
31615     
31616     /**
31617      * Add an Xtype element
31618      * @param {Object} xtype Xtype Object
31619      * @return {Object} created Object
31620      */
31621     addxtype : function(e){
31622         return this.add(e);  
31623     },
31624     
31625     /**
31626      * Returns the Element for this toolbar.
31627      * @return {Roo.Element}
31628      */
31629     getEl : function(){
31630         return this.el;  
31631     },
31632     
31633     /**
31634      * Adds a separator
31635      * @return {Roo.Toolbar.Item} The separator item
31636      */
31637     addSeparator : function(){
31638         return this.addItem(new Roo.Toolbar.Separator());
31639     },
31640
31641     /**
31642      * Adds a spacer element
31643      * @return {Roo.Toolbar.Spacer} The spacer item
31644      */
31645     addSpacer : function(){
31646         return this.addItem(new Roo.Toolbar.Spacer());
31647     },
31648
31649     /**
31650      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31651      * @return {Roo.Toolbar.Fill} The fill item
31652      */
31653     addFill : function(){
31654         return this.addItem(new Roo.Toolbar.Fill());
31655     },
31656
31657     /**
31658      * Adds any standard HTML element to the toolbar
31659      * @param {String/HTMLElement/Element} el The element or id of the element to add
31660      * @return {Roo.Toolbar.Item} The element's item
31661      */
31662     addElement : function(el){
31663         return this.addItem(new Roo.Toolbar.Item(el));
31664     },
31665     /**
31666      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31667      * @type Roo.util.MixedCollection  
31668      */
31669     items : false,
31670      
31671     /**
31672      * Adds any Toolbar.Item or subclass
31673      * @param {Roo.Toolbar.Item} item
31674      * @return {Roo.Toolbar.Item} The item
31675      */
31676     addItem : function(item){
31677         var td = this.nextBlock();
31678         item.render(td);
31679         this.items.add(item);
31680         return item;
31681     },
31682     
31683     /**
31684      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31685      * @param {Object/Array} config A button config or array of configs
31686      * @return {Roo.Toolbar.Button/Array}
31687      */
31688     addButton : function(config){
31689         if(config instanceof Array){
31690             var buttons = [];
31691             for(var i = 0, len = config.length; i < len; i++) {
31692                 buttons.push(this.addButton(config[i]));
31693             }
31694             return buttons;
31695         }
31696         var b = config;
31697         if(!(config instanceof Roo.Toolbar.Button)){
31698             b = config.split ?
31699                 new Roo.Toolbar.SplitButton(config) :
31700                 new Roo.Toolbar.Button(config);
31701         }
31702         var td = this.nextBlock();
31703         b.render(td);
31704         this.items.add(b);
31705         return b;
31706     },
31707     
31708     /**
31709      * Adds text to the toolbar
31710      * @param {String} text The text to add
31711      * @return {Roo.Toolbar.Item} The element's item
31712      */
31713     addText : function(text){
31714         return this.addItem(new Roo.Toolbar.TextItem(text));
31715     },
31716     
31717     /**
31718      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31719      * @param {Number} index The index where the item is to be inserted
31720      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31721      * @return {Roo.Toolbar.Button/Item}
31722      */
31723     insertButton : function(index, item){
31724         if(item instanceof Array){
31725             var buttons = [];
31726             for(var i = 0, len = item.length; i < len; i++) {
31727                buttons.push(this.insertButton(index + i, item[i]));
31728             }
31729             return buttons;
31730         }
31731         if (!(item instanceof Roo.Toolbar.Button)){
31732            item = new Roo.Toolbar.Button(item);
31733         }
31734         var td = document.createElement("td");
31735         this.tr.insertBefore(td, this.tr.childNodes[index]);
31736         item.render(td);
31737         this.items.insert(index, item);
31738         return item;
31739     },
31740     
31741     /**
31742      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31743      * @param {Object} config
31744      * @return {Roo.Toolbar.Item} The element's item
31745      */
31746     addDom : function(config, returnEl){
31747         var td = this.nextBlock();
31748         Roo.DomHelper.overwrite(td, config);
31749         var ti = new Roo.Toolbar.Item(td.firstChild);
31750         ti.render(td);
31751         this.items.add(ti);
31752         return ti;
31753     },
31754
31755     /**
31756      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31757      * @type Roo.util.MixedCollection  
31758      */
31759     fields : false,
31760     
31761     /**
31762      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31763      * Note: the field should not have been rendered yet. For a field that has already been
31764      * rendered, use {@link #addElement}.
31765      * @param {Roo.form.Field} field
31766      * @return {Roo.ToolbarItem}
31767      */
31768      
31769       
31770     addField : function(field) {
31771         if (!this.fields) {
31772             var autoId = 0;
31773             this.fields = new Roo.util.MixedCollection(false, function(o){
31774                 return o.id || ("item" + (++autoId));
31775             });
31776
31777         }
31778         
31779         var td = this.nextBlock();
31780         field.render(td);
31781         var ti = new Roo.Toolbar.Item(td.firstChild);
31782         ti.render(td);
31783         this.items.add(ti);
31784         this.fields.add(field);
31785         return ti;
31786     },
31787     /**
31788      * Hide the toolbar
31789      * @method hide
31790      */
31791      
31792       
31793     hide : function()
31794     {
31795         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31796         this.el.child('div').hide();
31797     },
31798     /**
31799      * Show the toolbar
31800      * @method show
31801      */
31802     show : function()
31803     {
31804         this.el.child('div').show();
31805     },
31806       
31807     // private
31808     nextBlock : function(){
31809         var td = document.createElement("td");
31810         this.tr.appendChild(td);
31811         return td;
31812     },
31813
31814     // private
31815     destroy : function(){
31816         if(this.items){ // rendered?
31817             Roo.destroy.apply(Roo, this.items.items);
31818         }
31819         if(this.fields){ // rendered?
31820             Roo.destroy.apply(Roo, this.fields.items);
31821         }
31822         Roo.Element.uncache(this.el, this.tr);
31823     }
31824 };
31825
31826 /**
31827  * @class Roo.Toolbar.Item
31828  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31829  * @constructor
31830  * Creates a new Item
31831  * @param {HTMLElement} el 
31832  */
31833 Roo.Toolbar.Item = function(el){
31834     var cfg = {};
31835     if (typeof (el.xtype) != 'undefined') {
31836         cfg = el;
31837         el = cfg.el;
31838     }
31839     
31840     this.el = Roo.getDom(el);
31841     this.id = Roo.id(this.el);
31842     this.hidden = false;
31843     
31844     this.addEvents({
31845          /**
31846              * @event render
31847              * Fires when the button is rendered
31848              * @param {Button} this
31849              */
31850         'render': true
31851     });
31852     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31853 };
31854 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31855 //Roo.Toolbar.Item.prototype = {
31856     
31857     /**
31858      * Get this item's HTML Element
31859      * @return {HTMLElement}
31860      */
31861     getEl : function(){
31862        return this.el;  
31863     },
31864
31865     // private
31866     render : function(td){
31867         
31868          this.td = td;
31869         td.appendChild(this.el);
31870         
31871         this.fireEvent('render', this);
31872     },
31873     
31874     /**
31875      * Removes and destroys this item.
31876      */
31877     destroy : function(){
31878         this.td.parentNode.removeChild(this.td);
31879     },
31880     
31881     /**
31882      * Shows this item.
31883      */
31884     show: function(){
31885         this.hidden = false;
31886         this.td.style.display = "";
31887     },
31888     
31889     /**
31890      * Hides this item.
31891      */
31892     hide: function(){
31893         this.hidden = true;
31894         this.td.style.display = "none";
31895     },
31896     
31897     /**
31898      * Convenience function for boolean show/hide.
31899      * @param {Boolean} visible true to show/false to hide
31900      */
31901     setVisible: function(visible){
31902         if(visible) {
31903             this.show();
31904         }else{
31905             this.hide();
31906         }
31907     },
31908     
31909     /**
31910      * Try to focus this item.
31911      */
31912     focus : function(){
31913         Roo.fly(this.el).focus();
31914     },
31915     
31916     /**
31917      * Disables this item.
31918      */
31919     disable : function(){
31920         Roo.fly(this.td).addClass("x-item-disabled");
31921         this.disabled = true;
31922         this.el.disabled = true;
31923     },
31924     
31925     /**
31926      * Enables this item.
31927      */
31928     enable : function(){
31929         Roo.fly(this.td).removeClass("x-item-disabled");
31930         this.disabled = false;
31931         this.el.disabled = false;
31932     }
31933 });
31934
31935
31936 /**
31937  * @class Roo.Toolbar.Separator
31938  * @extends Roo.Toolbar.Item
31939  * A simple toolbar separator class
31940  * @constructor
31941  * Creates a new Separator
31942  */
31943 Roo.Toolbar.Separator = function(cfg){
31944     
31945     var s = document.createElement("span");
31946     s.className = "ytb-sep";
31947     if (cfg) {
31948         cfg.el = s;
31949     }
31950     
31951     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31952 };
31953 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31954     enable:Roo.emptyFn,
31955     disable:Roo.emptyFn,
31956     focus:Roo.emptyFn
31957 });
31958
31959 /**
31960  * @class Roo.Toolbar.Spacer
31961  * @extends Roo.Toolbar.Item
31962  * A simple element that adds extra horizontal space to a toolbar.
31963  * @constructor
31964  * Creates a new Spacer
31965  */
31966 Roo.Toolbar.Spacer = function(cfg){
31967     var s = document.createElement("div");
31968     s.className = "ytb-spacer";
31969     if (cfg) {
31970         cfg.el = s;
31971     }
31972     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31973 };
31974 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31975     enable:Roo.emptyFn,
31976     disable:Roo.emptyFn,
31977     focus:Roo.emptyFn
31978 });
31979
31980 /**
31981  * @class Roo.Toolbar.Fill
31982  * @extends Roo.Toolbar.Spacer
31983  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31984  * @constructor
31985  * Creates a new Spacer
31986  */
31987 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31988     // private
31989     render : function(td){
31990         td.style.width = '100%';
31991         Roo.Toolbar.Fill.superclass.render.call(this, td);
31992     }
31993 });
31994
31995 /**
31996  * @class Roo.Toolbar.TextItem
31997  * @extends Roo.Toolbar.Item
31998  * A simple class that renders text directly into a toolbar.
31999  * @constructor
32000  * Creates a new TextItem
32001  * @cfg {string} text 
32002  */
32003 Roo.Toolbar.TextItem = function(cfg){
32004     var  text = cfg || "";
32005     if (typeof(cfg) == 'object') {
32006         text = cfg.text || "";
32007     }  else {
32008         cfg = null;
32009     }
32010     var s = document.createElement("span");
32011     s.className = "ytb-text";
32012     s.innerHTML = text;
32013     if (cfg) {
32014         cfg.el  = s;
32015     }
32016     
32017     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
32018 };
32019 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
32020     
32021      
32022     enable:Roo.emptyFn,
32023     disable:Roo.emptyFn,
32024     focus:Roo.emptyFn,
32025      /**
32026      * Shows this button
32027      */
32028     show: function(){
32029         this.hidden = false;
32030         this.el.style.display = "";
32031     },
32032     
32033     /**
32034      * Hides this button
32035      */
32036     hide: function(){
32037         this.hidden = true;
32038         this.el.style.display = "none";
32039     }
32040     
32041 });
32042
32043 /**
32044  * @class Roo.Toolbar.Button
32045  * @extends Roo.Button
32046  * A button that renders into a toolbar.
32047  * @constructor
32048  * Creates a new Button
32049  * @param {Object} config A standard {@link Roo.Button} config object
32050  */
32051 Roo.Toolbar.Button = function(config){
32052     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
32053 };
32054 Roo.extend(Roo.Toolbar.Button, Roo.Button,
32055 {
32056     
32057     
32058     render : function(td){
32059         this.td = td;
32060         Roo.Toolbar.Button.superclass.render.call(this, td);
32061     },
32062     
32063     /**
32064      * Removes and destroys this button
32065      */
32066     destroy : function(){
32067         Roo.Toolbar.Button.superclass.destroy.call(this);
32068         this.td.parentNode.removeChild(this.td);
32069     },
32070     
32071     /**
32072      * Shows this button
32073      */
32074     show: function(){
32075         this.hidden = false;
32076         this.td.style.display = "";
32077     },
32078     
32079     /**
32080      * Hides this button
32081      */
32082     hide: function(){
32083         this.hidden = true;
32084         this.td.style.display = "none";
32085     },
32086
32087     /**
32088      * Disables this item
32089      */
32090     disable : function(){
32091         Roo.fly(this.td).addClass("x-item-disabled");
32092         this.disabled = true;
32093     },
32094
32095     /**
32096      * Enables this item
32097      */
32098     enable : function(){
32099         Roo.fly(this.td).removeClass("x-item-disabled");
32100         this.disabled = false;
32101     }
32102 });
32103 // backwards compat
32104 Roo.ToolbarButton = Roo.Toolbar.Button;
32105
32106 /**
32107  * @class Roo.Toolbar.SplitButton
32108  * @extends Roo.SplitButton
32109  * A menu button that renders into a toolbar.
32110  * @constructor
32111  * Creates a new SplitButton
32112  * @param {Object} config A standard {@link Roo.SplitButton} config object
32113  */
32114 Roo.Toolbar.SplitButton = function(config){
32115     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
32116 };
32117 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
32118     render : function(td){
32119         this.td = td;
32120         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
32121     },
32122     
32123     /**
32124      * Removes and destroys this button
32125      */
32126     destroy : function(){
32127         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
32128         this.td.parentNode.removeChild(this.td);
32129     },
32130     
32131     /**
32132      * Shows this button
32133      */
32134     show: function(){
32135         this.hidden = false;
32136         this.td.style.display = "";
32137     },
32138     
32139     /**
32140      * Hides this button
32141      */
32142     hide: function(){
32143         this.hidden = true;
32144         this.td.style.display = "none";
32145     }
32146 });
32147
32148 // backwards compat
32149 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
32150  * Based on:
32151  * Ext JS Library 1.1.1
32152  * Copyright(c) 2006-2007, Ext JS, LLC.
32153  *
32154  * Originally Released Under LGPL - original licence link has changed is not relivant.
32155  *
32156  * Fork - LGPL
32157  * <script type="text/javascript">
32158  */
32159  
32160 /**
32161  * @class Roo.PagingToolbar
32162  * @extends Roo.Toolbar
32163  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
32164  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32165  * @constructor
32166  * Create a new PagingToolbar
32167  * @param {Object} config The config object
32168  */
32169 Roo.PagingToolbar = function(el, ds, config)
32170 {
32171     // old args format still supported... - xtype is prefered..
32172     if (typeof(el) == 'object' && el.xtype) {
32173         // created from xtype...
32174         config = el;
32175         ds = el.dataSource;
32176         el = config.container;
32177     }
32178     var items = [];
32179     if (config.items) {
32180         items = config.items;
32181         config.items = [];
32182     }
32183     
32184     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32185     this.ds = ds;
32186     this.cursor = 0;
32187     this.renderButtons(this.el);
32188     this.bind(ds);
32189     
32190     // supprot items array.
32191    
32192     Roo.each(items, function(e) {
32193         this.add(Roo.factory(e));
32194     },this);
32195     
32196 };
32197
32198 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32199    
32200     /**
32201      * @cfg {String/HTMLElement/Element} container
32202      * container The id or element that will contain the toolbar
32203      */
32204     /**
32205      * @cfg {Boolean} displayInfo
32206      * True to display the displayMsg (defaults to false)
32207      */
32208     
32209     
32210     /**
32211      * @cfg {Number} pageSize
32212      * The number of records to display per page (defaults to 20)
32213      */
32214     pageSize: 20,
32215     /**
32216      * @cfg {String} displayMsg
32217      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32218      */
32219     displayMsg : 'Displaying {0} - {1} of {2}',
32220     /**
32221      * @cfg {String} emptyMsg
32222      * The message to display when no records are found (defaults to "No data to display")
32223      */
32224     emptyMsg : 'No data to display',
32225     /**
32226      * Customizable piece of the default paging text (defaults to "Page")
32227      * @type String
32228      */
32229     beforePageText : "Page",
32230     /**
32231      * Customizable piece of the default paging text (defaults to "of %0")
32232      * @type String
32233      */
32234     afterPageText : "of {0}",
32235     /**
32236      * Customizable piece of the default paging text (defaults to "First Page")
32237      * @type String
32238      */
32239     firstText : "First Page",
32240     /**
32241      * Customizable piece of the default paging text (defaults to "Previous Page")
32242      * @type String
32243      */
32244     prevText : "Previous Page",
32245     /**
32246      * Customizable piece of the default paging text (defaults to "Next Page")
32247      * @type String
32248      */
32249     nextText : "Next Page",
32250     /**
32251      * Customizable piece of the default paging text (defaults to "Last Page")
32252      * @type String
32253      */
32254     lastText : "Last Page",
32255     /**
32256      * Customizable piece of the default paging text (defaults to "Refresh")
32257      * @type String
32258      */
32259     refreshText : "Refresh",
32260
32261     // private
32262     renderButtons : function(el){
32263         Roo.PagingToolbar.superclass.render.call(this, el);
32264         this.first = this.addButton({
32265             tooltip: this.firstText,
32266             cls: "x-btn-icon x-grid-page-first",
32267             disabled: true,
32268             handler: this.onClick.createDelegate(this, ["first"])
32269         });
32270         this.prev = this.addButton({
32271             tooltip: this.prevText,
32272             cls: "x-btn-icon x-grid-page-prev",
32273             disabled: true,
32274             handler: this.onClick.createDelegate(this, ["prev"])
32275         });
32276         //this.addSeparator();
32277         this.add(this.beforePageText);
32278         this.field = Roo.get(this.addDom({
32279            tag: "input",
32280            type: "text",
32281            size: "3",
32282            value: "1",
32283            cls: "x-grid-page-number"
32284         }).el);
32285         this.field.on("keydown", this.onPagingKeydown, this);
32286         this.field.on("focus", function(){this.dom.select();});
32287         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32288         this.field.setHeight(18);
32289         //this.addSeparator();
32290         this.next = this.addButton({
32291             tooltip: this.nextText,
32292             cls: "x-btn-icon x-grid-page-next",
32293             disabled: true,
32294             handler: this.onClick.createDelegate(this, ["next"])
32295         });
32296         this.last = this.addButton({
32297             tooltip: this.lastText,
32298             cls: "x-btn-icon x-grid-page-last",
32299             disabled: true,
32300             handler: this.onClick.createDelegate(this, ["last"])
32301         });
32302         //this.addSeparator();
32303         this.loading = this.addButton({
32304             tooltip: this.refreshText,
32305             cls: "x-btn-icon x-grid-loading",
32306             handler: this.onClick.createDelegate(this, ["refresh"])
32307         });
32308
32309         if(this.displayInfo){
32310             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32311         }
32312     },
32313
32314     // private
32315     updateInfo : function(){
32316         if(this.displayEl){
32317             var count = this.ds.getCount();
32318             var msg = count == 0 ?
32319                 this.emptyMsg :
32320                 String.format(
32321                     this.displayMsg,
32322                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32323                 );
32324             this.displayEl.update(msg);
32325         }
32326     },
32327
32328     // private
32329     onLoad : function(ds, r, o){
32330        this.cursor = o.params ? o.params.start : 0;
32331        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32332
32333        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32334        this.field.dom.value = ap;
32335        this.first.setDisabled(ap == 1);
32336        this.prev.setDisabled(ap == 1);
32337        this.next.setDisabled(ap == ps);
32338        this.last.setDisabled(ap == ps);
32339        this.loading.enable();
32340        this.updateInfo();
32341     },
32342
32343     // private
32344     getPageData : function(){
32345         var total = this.ds.getTotalCount();
32346         return {
32347             total : total,
32348             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32349             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32350         };
32351     },
32352
32353     // private
32354     onLoadError : function(){
32355         this.loading.enable();
32356     },
32357
32358     // private
32359     onPagingKeydown : function(e){
32360         var k = e.getKey();
32361         var d = this.getPageData();
32362         if(k == e.RETURN){
32363             var v = this.field.dom.value, pageNum;
32364             if(!v || isNaN(pageNum = parseInt(v, 10))){
32365                 this.field.dom.value = d.activePage;
32366                 return;
32367             }
32368             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32369             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32370             e.stopEvent();
32371         }
32372         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))
32373         {
32374           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32375           this.field.dom.value = pageNum;
32376           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32377           e.stopEvent();
32378         }
32379         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32380         {
32381           var v = this.field.dom.value, pageNum; 
32382           var increment = (e.shiftKey) ? 10 : 1;
32383           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32384             increment *= -1;
32385           }
32386           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32387             this.field.dom.value = d.activePage;
32388             return;
32389           }
32390           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32391           {
32392             this.field.dom.value = parseInt(v, 10) + increment;
32393             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32394             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32395           }
32396           e.stopEvent();
32397         }
32398     },
32399
32400     // private
32401     beforeLoad : function(){
32402         if(this.loading){
32403             this.loading.disable();
32404         }
32405     },
32406     /**
32407      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
32408      * @param {String} which (first|prev|next|last|refresh)  which button to press.
32409      *
32410      */
32411     // private
32412     onClick : function(which){
32413         var ds = this.ds;
32414         switch(which){
32415             case "first":
32416                 ds.load({params:{start: 0, limit: this.pageSize}});
32417             break;
32418             case "prev":
32419                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32420             break;
32421             case "next":
32422                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32423             break;
32424             case "last":
32425                 var total = ds.getTotalCount();
32426                 var extra = total % this.pageSize;
32427                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32428                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32429             break;
32430             case "refresh":
32431                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32432             break;
32433         }
32434     },
32435
32436     /**
32437      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32438      * @param {Roo.data.Store} store The data store to unbind
32439      */
32440     unbind : function(ds){
32441         ds.un("beforeload", this.beforeLoad, this);
32442         ds.un("load", this.onLoad, this);
32443         ds.un("loadexception", this.onLoadError, this);
32444         ds.un("remove", this.updateInfo, this);
32445         ds.un("add", this.updateInfo, this);
32446         this.ds = undefined;
32447     },
32448
32449     /**
32450      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32451      * @param {Roo.data.Store} store The data store to bind
32452      */
32453     bind : function(ds){
32454         ds.on("beforeload", this.beforeLoad, this);
32455         ds.on("load", this.onLoad, this);
32456         ds.on("loadexception", this.onLoadError, this);
32457         ds.on("remove", this.updateInfo, this);
32458         ds.on("add", this.updateInfo, this);
32459         this.ds = ds;
32460     }
32461 });/*
32462  * Based on:
32463  * Ext JS Library 1.1.1
32464  * Copyright(c) 2006-2007, Ext JS, LLC.
32465  *
32466  * Originally Released Under LGPL - original licence link has changed is not relivant.
32467  *
32468  * Fork - LGPL
32469  * <script type="text/javascript">
32470  */
32471
32472 /**
32473  * @class Roo.Resizable
32474  * @extends Roo.util.Observable
32475  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32476  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32477  * 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
32478  * the element will be wrapped for you automatically.</p>
32479  * <p>Here is the list of valid resize handles:</p>
32480  * <pre>
32481 Value   Description
32482 ------  -------------------
32483  'n'     north
32484  's'     south
32485  'e'     east
32486  'w'     west
32487  'nw'    northwest
32488  'sw'    southwest
32489  'se'    southeast
32490  'ne'    northeast
32491  'hd'    horizontal drag
32492  'all'   all
32493 </pre>
32494  * <p>Here's an example showing the creation of a typical Resizable:</p>
32495  * <pre><code>
32496 var resizer = new Roo.Resizable("element-id", {
32497     handles: 'all',
32498     minWidth: 200,
32499     minHeight: 100,
32500     maxWidth: 500,
32501     maxHeight: 400,
32502     pinned: true
32503 });
32504 resizer.on("resize", myHandler);
32505 </code></pre>
32506  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32507  * resizer.east.setDisplayed(false);</p>
32508  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32509  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32510  * resize operation's new size (defaults to [0, 0])
32511  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32512  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32513  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32514  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32515  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32516  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32517  * @cfg {Number} width The width of the element in pixels (defaults to null)
32518  * @cfg {Number} height The height of the element in pixels (defaults to null)
32519  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32520  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32521  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32522  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32523  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32524  * in favor of the handles config option (defaults to false)
32525  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32526  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32527  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32528  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32529  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32530  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32531  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32532  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32533  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32534  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32535  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32536  * @constructor
32537  * Create a new resizable component
32538  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32539  * @param {Object} config configuration options
32540   */
32541 Roo.Resizable = function(el, config)
32542 {
32543     this.el = Roo.get(el);
32544
32545     if(config && config.wrap){
32546         config.resizeChild = this.el;
32547         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32548         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32549         this.el.setStyle("overflow", "hidden");
32550         this.el.setPositioning(config.resizeChild.getPositioning());
32551         config.resizeChild.clearPositioning();
32552         if(!config.width || !config.height){
32553             var csize = config.resizeChild.getSize();
32554             this.el.setSize(csize.width, csize.height);
32555         }
32556         if(config.pinned && !config.adjustments){
32557             config.adjustments = "auto";
32558         }
32559     }
32560
32561     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32562     this.proxy.unselectable();
32563     this.proxy.enableDisplayMode('block');
32564
32565     Roo.apply(this, config);
32566
32567     if(this.pinned){
32568         this.disableTrackOver = true;
32569         this.el.addClass("x-resizable-pinned");
32570     }
32571     // if the element isn't positioned, make it relative
32572     var position = this.el.getStyle("position");
32573     if(position != "absolute" && position != "fixed"){
32574         this.el.setStyle("position", "relative");
32575     }
32576     if(!this.handles){ // no handles passed, must be legacy style
32577         this.handles = 's,e,se';
32578         if(this.multiDirectional){
32579             this.handles += ',n,w';
32580         }
32581     }
32582     if(this.handles == "all"){
32583         this.handles = "n s e w ne nw se sw";
32584     }
32585     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32586     var ps = Roo.Resizable.positions;
32587     for(var i = 0, len = hs.length; i < len; i++){
32588         if(hs[i] && ps[hs[i]]){
32589             var pos = ps[hs[i]];
32590             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32591         }
32592     }
32593     // legacy
32594     this.corner = this.southeast;
32595     
32596     // updateBox = the box can move..
32597     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32598         this.updateBox = true;
32599     }
32600
32601     this.activeHandle = null;
32602
32603     if(this.resizeChild){
32604         if(typeof this.resizeChild == "boolean"){
32605             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32606         }else{
32607             this.resizeChild = Roo.get(this.resizeChild, true);
32608         }
32609     }
32610     
32611     if(this.adjustments == "auto"){
32612         var rc = this.resizeChild;
32613         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32614         if(rc && (hw || hn)){
32615             rc.position("relative");
32616             rc.setLeft(hw ? hw.el.getWidth() : 0);
32617             rc.setTop(hn ? hn.el.getHeight() : 0);
32618         }
32619         this.adjustments = [
32620             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32621             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32622         ];
32623     }
32624
32625     if(this.draggable){
32626         this.dd = this.dynamic ?
32627             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32628         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32629     }
32630
32631     // public events
32632     this.addEvents({
32633         /**
32634          * @event beforeresize
32635          * Fired before resize is allowed. Set enabled to false to cancel resize.
32636          * @param {Roo.Resizable} this
32637          * @param {Roo.EventObject} e The mousedown event
32638          */
32639         "beforeresize" : true,
32640         /**
32641          * @event resizing
32642          * Fired a resizing.
32643          * @param {Roo.Resizable} this
32644          * @param {Number} x The new x position
32645          * @param {Number} y The new y position
32646          * @param {Number} w The new w width
32647          * @param {Number} h The new h hight
32648          * @param {Roo.EventObject} e The mouseup event
32649          */
32650         "resizing" : true,
32651         /**
32652          * @event resize
32653          * Fired after a resize.
32654          * @param {Roo.Resizable} this
32655          * @param {Number} width The new width
32656          * @param {Number} height The new height
32657          * @param {Roo.EventObject} e The mouseup event
32658          */
32659         "resize" : true
32660     });
32661
32662     if(this.width !== null && this.height !== null){
32663         this.resizeTo(this.width, this.height);
32664     }else{
32665         this.updateChildSize();
32666     }
32667     if(Roo.isIE){
32668         this.el.dom.style.zoom = 1;
32669     }
32670     Roo.Resizable.superclass.constructor.call(this);
32671 };
32672
32673 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32674         resizeChild : false,
32675         adjustments : [0, 0],
32676         minWidth : 5,
32677         minHeight : 5,
32678         maxWidth : 10000,
32679         maxHeight : 10000,
32680         enabled : true,
32681         animate : false,
32682         duration : .35,
32683         dynamic : false,
32684         handles : false,
32685         multiDirectional : false,
32686         disableTrackOver : false,
32687         easing : 'easeOutStrong',
32688         widthIncrement : 0,
32689         heightIncrement : 0,
32690         pinned : false,
32691         width : null,
32692         height : null,
32693         preserveRatio : false,
32694         transparent: false,
32695         minX: 0,
32696         minY: 0,
32697         draggable: false,
32698
32699         /**
32700          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32701          */
32702         constrainTo: undefined,
32703         /**
32704          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32705          */
32706         resizeRegion: undefined,
32707
32708
32709     /**
32710      * Perform a manual resize
32711      * @param {Number} width
32712      * @param {Number} height
32713      */
32714     resizeTo : function(width, height){
32715         this.el.setSize(width, height);
32716         this.updateChildSize();
32717         this.fireEvent("resize", this, width, height, null);
32718     },
32719
32720     // private
32721     startSizing : function(e, handle){
32722         this.fireEvent("beforeresize", this, e);
32723         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32724
32725             if(!this.overlay){
32726                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32727                 this.overlay.unselectable();
32728                 this.overlay.enableDisplayMode("block");
32729                 this.overlay.on("mousemove", this.onMouseMove, this);
32730                 this.overlay.on("mouseup", this.onMouseUp, this);
32731             }
32732             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32733
32734             this.resizing = true;
32735             this.startBox = this.el.getBox();
32736             this.startPoint = e.getXY();
32737             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32738                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32739
32740             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32741             this.overlay.show();
32742
32743             if(this.constrainTo) {
32744                 var ct = Roo.get(this.constrainTo);
32745                 this.resizeRegion = ct.getRegion().adjust(
32746                     ct.getFrameWidth('t'),
32747                     ct.getFrameWidth('l'),
32748                     -ct.getFrameWidth('b'),
32749                     -ct.getFrameWidth('r')
32750                 );
32751             }
32752
32753             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32754             this.proxy.show();
32755             this.proxy.setBox(this.startBox);
32756             if(!this.dynamic){
32757                 this.proxy.setStyle('visibility', 'visible');
32758             }
32759         }
32760     },
32761
32762     // private
32763     onMouseDown : function(handle, e){
32764         if(this.enabled){
32765             e.stopEvent();
32766             this.activeHandle = handle;
32767             this.startSizing(e, handle);
32768         }
32769     },
32770
32771     // private
32772     onMouseUp : function(e){
32773         var size = this.resizeElement();
32774         this.resizing = false;
32775         this.handleOut();
32776         this.overlay.hide();
32777         this.proxy.hide();
32778         this.fireEvent("resize", this, size.width, size.height, e);
32779     },
32780
32781     // private
32782     updateChildSize : function(){
32783         
32784         if(this.resizeChild){
32785             var el = this.el;
32786             var child = this.resizeChild;
32787             var adj = this.adjustments;
32788             if(el.dom.offsetWidth){
32789                 var b = el.getSize(true);
32790                 child.setSize(b.width+adj[0], b.height+adj[1]);
32791             }
32792             // Second call here for IE
32793             // The first call enables instant resizing and
32794             // the second call corrects scroll bars if they
32795             // exist
32796             if(Roo.isIE){
32797                 setTimeout(function(){
32798                     if(el.dom.offsetWidth){
32799                         var b = el.getSize(true);
32800                         child.setSize(b.width+adj[0], b.height+adj[1]);
32801                     }
32802                 }, 10);
32803             }
32804         }
32805     },
32806
32807     // private
32808     snap : function(value, inc, min){
32809         if(!inc || !value) {
32810             return value;
32811         }
32812         var newValue = value;
32813         var m = value % inc;
32814         if(m > 0){
32815             if(m > (inc/2)){
32816                 newValue = value + (inc-m);
32817             }else{
32818                 newValue = value - m;
32819             }
32820         }
32821         return Math.max(min, newValue);
32822     },
32823
32824     // private
32825     resizeElement : function(){
32826         var box = this.proxy.getBox();
32827         if(this.updateBox){
32828             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32829         }else{
32830             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32831         }
32832         this.updateChildSize();
32833         if(!this.dynamic){
32834             this.proxy.hide();
32835         }
32836         return box;
32837     },
32838
32839     // private
32840     constrain : function(v, diff, m, mx){
32841         if(v - diff < m){
32842             diff = v - m;
32843         }else if(v - diff > mx){
32844             diff = mx - v;
32845         }
32846         return diff;
32847     },
32848
32849     // private
32850     onMouseMove : function(e){
32851         
32852         if(this.enabled){
32853             try{// try catch so if something goes wrong the user doesn't get hung
32854
32855             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32856                 return;
32857             }
32858
32859             //var curXY = this.startPoint;
32860             var curSize = this.curSize || this.startBox;
32861             var x = this.startBox.x, y = this.startBox.y;
32862             var ox = x, oy = y;
32863             var w = curSize.width, h = curSize.height;
32864             var ow = w, oh = h;
32865             var mw = this.minWidth, mh = this.minHeight;
32866             var mxw = this.maxWidth, mxh = this.maxHeight;
32867             var wi = this.widthIncrement;
32868             var hi = this.heightIncrement;
32869
32870             var eventXY = e.getXY();
32871             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32872             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32873
32874             var pos = this.activeHandle.position;
32875
32876             switch(pos){
32877                 case "east":
32878                     w += diffX;
32879                     w = Math.min(Math.max(mw, w), mxw);
32880                     break;
32881              
32882                 case "south":
32883                     h += diffY;
32884                     h = Math.min(Math.max(mh, h), mxh);
32885                     break;
32886                 case "southeast":
32887                     w += diffX;
32888                     h += diffY;
32889                     w = Math.min(Math.max(mw, w), mxw);
32890                     h = Math.min(Math.max(mh, h), mxh);
32891                     break;
32892                 case "north":
32893                     diffY = this.constrain(h, diffY, mh, mxh);
32894                     y += diffY;
32895                     h -= diffY;
32896                     break;
32897                 case "hdrag":
32898                     
32899                     if (wi) {
32900                         var adiffX = Math.abs(diffX);
32901                         var sub = (adiffX % wi); // how much 
32902                         if (sub > (wi/2)) { // far enough to snap
32903                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32904                         } else {
32905                             // remove difference.. 
32906                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32907                         }
32908                     }
32909                     x += diffX;
32910                     x = Math.max(this.minX, x);
32911                     break;
32912                 case "west":
32913                     diffX = this.constrain(w, diffX, mw, mxw);
32914                     x += diffX;
32915                     w -= diffX;
32916                     break;
32917                 case "northeast":
32918                     w += diffX;
32919                     w = Math.min(Math.max(mw, w), mxw);
32920                     diffY = this.constrain(h, diffY, mh, mxh);
32921                     y += diffY;
32922                     h -= diffY;
32923                     break;
32924                 case "northwest":
32925                     diffX = this.constrain(w, diffX, mw, mxw);
32926                     diffY = this.constrain(h, diffY, mh, mxh);
32927                     y += diffY;
32928                     h -= diffY;
32929                     x += diffX;
32930                     w -= diffX;
32931                     break;
32932                case "southwest":
32933                     diffX = this.constrain(w, diffX, mw, mxw);
32934                     h += diffY;
32935                     h = Math.min(Math.max(mh, h), mxh);
32936                     x += diffX;
32937                     w -= diffX;
32938                     break;
32939             }
32940
32941             var sw = this.snap(w, wi, mw);
32942             var sh = this.snap(h, hi, mh);
32943             if(sw != w || sh != h){
32944                 switch(pos){
32945                     case "northeast":
32946                         y -= sh - h;
32947                     break;
32948                     case "north":
32949                         y -= sh - h;
32950                         break;
32951                     case "southwest":
32952                         x -= sw - w;
32953                     break;
32954                     case "west":
32955                         x -= sw - w;
32956                         break;
32957                     case "northwest":
32958                         x -= sw - w;
32959                         y -= sh - h;
32960                     break;
32961                 }
32962                 w = sw;
32963                 h = sh;
32964             }
32965
32966             if(this.preserveRatio){
32967                 switch(pos){
32968                     case "southeast":
32969                     case "east":
32970                         h = oh * (w/ow);
32971                         h = Math.min(Math.max(mh, h), mxh);
32972                         w = ow * (h/oh);
32973                        break;
32974                     case "south":
32975                         w = ow * (h/oh);
32976                         w = Math.min(Math.max(mw, w), mxw);
32977                         h = oh * (w/ow);
32978                         break;
32979                     case "northeast":
32980                         w = ow * (h/oh);
32981                         w = Math.min(Math.max(mw, w), mxw);
32982                         h = oh * (w/ow);
32983                     break;
32984                     case "north":
32985                         var tw = w;
32986                         w = ow * (h/oh);
32987                         w = Math.min(Math.max(mw, w), mxw);
32988                         h = oh * (w/ow);
32989                         x += (tw - w) / 2;
32990                         break;
32991                     case "southwest":
32992                         h = oh * (w/ow);
32993                         h = Math.min(Math.max(mh, h), mxh);
32994                         var tw = w;
32995                         w = ow * (h/oh);
32996                         x += tw - w;
32997                         break;
32998                     case "west":
32999                         var th = h;
33000                         h = oh * (w/ow);
33001                         h = Math.min(Math.max(mh, h), mxh);
33002                         y += (th - h) / 2;
33003                         var tw = w;
33004                         w = ow * (h/oh);
33005                         x += tw - w;
33006                        break;
33007                     case "northwest":
33008                         var tw = w;
33009                         var th = h;
33010                         h = oh * (w/ow);
33011                         h = Math.min(Math.max(mh, h), mxh);
33012                         w = ow * (h/oh);
33013                         y += th - h;
33014                         x += tw - w;
33015                        break;
33016
33017                 }
33018             }
33019             if (pos == 'hdrag') {
33020                 w = ow;
33021             }
33022             this.proxy.setBounds(x, y, w, h);
33023             if(this.dynamic){
33024                 this.resizeElement();
33025             }
33026             }catch(e){}
33027         }
33028         this.fireEvent("resizing", this, x, y, w, h, e);
33029     },
33030
33031     // private
33032     handleOver : function(){
33033         if(this.enabled){
33034             this.el.addClass("x-resizable-over");
33035         }
33036     },
33037
33038     // private
33039     handleOut : function(){
33040         if(!this.resizing){
33041             this.el.removeClass("x-resizable-over");
33042         }
33043     },
33044
33045     /**
33046      * Returns the element this component is bound to.
33047      * @return {Roo.Element}
33048      */
33049     getEl : function(){
33050         return this.el;
33051     },
33052
33053     /**
33054      * Returns the resizeChild element (or null).
33055      * @return {Roo.Element}
33056      */
33057     getResizeChild : function(){
33058         return this.resizeChild;
33059     },
33060     groupHandler : function()
33061     {
33062         
33063     },
33064     /**
33065      * Destroys this resizable. If the element was wrapped and
33066      * removeEl is not true then the element remains.
33067      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33068      */
33069     destroy : function(removeEl){
33070         this.proxy.remove();
33071         if(this.overlay){
33072             this.overlay.removeAllListeners();
33073             this.overlay.remove();
33074         }
33075         var ps = Roo.Resizable.positions;
33076         for(var k in ps){
33077             if(typeof ps[k] != "function" && this[ps[k]]){
33078                 var h = this[ps[k]];
33079                 h.el.removeAllListeners();
33080                 h.el.remove();
33081             }
33082         }
33083         if(removeEl){
33084             this.el.update("");
33085             this.el.remove();
33086         }
33087     }
33088 });
33089
33090 // private
33091 // hash to map config positions to true positions
33092 Roo.Resizable.positions = {
33093     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
33094     hd: "hdrag"
33095 };
33096
33097 // private
33098 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
33099     if(!this.tpl){
33100         // only initialize the template if resizable is used
33101         var tpl = Roo.DomHelper.createTemplate(
33102             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
33103         );
33104         tpl.compile();
33105         Roo.Resizable.Handle.prototype.tpl = tpl;
33106     }
33107     this.position = pos;
33108     this.rz = rz;
33109     // show north drag fro topdra
33110     var handlepos = pos == 'hdrag' ? 'north' : pos;
33111     
33112     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
33113     if (pos == 'hdrag') {
33114         this.el.setStyle('cursor', 'pointer');
33115     }
33116     this.el.unselectable();
33117     if(transparent){
33118         this.el.setOpacity(0);
33119     }
33120     this.el.on("mousedown", this.onMouseDown, this);
33121     if(!disableTrackOver){
33122         this.el.on("mouseover", this.onMouseOver, this);
33123         this.el.on("mouseout", this.onMouseOut, this);
33124     }
33125 };
33126
33127 // private
33128 Roo.Resizable.Handle.prototype = {
33129     afterResize : function(rz){
33130         Roo.log('after?');
33131         // do nothing
33132     },
33133     // private
33134     onMouseDown : function(e){
33135         this.rz.onMouseDown(this, e);
33136     },
33137     // private
33138     onMouseOver : function(e){
33139         this.rz.handleOver(this, e);
33140     },
33141     // private
33142     onMouseOut : function(e){
33143         this.rz.handleOut(this, e);
33144     }
33145 };/*
33146  * Based on:
33147  * Ext JS Library 1.1.1
33148  * Copyright(c) 2006-2007, Ext JS, LLC.
33149  *
33150  * Originally Released Under LGPL - original licence link has changed is not relivant.
33151  *
33152  * Fork - LGPL
33153  * <script type="text/javascript">
33154  */
33155
33156 /**
33157  * @class Roo.Editor
33158  * @extends Roo.Component
33159  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
33160  * @constructor
33161  * Create a new Editor
33162  * @param {Roo.form.Field} field The Field object (or descendant)
33163  * @param {Object} config The config object
33164  */
33165 Roo.Editor = function(field, config){
33166     Roo.Editor.superclass.constructor.call(this, config);
33167     this.field = field;
33168     this.addEvents({
33169         /**
33170              * @event beforestartedit
33171              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33172              * false from the handler of this event.
33173              * @param {Editor} this
33174              * @param {Roo.Element} boundEl The underlying element bound to this editor
33175              * @param {Mixed} value The field value being set
33176              */
33177         "beforestartedit" : true,
33178         /**
33179              * @event startedit
33180              * Fires when this editor is displayed
33181              * @param {Roo.Element} boundEl The underlying element bound to this editor
33182              * @param {Mixed} value The starting field value
33183              */
33184         "startedit" : true,
33185         /**
33186              * @event beforecomplete
33187              * Fires after a change has been made to the field, but before the change is reflected in the underlying
33188              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
33189              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33190              * event will not fire since no edit actually occurred.
33191              * @param {Editor} this
33192              * @param {Mixed} value The current field value
33193              * @param {Mixed} startValue The original field value
33194              */
33195         "beforecomplete" : true,
33196         /**
33197              * @event complete
33198              * Fires after editing is complete and any changed value has been written to the underlying field.
33199              * @param {Editor} this
33200              * @param {Mixed} value The current field value
33201              * @param {Mixed} startValue The original field value
33202              */
33203         "complete" : true,
33204         /**
33205          * @event specialkey
33206          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
33207          * {@link Roo.EventObject#getKey} to determine which key was pressed.
33208          * @param {Roo.form.Field} this
33209          * @param {Roo.EventObject} e The event object
33210          */
33211         "specialkey" : true
33212     });
33213 };
33214
33215 Roo.extend(Roo.Editor, Roo.Component, {
33216     /**
33217      * @cfg {Boolean/String} autosize
33218      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33219      * or "height" to adopt the height only (defaults to false)
33220      */
33221     /**
33222      * @cfg {Boolean} revertInvalid
33223      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33224      * validation fails (defaults to true)
33225      */
33226     /**
33227      * @cfg {Boolean} ignoreNoChange
33228      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33229      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
33230      * will never be ignored.
33231      */
33232     /**
33233      * @cfg {Boolean} hideEl
33234      * False to keep the bound element visible while the editor is displayed (defaults to true)
33235      */
33236     /**
33237      * @cfg {Mixed} value
33238      * The data value of the underlying field (defaults to "")
33239      */
33240     value : "",
33241     /**
33242      * @cfg {String} alignment
33243      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33244      */
33245     alignment: "c-c?",
33246     /**
33247      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33248      * for bottom-right shadow (defaults to "frame")
33249      */
33250     shadow : "frame",
33251     /**
33252      * @cfg {Boolean} constrain True to constrain the editor to the viewport
33253      */
33254     constrain : false,
33255     /**
33256      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33257      */
33258     completeOnEnter : false,
33259     /**
33260      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33261      */
33262     cancelOnEsc : false,
33263     /**
33264      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33265      */
33266     updateEl : false,
33267
33268     // private
33269     onRender : function(ct, position){
33270         this.el = new Roo.Layer({
33271             shadow: this.shadow,
33272             cls: "x-editor",
33273             parentEl : ct,
33274             shim : this.shim,
33275             shadowOffset:4,
33276             id: this.id,
33277             constrain: this.constrain
33278         });
33279         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33280         if(this.field.msgTarget != 'title'){
33281             this.field.msgTarget = 'qtip';
33282         }
33283         this.field.render(this.el);
33284         if(Roo.isGecko){
33285             this.field.el.dom.setAttribute('autocomplete', 'off');
33286         }
33287         this.field.on("specialkey", this.onSpecialKey, this);
33288         if(this.swallowKeys){
33289             this.field.el.swallowEvent(['keydown','keypress']);
33290         }
33291         this.field.show();
33292         this.field.on("blur", this.onBlur, this);
33293         if(this.field.grow){
33294             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
33295         }
33296     },
33297
33298     onSpecialKey : function(field, e)
33299     {
33300         //Roo.log('editor onSpecialKey');
33301         if(this.completeOnEnter && e.getKey() == e.ENTER){
33302             e.stopEvent();
33303             this.completeEdit();
33304             return;
33305         }
33306         // do not fire special key otherwise it might hide close the editor...
33307         if(e.getKey() == e.ENTER){    
33308             return;
33309         }
33310         if(this.cancelOnEsc && e.getKey() == e.ESC){
33311             this.cancelEdit();
33312             return;
33313         } 
33314         this.fireEvent('specialkey', field, e);
33315     
33316     },
33317
33318     /**
33319      * Starts the editing process and shows the editor.
33320      * @param {String/HTMLElement/Element} el The element to edit
33321      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33322       * to the innerHTML of el.
33323      */
33324     startEdit : function(el, value){
33325         if(this.editing){
33326             this.completeEdit();
33327         }
33328         this.boundEl = Roo.get(el);
33329         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33330         if(!this.rendered){
33331             this.render(this.parentEl || document.body);
33332         }
33333         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33334             return;
33335         }
33336         this.startValue = v;
33337         this.field.setValue(v);
33338         if(this.autoSize){
33339             var sz = this.boundEl.getSize();
33340             switch(this.autoSize){
33341                 case "width":
33342                 this.setSize(sz.width,  "");
33343                 break;
33344                 case "height":
33345                 this.setSize("",  sz.height);
33346                 break;
33347                 default:
33348                 this.setSize(sz.width,  sz.height);
33349             }
33350         }
33351         this.el.alignTo(this.boundEl, this.alignment);
33352         this.editing = true;
33353         if(Roo.QuickTips){
33354             Roo.QuickTips.disable();
33355         }
33356         this.show();
33357     },
33358
33359     /**
33360      * Sets the height and width of this editor.
33361      * @param {Number} width The new width
33362      * @param {Number} height The new height
33363      */
33364     setSize : function(w, h){
33365         this.field.setSize(w, h);
33366         if(this.el){
33367             this.el.sync();
33368         }
33369     },
33370
33371     /**
33372      * Realigns the editor to the bound field based on the current alignment config value.
33373      */
33374     realign : function(){
33375         this.el.alignTo(this.boundEl, this.alignment);
33376     },
33377
33378     /**
33379      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33380      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33381      */
33382     completeEdit : function(remainVisible){
33383         if(!this.editing){
33384             return;
33385         }
33386         var v = this.getValue();
33387         if(this.revertInvalid !== false && !this.field.isValid()){
33388             v = this.startValue;
33389             this.cancelEdit(true);
33390         }
33391         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33392             this.editing = false;
33393             this.hide();
33394             return;
33395         }
33396         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33397             this.editing = false;
33398             if(this.updateEl && this.boundEl){
33399                 this.boundEl.update(v);
33400             }
33401             if(remainVisible !== true){
33402                 this.hide();
33403             }
33404             this.fireEvent("complete", this, v, this.startValue);
33405         }
33406     },
33407
33408     // private
33409     onShow : function(){
33410         this.el.show();
33411         if(this.hideEl !== false){
33412             this.boundEl.hide();
33413         }
33414         this.field.show();
33415         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33416             this.fixIEFocus = true;
33417             this.deferredFocus.defer(50, this);
33418         }else{
33419             this.field.focus();
33420         }
33421         this.fireEvent("startedit", this.boundEl, this.startValue);
33422     },
33423
33424     deferredFocus : function(){
33425         if(this.editing){
33426             this.field.focus();
33427         }
33428     },
33429
33430     /**
33431      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33432      * reverted to the original starting value.
33433      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33434      * cancel (defaults to false)
33435      */
33436     cancelEdit : function(remainVisible){
33437         if(this.editing){
33438             this.setValue(this.startValue);
33439             if(remainVisible !== true){
33440                 this.hide();
33441             }
33442         }
33443     },
33444
33445     // private
33446     onBlur : function(){
33447         if(this.allowBlur !== true && this.editing){
33448             this.completeEdit();
33449         }
33450     },
33451
33452     // private
33453     onHide : function(){
33454         if(this.editing){
33455             this.completeEdit();
33456             return;
33457         }
33458         this.field.blur();
33459         if(this.field.collapse){
33460             this.field.collapse();
33461         }
33462         this.el.hide();
33463         if(this.hideEl !== false){
33464             this.boundEl.show();
33465         }
33466         if(Roo.QuickTips){
33467             Roo.QuickTips.enable();
33468         }
33469     },
33470
33471     /**
33472      * Sets the data value of the editor
33473      * @param {Mixed} value Any valid value supported by the underlying field
33474      */
33475     setValue : function(v){
33476         this.field.setValue(v);
33477     },
33478
33479     /**
33480      * Gets the data value of the editor
33481      * @return {Mixed} The data value
33482      */
33483     getValue : function(){
33484         return this.field.getValue();
33485     }
33486 });/*
33487  * Based on:
33488  * Ext JS Library 1.1.1
33489  * Copyright(c) 2006-2007, Ext JS, LLC.
33490  *
33491  * Originally Released Under LGPL - original licence link has changed is not relivant.
33492  *
33493  * Fork - LGPL
33494  * <script type="text/javascript">
33495  */
33496  
33497 /**
33498  * @class Roo.BasicDialog
33499  * @extends Roo.util.Observable
33500  * @parent none builder
33501  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33502  * <pre><code>
33503 var dlg = new Roo.BasicDialog("my-dlg", {
33504     height: 200,
33505     width: 300,
33506     minHeight: 100,
33507     minWidth: 150,
33508     modal: true,
33509     proxyDrag: true,
33510     shadow: true
33511 });
33512 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33513 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33514 dlg.addButton('Cancel', dlg.hide, dlg);
33515 dlg.show();
33516 </code></pre>
33517   <b>A Dialog should always be a direct child of the body element.</b>
33518  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33519  * @cfg {String} title Default text to display in the title bar (defaults to null)
33520  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33521  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33522  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33523  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33524  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33525  * (defaults to null with no animation)
33526  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33527  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33528  * property for valid values (defaults to 'all')
33529  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33530  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33531  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33532  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33533  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33534  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33535  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33536  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33537  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33538  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33539  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33540  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33541  * draggable = true (defaults to false)
33542  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33543  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33544  * shadow (defaults to false)
33545  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33546  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33547  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33548  * @cfg {Array} buttons Array of buttons
33549  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33550  * @constructor
33551  * Create a new BasicDialog.
33552  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33553  * @param {Object} config Configuration options
33554  */
33555 Roo.BasicDialog = function(el, config){
33556     this.el = Roo.get(el);
33557     var dh = Roo.DomHelper;
33558     if(!this.el && config && config.autoCreate){
33559         if(typeof config.autoCreate == "object"){
33560             if(!config.autoCreate.id){
33561                 config.autoCreate.id = el;
33562             }
33563             this.el = dh.append(document.body,
33564                         config.autoCreate, true);
33565         }else{
33566             this.el = dh.append(document.body,
33567                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33568         }
33569     }
33570     el = this.el;
33571     el.setDisplayed(true);
33572     el.hide = this.hideAction;
33573     this.id = el.id;
33574     el.addClass("x-dlg");
33575
33576     Roo.apply(this, config);
33577
33578     this.proxy = el.createProxy("x-dlg-proxy");
33579     this.proxy.hide = this.hideAction;
33580     this.proxy.setOpacity(.5);
33581     this.proxy.hide();
33582
33583     if(config.width){
33584         el.setWidth(config.width);
33585     }
33586     if(config.height){
33587         el.setHeight(config.height);
33588     }
33589     this.size = el.getSize();
33590     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33591         this.xy = [config.x,config.y];
33592     }else{
33593         this.xy = el.getCenterXY(true);
33594     }
33595     /** The header element @type Roo.Element */
33596     this.header = el.child("> .x-dlg-hd");
33597     /** The body element @type Roo.Element */
33598     this.body = el.child("> .x-dlg-bd");
33599     /** The footer element @type Roo.Element */
33600     this.footer = el.child("> .x-dlg-ft");
33601
33602     if(!this.header){
33603         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33604     }
33605     if(!this.body){
33606         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33607     }
33608
33609     this.header.unselectable();
33610     if(this.title){
33611         this.header.update(this.title);
33612     }
33613     // this element allows the dialog to be focused for keyboard event
33614     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33615     this.focusEl.swallowEvent("click", true);
33616
33617     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33618
33619     // wrap the body and footer for special rendering
33620     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33621     if(this.footer){
33622         this.bwrap.dom.appendChild(this.footer.dom);
33623     }
33624
33625     this.bg = this.el.createChild({
33626         tag: "div", cls:"x-dlg-bg",
33627         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33628     });
33629     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33630
33631
33632     if(this.autoScroll !== false && !this.autoTabs){
33633         this.body.setStyle("overflow", "auto");
33634     }
33635
33636     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33637
33638     if(this.closable !== false){
33639         this.el.addClass("x-dlg-closable");
33640         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33641         this.close.on("click", this.closeClick, this);
33642         this.close.addClassOnOver("x-dlg-close-over");
33643     }
33644     if(this.collapsible !== false){
33645         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33646         this.collapseBtn.on("click", this.collapseClick, this);
33647         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33648         this.header.on("dblclick", this.collapseClick, this);
33649     }
33650     if(this.resizable !== false){
33651         this.el.addClass("x-dlg-resizable");
33652         this.resizer = new Roo.Resizable(el, {
33653             minWidth: this.minWidth || 80,
33654             minHeight:this.minHeight || 80,
33655             handles: this.resizeHandles || "all",
33656             pinned: true
33657         });
33658         this.resizer.on("beforeresize", this.beforeResize, this);
33659         this.resizer.on("resize", this.onResize, this);
33660     }
33661     if(this.draggable !== false){
33662         el.addClass("x-dlg-draggable");
33663         if (!this.proxyDrag) {
33664             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33665         }
33666         else {
33667             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33668         }
33669         dd.setHandleElId(this.header.id);
33670         dd.endDrag = this.endMove.createDelegate(this);
33671         dd.startDrag = this.startMove.createDelegate(this);
33672         dd.onDrag = this.onDrag.createDelegate(this);
33673         dd.scroll = false;
33674         this.dd = dd;
33675     }
33676     if(this.modal){
33677         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33678         this.mask.enableDisplayMode("block");
33679         this.mask.hide();
33680         this.el.addClass("x-dlg-modal");
33681     }
33682     if(this.shadow){
33683         this.shadow = new Roo.Shadow({
33684             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33685             offset : this.shadowOffset
33686         });
33687     }else{
33688         this.shadowOffset = 0;
33689     }
33690     if(Roo.useShims && this.shim !== false){
33691         this.shim = this.el.createShim();
33692         this.shim.hide = this.hideAction;
33693         this.shim.hide();
33694     }else{
33695         this.shim = false;
33696     }
33697     if(this.autoTabs){
33698         this.initTabs();
33699     }
33700     if (this.buttons) { 
33701         var bts= this.buttons;
33702         this.buttons = [];
33703         Roo.each(bts, function(b) {
33704             this.addButton(b);
33705         }, this);
33706     }
33707     
33708     
33709     this.addEvents({
33710         /**
33711          * @event keydown
33712          * Fires when a key is pressed
33713          * @param {Roo.BasicDialog} this
33714          * @param {Roo.EventObject} e
33715          */
33716         "keydown" : true,
33717         /**
33718          * @event move
33719          * Fires when this dialog is moved by the user.
33720          * @param {Roo.BasicDialog} this
33721          * @param {Number} x The new page X
33722          * @param {Number} y The new page Y
33723          */
33724         "move" : true,
33725         /**
33726          * @event resize
33727          * Fires when this dialog is resized by the user.
33728          * @param {Roo.BasicDialog} this
33729          * @param {Number} width The new width
33730          * @param {Number} height The new height
33731          */
33732         "resize" : true,
33733         /**
33734          * @event beforehide
33735          * Fires before this dialog is hidden.
33736          * @param {Roo.BasicDialog} this
33737          */
33738         "beforehide" : true,
33739         /**
33740          * @event hide
33741          * Fires when this dialog is hidden.
33742          * @param {Roo.BasicDialog} this
33743          */
33744         "hide" : true,
33745         /**
33746          * @event beforeshow
33747          * Fires before this dialog is shown.
33748          * @param {Roo.BasicDialog} this
33749          */
33750         "beforeshow" : true,
33751         /**
33752          * @event show
33753          * Fires when this dialog is shown.
33754          * @param {Roo.BasicDialog} this
33755          */
33756         "show" : true
33757     });
33758     el.on("keydown", this.onKeyDown, this);
33759     el.on("mousedown", this.toFront, this);
33760     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33761     this.el.hide();
33762     Roo.DialogManager.register(this);
33763     Roo.BasicDialog.superclass.constructor.call(this);
33764 };
33765
33766 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33767     shadowOffset: Roo.isIE ? 6 : 5,
33768     minHeight: 80,
33769     minWidth: 200,
33770     minButtonWidth: 75,
33771     defaultButton: null,
33772     buttonAlign: "right",
33773     tabTag: 'div',
33774     firstShow: true,
33775
33776     /**
33777      * Sets the dialog title text
33778      * @param {String} text The title text to display
33779      * @return {Roo.BasicDialog} this
33780      */
33781     setTitle : function(text){
33782         this.header.update(text);
33783         return this;
33784     },
33785
33786     // private
33787     closeClick : function(){
33788         this.hide();
33789     },
33790
33791     // private
33792     collapseClick : function(){
33793         this[this.collapsed ? "expand" : "collapse"]();
33794     },
33795
33796     /**
33797      * Collapses the dialog to its minimized state (only the title bar is visible).
33798      * Equivalent to the user clicking the collapse dialog button.
33799      */
33800     collapse : function(){
33801         if(!this.collapsed){
33802             this.collapsed = true;
33803             this.el.addClass("x-dlg-collapsed");
33804             this.restoreHeight = this.el.getHeight();
33805             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33806         }
33807     },
33808
33809     /**
33810      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33811      * clicking the expand dialog button.
33812      */
33813     expand : function(){
33814         if(this.collapsed){
33815             this.collapsed = false;
33816             this.el.removeClass("x-dlg-collapsed");
33817             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33818         }
33819     },
33820
33821     /**
33822      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33823      * @return {Roo.TabPanel} The tabs component
33824      */
33825     initTabs : function(){
33826         var tabs = this.getTabs();
33827         while(tabs.getTab(0)){
33828             tabs.removeTab(0);
33829         }
33830         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33831             var dom = el.dom;
33832             tabs.addTab(Roo.id(dom), dom.title);
33833             dom.title = "";
33834         });
33835         tabs.activate(0);
33836         return tabs;
33837     },
33838
33839     // private
33840     beforeResize : function(){
33841         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33842     },
33843
33844     // private
33845     onResize : function(){
33846         this.refreshSize();
33847         this.syncBodyHeight();
33848         this.adjustAssets();
33849         this.focus();
33850         this.fireEvent("resize", this, this.size.width, this.size.height);
33851     },
33852
33853     // private
33854     onKeyDown : function(e){
33855         if(this.isVisible()){
33856             this.fireEvent("keydown", this, e);
33857         }
33858     },
33859
33860     /**
33861      * Resizes the dialog.
33862      * @param {Number} width
33863      * @param {Number} height
33864      * @return {Roo.BasicDialog} this
33865      */
33866     resizeTo : function(width, height){
33867         this.el.setSize(width, height);
33868         this.size = {width: width, height: height};
33869         this.syncBodyHeight();
33870         if(this.fixedcenter){
33871             this.center();
33872         }
33873         if(this.isVisible()){
33874             this.constrainXY();
33875             this.adjustAssets();
33876         }
33877         this.fireEvent("resize", this, width, height);
33878         return this;
33879     },
33880
33881
33882     /**
33883      * Resizes the dialog to fit the specified content size.
33884      * @param {Number} width
33885      * @param {Number} height
33886      * @return {Roo.BasicDialog} this
33887      */
33888     setContentSize : function(w, h){
33889         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33890         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33891         //if(!this.el.isBorderBox()){
33892             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33893             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33894         //}
33895         if(this.tabs){
33896             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33897             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33898         }
33899         this.resizeTo(w, h);
33900         return this;
33901     },
33902
33903     /**
33904      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33905      * executed in response to a particular key being pressed while the dialog is active.
33906      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33907      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33908      * @param {Function} fn The function to call
33909      * @param {Object} scope (optional) The scope of the function
33910      * @return {Roo.BasicDialog} this
33911      */
33912     addKeyListener : function(key, fn, scope){
33913         var keyCode, shift, ctrl, alt;
33914         if(typeof key == "object" && !(key instanceof Array)){
33915             keyCode = key["key"];
33916             shift = key["shift"];
33917             ctrl = key["ctrl"];
33918             alt = key["alt"];
33919         }else{
33920             keyCode = key;
33921         }
33922         var handler = function(dlg, e){
33923             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33924                 var k = e.getKey();
33925                 if(keyCode instanceof Array){
33926                     for(var i = 0, len = keyCode.length; i < len; i++){
33927                         if(keyCode[i] == k){
33928                           fn.call(scope || window, dlg, k, e);
33929                           return;
33930                         }
33931                     }
33932                 }else{
33933                     if(k == keyCode){
33934                         fn.call(scope || window, dlg, k, e);
33935                     }
33936                 }
33937             }
33938         };
33939         this.on("keydown", handler);
33940         return this;
33941     },
33942
33943     /**
33944      * Returns the TabPanel component (creates it if it doesn't exist).
33945      * Note: If you wish to simply check for the existence of tabs without creating them,
33946      * check for a null 'tabs' property.
33947      * @return {Roo.TabPanel} The tabs component
33948      */
33949     getTabs : function(){
33950         if(!this.tabs){
33951             this.el.addClass("x-dlg-auto-tabs");
33952             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33953             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33954         }
33955         return this.tabs;
33956     },
33957
33958     /**
33959      * Adds a button to the footer section of the dialog.
33960      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33961      * object or a valid Roo.DomHelper element config
33962      * @param {Function} handler The function called when the button is clicked
33963      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33964      * @return {Roo.Button} The new button
33965      */
33966     addButton : function(config, handler, scope){
33967         var dh = Roo.DomHelper;
33968         if(!this.footer){
33969             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33970         }
33971         if(!this.btnContainer){
33972             var tb = this.footer.createChild({
33973
33974                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33975                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33976             }, null, true);
33977             this.btnContainer = tb.firstChild.firstChild.firstChild;
33978         }
33979         var bconfig = {
33980             handler: handler,
33981             scope: scope,
33982             minWidth: this.minButtonWidth,
33983             hideParent:true
33984         };
33985         if(typeof config == "string"){
33986             bconfig.text = config;
33987         }else{
33988             if(config.tag){
33989                 bconfig.dhconfig = config;
33990             }else{
33991                 Roo.apply(bconfig, config);
33992             }
33993         }
33994         var fc = false;
33995         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33996             bconfig.position = Math.max(0, bconfig.position);
33997             fc = this.btnContainer.childNodes[bconfig.position];
33998         }
33999          
34000         var btn = new Roo.Button(
34001             fc ? 
34002                 this.btnContainer.insertBefore(document.createElement("td"),fc)
34003                 : this.btnContainer.appendChild(document.createElement("td")),
34004             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
34005             bconfig
34006         );
34007         this.syncBodyHeight();
34008         if(!this.buttons){
34009             /**
34010              * Array of all the buttons that have been added to this dialog via addButton
34011              * @type Array
34012              */
34013             this.buttons = [];
34014         }
34015         this.buttons.push(btn);
34016         return btn;
34017     },
34018
34019     /**
34020      * Sets the default button to be focused when the dialog is displayed.
34021      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
34022      * @return {Roo.BasicDialog} this
34023      */
34024     setDefaultButton : function(btn){
34025         this.defaultButton = btn;
34026         return this;
34027     },
34028
34029     // private
34030     getHeaderFooterHeight : function(safe){
34031         var height = 0;
34032         if(this.header){
34033            height += this.header.getHeight();
34034         }
34035         if(this.footer){
34036            var fm = this.footer.getMargins();
34037             height += (this.footer.getHeight()+fm.top+fm.bottom);
34038         }
34039         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
34040         height += this.centerBg.getPadding("tb");
34041         return height;
34042     },
34043
34044     // private
34045     syncBodyHeight : function()
34046     {
34047         var bd = this.body, // the text
34048             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
34049             bw = this.bwrap;
34050         var height = this.size.height - this.getHeaderFooterHeight(false);
34051         bd.setHeight(height-bd.getMargins("tb"));
34052         var hh = this.header.getHeight();
34053         var h = this.size.height-hh;
34054         cb.setHeight(h);
34055         
34056         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
34057         bw.setHeight(h-cb.getPadding("tb"));
34058         
34059         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
34060         bd.setWidth(bw.getWidth(true));
34061         if(this.tabs){
34062             this.tabs.syncHeight();
34063             if(Roo.isIE){
34064                 this.tabs.el.repaint();
34065             }
34066         }
34067     },
34068
34069     /**
34070      * Restores the previous state of the dialog if Roo.state is configured.
34071      * @return {Roo.BasicDialog} this
34072      */
34073     restoreState : function(){
34074         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
34075         if(box && box.width){
34076             this.xy = [box.x, box.y];
34077             this.resizeTo(box.width, box.height);
34078         }
34079         return this;
34080     },
34081
34082     // private
34083     beforeShow : function(){
34084         this.expand();
34085         if(this.fixedcenter){
34086             this.xy = this.el.getCenterXY(true);
34087         }
34088         if(this.modal){
34089             Roo.get(document.body).addClass("x-body-masked");
34090             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34091             this.mask.show();
34092         }
34093         this.constrainXY();
34094     },
34095
34096     // private
34097     animShow : function(){
34098         var b = Roo.get(this.animateTarget).getBox();
34099         this.proxy.setSize(b.width, b.height);
34100         this.proxy.setLocation(b.x, b.y);
34101         this.proxy.show();
34102         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
34103                     true, .35, this.showEl.createDelegate(this));
34104     },
34105
34106     /**
34107      * Shows the dialog.
34108      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
34109      * @return {Roo.BasicDialog} this
34110      */
34111     show : function(animateTarget){
34112         if (this.fireEvent("beforeshow", this) === false){
34113             return;
34114         }
34115         if(this.syncHeightBeforeShow){
34116             this.syncBodyHeight();
34117         }else if(this.firstShow){
34118             this.firstShow = false;
34119             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
34120         }
34121         this.animateTarget = animateTarget || this.animateTarget;
34122         if(!this.el.isVisible()){
34123             this.beforeShow();
34124             if(this.animateTarget && Roo.get(this.animateTarget)){
34125                 this.animShow();
34126             }else{
34127                 this.showEl();
34128             }
34129         }
34130         return this;
34131     },
34132
34133     // private
34134     showEl : function(){
34135         this.proxy.hide();
34136         this.el.setXY(this.xy);
34137         this.el.show();
34138         this.adjustAssets(true);
34139         this.toFront();
34140         this.focus();
34141         // IE peekaboo bug - fix found by Dave Fenwick
34142         if(Roo.isIE){
34143             this.el.repaint();
34144         }
34145         this.fireEvent("show", this);
34146     },
34147
34148     /**
34149      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
34150      * dialog itself will receive focus.
34151      */
34152     focus : function(){
34153         if(this.defaultButton){
34154             this.defaultButton.focus();
34155         }else{
34156             this.focusEl.focus();
34157         }
34158     },
34159
34160     // private
34161     constrainXY : function(){
34162         if(this.constraintoviewport !== false){
34163             if(!this.viewSize){
34164                 if(this.container){
34165                     var s = this.container.getSize();
34166                     this.viewSize = [s.width, s.height];
34167                 }else{
34168                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
34169                 }
34170             }
34171             var s = Roo.get(this.container||document).getScroll();
34172
34173             var x = this.xy[0], y = this.xy[1];
34174             var w = this.size.width, h = this.size.height;
34175             var vw = this.viewSize[0], vh = this.viewSize[1];
34176             // only move it if it needs it
34177             var moved = false;
34178             // first validate right/bottom
34179             if(x + w > vw+s.left){
34180                 x = vw - w;
34181                 moved = true;
34182             }
34183             if(y + h > vh+s.top){
34184                 y = vh - h;
34185                 moved = true;
34186             }
34187             // then make sure top/left isn't negative
34188             if(x < s.left){
34189                 x = s.left;
34190                 moved = true;
34191             }
34192             if(y < s.top){
34193                 y = s.top;
34194                 moved = true;
34195             }
34196             if(moved){
34197                 // cache xy
34198                 this.xy = [x, y];
34199                 if(this.isVisible()){
34200                     this.el.setLocation(x, y);
34201                     this.adjustAssets();
34202                 }
34203             }
34204         }
34205     },
34206
34207     // private
34208     onDrag : function(){
34209         if(!this.proxyDrag){
34210             this.xy = this.el.getXY();
34211             this.adjustAssets();
34212         }
34213     },
34214
34215     // private
34216     adjustAssets : function(doShow){
34217         var x = this.xy[0], y = this.xy[1];
34218         var w = this.size.width, h = this.size.height;
34219         if(doShow === true){
34220             if(this.shadow){
34221                 this.shadow.show(this.el);
34222             }
34223             if(this.shim){
34224                 this.shim.show();
34225             }
34226         }
34227         if(this.shadow && this.shadow.isVisible()){
34228             this.shadow.show(this.el);
34229         }
34230         if(this.shim && this.shim.isVisible()){
34231             this.shim.setBounds(x, y, w, h);
34232         }
34233     },
34234
34235     // private
34236     adjustViewport : function(w, h){
34237         if(!w || !h){
34238             w = Roo.lib.Dom.getViewWidth();
34239             h = Roo.lib.Dom.getViewHeight();
34240         }
34241         // cache the size
34242         this.viewSize = [w, h];
34243         if(this.modal && this.mask.isVisible()){
34244             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34245             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34246         }
34247         if(this.isVisible()){
34248             this.constrainXY();
34249         }
34250     },
34251
34252     /**
34253      * Destroys this dialog and all its supporting elements (including any tabs, shim,
34254      * shadow, proxy, mask, etc.)  Also removes all event listeners.
34255      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34256      */
34257     destroy : function(removeEl){
34258         if(this.isVisible()){
34259             this.animateTarget = null;
34260             this.hide();
34261         }
34262         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34263         if(this.tabs){
34264             this.tabs.destroy(removeEl);
34265         }
34266         Roo.destroy(
34267              this.shim,
34268              this.proxy,
34269              this.resizer,
34270              this.close,
34271              this.mask
34272         );
34273         if(this.dd){
34274             this.dd.unreg();
34275         }
34276         if(this.buttons){
34277            for(var i = 0, len = this.buttons.length; i < len; i++){
34278                this.buttons[i].destroy();
34279            }
34280         }
34281         this.el.removeAllListeners();
34282         if(removeEl === true){
34283             this.el.update("");
34284             this.el.remove();
34285         }
34286         Roo.DialogManager.unregister(this);
34287     },
34288
34289     // private
34290     startMove : function(){
34291         if(this.proxyDrag){
34292             this.proxy.show();
34293         }
34294         if(this.constraintoviewport !== false){
34295             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34296         }
34297     },
34298
34299     // private
34300     endMove : function(){
34301         if(!this.proxyDrag){
34302             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34303         }else{
34304             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34305             this.proxy.hide();
34306         }
34307         this.refreshSize();
34308         this.adjustAssets();
34309         this.focus();
34310         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34311     },
34312
34313     /**
34314      * Brings this dialog to the front of any other visible dialogs
34315      * @return {Roo.BasicDialog} this
34316      */
34317     toFront : function(){
34318         Roo.DialogManager.bringToFront(this);
34319         return this;
34320     },
34321
34322     /**
34323      * Sends this dialog to the back (under) of any other visible dialogs
34324      * @return {Roo.BasicDialog} this
34325      */
34326     toBack : function(){
34327         Roo.DialogManager.sendToBack(this);
34328         return this;
34329     },
34330
34331     /**
34332      * Centers this dialog in the viewport
34333      * @return {Roo.BasicDialog} this
34334      */
34335     center : function(){
34336         var xy = this.el.getCenterXY(true);
34337         this.moveTo(xy[0], xy[1]);
34338         return this;
34339     },
34340
34341     /**
34342      * Moves the dialog's top-left corner to the specified point
34343      * @param {Number} x
34344      * @param {Number} y
34345      * @return {Roo.BasicDialog} this
34346      */
34347     moveTo : function(x, y){
34348         this.xy = [x,y];
34349         if(this.isVisible()){
34350             this.el.setXY(this.xy);
34351             this.adjustAssets();
34352         }
34353         return this;
34354     },
34355
34356     /**
34357      * Aligns the dialog to the specified element
34358      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34359      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34360      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34361      * @return {Roo.BasicDialog} this
34362      */
34363     alignTo : function(element, position, offsets){
34364         this.xy = this.el.getAlignToXY(element, position, offsets);
34365         if(this.isVisible()){
34366             this.el.setXY(this.xy);
34367             this.adjustAssets();
34368         }
34369         return this;
34370     },
34371
34372     /**
34373      * Anchors an element to another element and realigns it when the window is resized.
34374      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34375      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34376      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34377      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34378      * is a number, it is used as the buffer delay (defaults to 50ms).
34379      * @return {Roo.BasicDialog} this
34380      */
34381     anchorTo : function(el, alignment, offsets, monitorScroll){
34382         var action = function(){
34383             this.alignTo(el, alignment, offsets);
34384         };
34385         Roo.EventManager.onWindowResize(action, this);
34386         var tm = typeof monitorScroll;
34387         if(tm != 'undefined'){
34388             Roo.EventManager.on(window, 'scroll', action, this,
34389                 {buffer: tm == 'number' ? monitorScroll : 50});
34390         }
34391         action.call(this);
34392         return this;
34393     },
34394
34395     /**
34396      * Returns true if the dialog is visible
34397      * @return {Boolean}
34398      */
34399     isVisible : function(){
34400         return this.el.isVisible();
34401     },
34402
34403     // private
34404     animHide : function(callback){
34405         var b = Roo.get(this.animateTarget).getBox();
34406         this.proxy.show();
34407         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34408         this.el.hide();
34409         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34410                     this.hideEl.createDelegate(this, [callback]));
34411     },
34412
34413     /**
34414      * Hides the dialog.
34415      * @param {Function} callback (optional) Function to call when the dialog is hidden
34416      * @return {Roo.BasicDialog} this
34417      */
34418     hide : function(callback){
34419         if (this.fireEvent("beforehide", this) === false){
34420             return;
34421         }
34422         if(this.shadow){
34423             this.shadow.hide();
34424         }
34425         if(this.shim) {
34426           this.shim.hide();
34427         }
34428         // sometimes animateTarget seems to get set.. causing problems...
34429         // this just double checks..
34430         if(this.animateTarget && Roo.get(this.animateTarget)) {
34431            this.animHide(callback);
34432         }else{
34433             this.el.hide();
34434             this.hideEl(callback);
34435         }
34436         return this;
34437     },
34438
34439     // private
34440     hideEl : function(callback){
34441         this.proxy.hide();
34442         if(this.modal){
34443             this.mask.hide();
34444             Roo.get(document.body).removeClass("x-body-masked");
34445         }
34446         this.fireEvent("hide", this);
34447         if(typeof callback == "function"){
34448             callback();
34449         }
34450     },
34451
34452     // private
34453     hideAction : function(){
34454         this.setLeft("-10000px");
34455         this.setTop("-10000px");
34456         this.setStyle("visibility", "hidden");
34457     },
34458
34459     // private
34460     refreshSize : function(){
34461         this.size = this.el.getSize();
34462         this.xy = this.el.getXY();
34463         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34464     },
34465
34466     // private
34467     // z-index is managed by the DialogManager and may be overwritten at any time
34468     setZIndex : function(index){
34469         if(this.modal){
34470             this.mask.setStyle("z-index", index);
34471         }
34472         if(this.shim){
34473             this.shim.setStyle("z-index", ++index);
34474         }
34475         if(this.shadow){
34476             this.shadow.setZIndex(++index);
34477         }
34478         this.el.setStyle("z-index", ++index);
34479         if(this.proxy){
34480             this.proxy.setStyle("z-index", ++index);
34481         }
34482         if(this.resizer){
34483             this.resizer.proxy.setStyle("z-index", ++index);
34484         }
34485
34486         this.lastZIndex = index;
34487     },
34488
34489     /**
34490      * Returns the element for this dialog
34491      * @return {Roo.Element} The underlying dialog Element
34492      */
34493     getEl : function(){
34494         return this.el;
34495     }
34496 });
34497
34498 /**
34499  * @class Roo.DialogManager
34500  * Provides global access to BasicDialogs that have been created and
34501  * support for z-indexing (layering) multiple open dialogs.
34502  */
34503 Roo.DialogManager = function(){
34504     var list = {};
34505     var accessList = [];
34506     var front = null;
34507
34508     // private
34509     var sortDialogs = function(d1, d2){
34510         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34511     };
34512
34513     // private
34514     var orderDialogs = function(){
34515         accessList.sort(sortDialogs);
34516         var seed = Roo.DialogManager.zseed;
34517         for(var i = 0, len = accessList.length; i < len; i++){
34518             var dlg = accessList[i];
34519             if(dlg){
34520                 dlg.setZIndex(seed + (i*10));
34521             }
34522         }
34523     };
34524
34525     return {
34526         /**
34527          * The starting z-index for BasicDialogs (defaults to 9000)
34528          * @type Number The z-index value
34529          */
34530         zseed : 9000,
34531
34532         // private
34533         register : function(dlg){
34534             list[dlg.id] = dlg;
34535             accessList.push(dlg);
34536         },
34537
34538         // private
34539         unregister : function(dlg){
34540             delete list[dlg.id];
34541             var i=0;
34542             var len=0;
34543             if(!accessList.indexOf){
34544                 for(  i = 0, len = accessList.length; i < len; i++){
34545                     if(accessList[i] == dlg){
34546                         accessList.splice(i, 1);
34547                         return;
34548                     }
34549                 }
34550             }else{
34551                  i = accessList.indexOf(dlg);
34552                 if(i != -1){
34553                     accessList.splice(i, 1);
34554                 }
34555             }
34556         },
34557
34558         /**
34559          * Gets a registered dialog by id
34560          * @param {String/Object} id The id of the dialog or a dialog
34561          * @return {Roo.BasicDialog} this
34562          */
34563         get : function(id){
34564             return typeof id == "object" ? id : list[id];
34565         },
34566
34567         /**
34568          * Brings the specified dialog to the front
34569          * @param {String/Object} dlg The id of the dialog or a dialog
34570          * @return {Roo.BasicDialog} this
34571          */
34572         bringToFront : function(dlg){
34573             dlg = this.get(dlg);
34574             if(dlg != front){
34575                 front = dlg;
34576                 dlg._lastAccess = new Date().getTime();
34577                 orderDialogs();
34578             }
34579             return dlg;
34580         },
34581
34582         /**
34583          * Sends the specified dialog to the back
34584          * @param {String/Object} dlg The id of the dialog or a dialog
34585          * @return {Roo.BasicDialog} this
34586          */
34587         sendToBack : function(dlg){
34588             dlg = this.get(dlg);
34589             dlg._lastAccess = -(new Date().getTime());
34590             orderDialogs();
34591             return dlg;
34592         },
34593
34594         /**
34595          * Hides all dialogs
34596          */
34597         hideAll : function(){
34598             for(var id in list){
34599                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34600                     list[id].hide();
34601                 }
34602             }
34603         }
34604     };
34605 }();
34606
34607 /**
34608  * @class Roo.LayoutDialog
34609  * @extends Roo.BasicDialog
34610  * @children Roo.ContentPanel
34611  * @parent builder none
34612  * Dialog which provides adjustments for working with a layout in a Dialog.
34613  * Add your necessary layout config options to the dialog's config.<br>
34614  * Example usage (including a nested layout):
34615  * <pre><code>
34616 if(!dialog){
34617     dialog = new Roo.LayoutDialog("download-dlg", {
34618         modal: true,
34619         width:600,
34620         height:450,
34621         shadow:true,
34622         minWidth:500,
34623         minHeight:350,
34624         autoTabs:true,
34625         proxyDrag:true,
34626         // layout config merges with the dialog config
34627         center:{
34628             tabPosition: "top",
34629             alwaysShowTabs: true
34630         }
34631     });
34632     dialog.addKeyListener(27, dialog.hide, dialog);
34633     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34634     dialog.addButton("Build It!", this.getDownload, this);
34635
34636     // we can even add nested layouts
34637     var innerLayout = new Roo.BorderLayout("dl-inner", {
34638         east: {
34639             initialSize: 200,
34640             autoScroll:true,
34641             split:true
34642         },
34643         center: {
34644             autoScroll:true
34645         }
34646     });
34647     innerLayout.beginUpdate();
34648     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34649     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34650     innerLayout.endUpdate(true);
34651
34652     var layout = dialog.getLayout();
34653     layout.beginUpdate();
34654     layout.add("center", new Roo.ContentPanel("standard-panel",
34655                         {title: "Download the Source", fitToFrame:true}));
34656     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34657                {title: "Build your own roo.js"}));
34658     layout.getRegion("center").showPanel(sp);
34659     layout.endUpdate();
34660 }
34661 </code></pre>
34662     * @constructor
34663     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34664     * @param {Object} config configuration options
34665   */
34666 Roo.LayoutDialog = function(el, cfg){
34667     
34668     var config=  cfg;
34669     if (typeof(cfg) == 'undefined') {
34670         config = Roo.apply({}, el);
34671         // not sure why we use documentElement here.. - it should always be body.
34672         // IE7 borks horribly if we use documentElement.
34673         // webkit also does not like documentElement - it creates a body element...
34674         el = Roo.get( document.body || document.documentElement ).createChild();
34675         //config.autoCreate = true;
34676     }
34677     
34678     
34679     config.autoTabs = false;
34680     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34681     this.body.setStyle({overflow:"hidden", position:"relative"});
34682     this.layout = new Roo.BorderLayout(this.body.dom, config);
34683     this.layout.monitorWindowResize = false;
34684     this.el.addClass("x-dlg-auto-layout");
34685     // fix case when center region overwrites center function
34686     this.center = Roo.BasicDialog.prototype.center;
34687     this.on("show", this.layout.layout, this.layout, true);
34688     if (config.items) {
34689         var xitems = config.items;
34690         delete config.items;
34691         Roo.each(xitems, this.addxtype, this);
34692     }
34693     
34694     
34695 };
34696 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34697     
34698     
34699     /**
34700      * @cfg {Roo.LayoutRegion} east  
34701      */
34702     /**
34703      * @cfg {Roo.LayoutRegion} west
34704      */
34705     /**
34706      * @cfg {Roo.LayoutRegion} south
34707      */
34708     /**
34709      * @cfg {Roo.LayoutRegion} north
34710      */
34711     /**
34712      * @cfg {Roo.LayoutRegion} center
34713      */
34714     /**
34715      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34716      */
34717     
34718     
34719     /**
34720      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34721      * @deprecated
34722      */
34723     endUpdate : function(){
34724         this.layout.endUpdate();
34725     },
34726
34727     /**
34728      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34729      *  @deprecated
34730      */
34731     beginUpdate : function(){
34732         this.layout.beginUpdate();
34733     },
34734
34735     /**
34736      * Get the BorderLayout for this dialog
34737      * @return {Roo.BorderLayout}
34738      */
34739     getLayout : function(){
34740         return this.layout;
34741     },
34742
34743     showEl : function(){
34744         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34745         if(Roo.isIE7){
34746             this.layout.layout();
34747         }
34748     },
34749
34750     // private
34751     // Use the syncHeightBeforeShow config option to control this automatically
34752     syncBodyHeight : function(){
34753         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34754         if(this.layout){this.layout.layout();}
34755     },
34756     
34757       /**
34758      * Add an xtype element (actually adds to the layout.)
34759      * @return {Object} xdata xtype object data.
34760      */
34761     
34762     addxtype : function(c) {
34763         return this.layout.addxtype(c);
34764     }
34765 });/*
34766  * Based on:
34767  * Ext JS Library 1.1.1
34768  * Copyright(c) 2006-2007, Ext JS, LLC.
34769  *
34770  * Originally Released Under LGPL - original licence link has changed is not relivant.
34771  *
34772  * Fork - LGPL
34773  * <script type="text/javascript">
34774  */
34775  
34776 /**
34777  * @class Roo.MessageBox
34778  * @static
34779  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34780  * Example usage:
34781  *<pre><code>
34782 // Basic alert:
34783 Roo.Msg.alert('Status', 'Changes saved successfully.');
34784
34785 // Prompt for user data:
34786 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34787     if (btn == 'ok'){
34788         // process text value...
34789     }
34790 });
34791
34792 // Show a dialog using config options:
34793 Roo.Msg.show({
34794    title:'Save Changes?',
34795    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34796    buttons: Roo.Msg.YESNOCANCEL,
34797    fn: processResult,
34798    animEl: 'elId'
34799 });
34800 </code></pre>
34801  * @static
34802  */
34803 Roo.MessageBox = function(){
34804     var dlg, opt, mask, waitTimer;
34805     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34806     var buttons, activeTextEl, bwidth;
34807
34808     // private
34809     var handleButton = function(button){
34810         dlg.hide();
34811         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34812     };
34813
34814     // private
34815     var handleHide = function(){
34816         if(opt && opt.cls){
34817             dlg.el.removeClass(opt.cls);
34818         }
34819         if(waitTimer){
34820             Roo.TaskMgr.stop(waitTimer);
34821             waitTimer = null;
34822         }
34823     };
34824
34825     // private
34826     var updateButtons = function(b){
34827         var width = 0;
34828         if(!b){
34829             buttons["ok"].hide();
34830             buttons["cancel"].hide();
34831             buttons["yes"].hide();
34832             buttons["no"].hide();
34833             dlg.footer.dom.style.display = 'none';
34834             return width;
34835         }
34836         dlg.footer.dom.style.display = '';
34837         for(var k in buttons){
34838             if(typeof buttons[k] != "function"){
34839                 if(b[k]){
34840                     buttons[k].show();
34841                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34842                     width += buttons[k].el.getWidth()+15;
34843                 }else{
34844                     buttons[k].hide();
34845                 }
34846             }
34847         }
34848         return width;
34849     };
34850
34851     // private
34852     var handleEsc = function(d, k, e){
34853         if(opt && opt.closable !== false){
34854             dlg.hide();
34855         }
34856         if(e){
34857             e.stopEvent();
34858         }
34859     };
34860
34861     return {
34862         /**
34863          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34864          * @return {Roo.BasicDialog} The BasicDialog element
34865          */
34866         getDialog : function(){
34867            if(!dlg){
34868                 dlg = new Roo.BasicDialog("x-msg-box", {
34869                     autoCreate : true,
34870                     shadow: true,
34871                     draggable: true,
34872                     resizable:false,
34873                     constraintoviewport:false,
34874                     fixedcenter:true,
34875                     collapsible : false,
34876                     shim:true,
34877                     modal: true,
34878                     width:400, height:100,
34879                     buttonAlign:"center",
34880                     closeClick : function(){
34881                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34882                             handleButton("no");
34883                         }else{
34884                             handleButton("cancel");
34885                         }
34886                     }
34887                 });
34888               
34889                 dlg.on("hide", handleHide);
34890                 mask = dlg.mask;
34891                 dlg.addKeyListener(27, handleEsc);
34892                 buttons = {};
34893                 var bt = this.buttonText;
34894                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34895                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34896                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34897                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34898                 bodyEl = dlg.body.createChild({
34899
34900                     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>'
34901                 });
34902                 msgEl = bodyEl.dom.firstChild;
34903                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34904                 textboxEl.enableDisplayMode();
34905                 textboxEl.addKeyListener([10,13], function(){
34906                     if(dlg.isVisible() && opt && opt.buttons){
34907                         if(opt.buttons.ok){
34908                             handleButton("ok");
34909                         }else if(opt.buttons.yes){
34910                             handleButton("yes");
34911                         }
34912                     }
34913                 });
34914                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34915                 textareaEl.enableDisplayMode();
34916                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34917                 progressEl.enableDisplayMode();
34918                 var pf = progressEl.dom.firstChild;
34919                 if (pf) {
34920                     pp = Roo.get(pf.firstChild);
34921                     pp.setHeight(pf.offsetHeight);
34922                 }
34923                 
34924             }
34925             return dlg;
34926         },
34927
34928         /**
34929          * Updates the message box body text
34930          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34931          * the XHTML-compliant non-breaking space character '&amp;#160;')
34932          * @return {Roo.MessageBox} This message box
34933          */
34934         updateText : function(text){
34935             if(!dlg.isVisible() && !opt.width){
34936                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34937             }
34938             msgEl.innerHTML = text || '&#160;';
34939       
34940             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34941             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34942             var w = Math.max(
34943                     Math.min(opt.width || cw , this.maxWidth), 
34944                     Math.max(opt.minWidth || this.minWidth, bwidth)
34945             );
34946             if(opt.prompt){
34947                 activeTextEl.setWidth(w);
34948             }
34949             if(dlg.isVisible()){
34950                 dlg.fixedcenter = false;
34951             }
34952             // to big, make it scroll. = But as usual stupid IE does not support
34953             // !important..
34954             
34955             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34956                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34957                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34958             } else {
34959                 bodyEl.dom.style.height = '';
34960                 bodyEl.dom.style.overflowY = '';
34961             }
34962             if (cw > w) {
34963                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34964             } else {
34965                 bodyEl.dom.style.overflowX = '';
34966             }
34967             
34968             dlg.setContentSize(w, bodyEl.getHeight());
34969             if(dlg.isVisible()){
34970                 dlg.fixedcenter = true;
34971             }
34972             return this;
34973         },
34974
34975         /**
34976          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34977          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34978          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34979          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34980          * @return {Roo.MessageBox} This message box
34981          */
34982         updateProgress : function(value, text){
34983             if(text){
34984                 this.updateText(text);
34985             }
34986             if (pp) { // weird bug on my firefox - for some reason this is not defined
34987                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34988             }
34989             return this;
34990         },        
34991
34992         /**
34993          * Returns true if the message box is currently displayed
34994          * @return {Boolean} True if the message box is visible, else false
34995          */
34996         isVisible : function(){
34997             return dlg && dlg.isVisible();  
34998         },
34999
35000         /**
35001          * Hides the message box if it is displayed
35002          */
35003         hide : function(){
35004             if(this.isVisible()){
35005                 dlg.hide();
35006             }  
35007         },
35008
35009         /**
35010          * Displays a new message box, or reinitializes an existing message box, based on the config options
35011          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
35012          * The following config object properties are supported:
35013          * <pre>
35014 Property    Type             Description
35015 ----------  ---------------  ------------------------------------------------------------------------------------
35016 animEl            String/Element   An id or Element from which the message box should animate as it opens and
35017                                    closes (defaults to undefined)
35018 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
35019                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
35020 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
35021                                    progress and wait dialogs will ignore this property and always hide the
35022                                    close button as they can only be closed programmatically.
35023 cls               String           A custom CSS class to apply to the message box element
35024 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
35025                                    displayed (defaults to 75)
35026 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
35027                                    function will be btn (the name of the button that was clicked, if applicable,
35028                                    e.g. "ok"), and text (the value of the active text field, if applicable).
35029                                    Progress and wait dialogs will ignore this option since they do not respond to
35030                                    user actions and can only be closed programmatically, so any required function
35031                                    should be called by the same code after it closes the dialog.
35032 icon              String           A CSS class that provides a background image to be used as an icon for
35033                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
35034 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
35035 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
35036 modal             Boolean          False to allow user interaction with the page while the message box is
35037                                    displayed (defaults to true)
35038 msg               String           A string that will replace the existing message box body text (defaults
35039                                    to the XHTML-compliant non-breaking space character '&#160;')
35040 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
35041 progress          Boolean          True to display a progress bar (defaults to false)
35042 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
35043 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
35044 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
35045 title             String           The title text
35046 value             String           The string value to set into the active textbox element if displayed
35047 wait              Boolean          True to display a progress bar (defaults to false)
35048 width             Number           The width of the dialog in pixels
35049 </pre>
35050          *
35051          * Example usage:
35052          * <pre><code>
35053 Roo.Msg.show({
35054    title: 'Address',
35055    msg: 'Please enter your address:',
35056    width: 300,
35057    buttons: Roo.MessageBox.OKCANCEL,
35058    multiline: true,
35059    fn: saveAddress,
35060    animEl: 'addAddressBtn'
35061 });
35062 </code></pre>
35063          * @param {Object} config Configuration options
35064          * @return {Roo.MessageBox} This message box
35065          */
35066         show : function(options)
35067         {
35068             
35069             // this causes nightmares if you show one dialog after another
35070             // especially on callbacks..
35071              
35072             if(this.isVisible()){
35073                 
35074                 this.hide();
35075                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
35076                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
35077                 Roo.log("New Dialog Message:" +  options.msg )
35078                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
35079                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
35080                 
35081             }
35082             var d = this.getDialog();
35083             opt = options;
35084             d.setTitle(opt.title || "&#160;");
35085             d.close.setDisplayed(opt.closable !== false);
35086             activeTextEl = textboxEl;
35087             opt.prompt = opt.prompt || (opt.multiline ? true : false);
35088             if(opt.prompt){
35089                 if(opt.multiline){
35090                     textboxEl.hide();
35091                     textareaEl.show();
35092                     textareaEl.setHeight(typeof opt.multiline == "number" ?
35093                         opt.multiline : this.defaultTextHeight);
35094                     activeTextEl = textareaEl;
35095                 }else{
35096                     textboxEl.show();
35097                     textareaEl.hide();
35098                 }
35099             }else{
35100                 textboxEl.hide();
35101                 textareaEl.hide();
35102             }
35103             progressEl.setDisplayed(opt.progress === true);
35104             this.updateProgress(0);
35105             activeTextEl.dom.value = opt.value || "";
35106             if(opt.prompt){
35107                 dlg.setDefaultButton(activeTextEl);
35108             }else{
35109                 var bs = opt.buttons;
35110                 var db = null;
35111                 if(bs && bs.ok){
35112                     db = buttons["ok"];
35113                 }else if(bs && bs.yes){
35114                     db = buttons["yes"];
35115                 }
35116                 dlg.setDefaultButton(db);
35117             }
35118             bwidth = updateButtons(opt.buttons);
35119             this.updateText(opt.msg);
35120             if(opt.cls){
35121                 d.el.addClass(opt.cls);
35122             }
35123             d.proxyDrag = opt.proxyDrag === true;
35124             d.modal = opt.modal !== false;
35125             d.mask = opt.modal !== false ? mask : false;
35126             if(!d.isVisible()){
35127                 // force it to the end of the z-index stack so it gets a cursor in FF
35128                 document.body.appendChild(dlg.el.dom);
35129                 d.animateTarget = null;
35130                 d.show(options.animEl);
35131             }
35132             dlg.toFront();
35133             return this;
35134         },
35135
35136         /**
35137          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
35138          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
35139          * and closing the message box when the process is complete.
35140          * @param {String} title The title bar text
35141          * @param {String} msg The message box body text
35142          * @return {Roo.MessageBox} This message box
35143          */
35144         progress : function(title, msg){
35145             this.show({
35146                 title : title,
35147                 msg : msg,
35148                 buttons: false,
35149                 progress:true,
35150                 closable:false,
35151                 minWidth: this.minProgressWidth,
35152                 modal : true
35153             });
35154             return this;
35155         },
35156
35157         /**
35158          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
35159          * If a callback function is passed it will be called after the user clicks the button, and the
35160          * id of the button that was clicked will be passed as the only parameter to the callback
35161          * (could also be the top-right close button).
35162          * @param {String} title The title bar text
35163          * @param {String} msg The message box body text
35164          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35165          * @param {Object} scope (optional) The scope of the callback function
35166          * @return {Roo.MessageBox} This message box
35167          */
35168         alert : function(title, msg, fn, scope){
35169             this.show({
35170                 title : title,
35171                 msg : msg,
35172                 buttons: this.OK,
35173                 fn: fn,
35174                 scope : scope,
35175                 modal : true
35176             });
35177             return this;
35178         },
35179
35180         /**
35181          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
35182          * interaction while waiting for a long-running process to complete that does not have defined intervals.
35183          * You are responsible for closing the message box when the process is complete.
35184          * @param {String} msg The message box body text
35185          * @param {String} title (optional) The title bar text
35186          * @return {Roo.MessageBox} This message box
35187          */
35188         wait : function(msg, title){
35189             this.show({
35190                 title : title,
35191                 msg : msg,
35192                 buttons: false,
35193                 closable:false,
35194                 progress:true,
35195                 modal:true,
35196                 width:300,
35197                 wait:true
35198             });
35199             waitTimer = Roo.TaskMgr.start({
35200                 run: function(i){
35201                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35202                 },
35203                 interval: 1000
35204             });
35205             return this;
35206         },
35207
35208         /**
35209          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35210          * If a callback function is passed it will be called after the user clicks either button, and the id of the
35211          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35212          * @param {String} title The title bar text
35213          * @param {String} msg The message box body text
35214          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35215          * @param {Object} scope (optional) The scope of the callback function
35216          * @return {Roo.MessageBox} This message box
35217          */
35218         confirm : function(title, msg, fn, scope){
35219             this.show({
35220                 title : title,
35221                 msg : msg,
35222                 buttons: this.YESNO,
35223                 fn: fn,
35224                 scope : scope,
35225                 modal : true
35226             });
35227             return this;
35228         },
35229
35230         /**
35231          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35232          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
35233          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35234          * (could also be the top-right close button) and the text that was entered will be passed as the two
35235          * parameters to the callback.
35236          * @param {String} title The title bar text
35237          * @param {String} msg The message box body text
35238          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35239          * @param {Object} scope (optional) The scope of the callback function
35240          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35241          * property, or the height in pixels to create the textbox (defaults to false / single-line)
35242          * @return {Roo.MessageBox} This message box
35243          */
35244         prompt : function(title, msg, fn, scope, multiline){
35245             this.show({
35246                 title : title,
35247                 msg : msg,
35248                 buttons: this.OKCANCEL,
35249                 fn: fn,
35250                 minWidth:250,
35251                 scope : scope,
35252                 prompt:true,
35253                 multiline: multiline,
35254                 modal : true
35255             });
35256             return this;
35257         },
35258
35259         /**
35260          * Button config that displays a single OK button
35261          * @type Object
35262          */
35263         OK : {ok:true},
35264         /**
35265          * Button config that displays Yes and No buttons
35266          * @type Object
35267          */
35268         YESNO : {yes:true, no:true},
35269         /**
35270          * Button config that displays OK and Cancel buttons
35271          * @type Object
35272          */
35273         OKCANCEL : {ok:true, cancel:true},
35274         /**
35275          * Button config that displays Yes, No and Cancel buttons
35276          * @type Object
35277          */
35278         YESNOCANCEL : {yes:true, no:true, cancel:true},
35279
35280         /**
35281          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35282          * @type Number
35283          */
35284         defaultTextHeight : 75,
35285         /**
35286          * The maximum width in pixels of the message box (defaults to 600)
35287          * @type Number
35288          */
35289         maxWidth : 600,
35290         /**
35291          * The minimum width in pixels of the message box (defaults to 100)
35292          * @type Number
35293          */
35294         minWidth : 100,
35295         /**
35296          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
35297          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35298          * @type Number
35299          */
35300         minProgressWidth : 250,
35301         /**
35302          * An object containing the default button text strings that can be overriden for localized language support.
35303          * Supported properties are: ok, cancel, yes and no.
35304          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35305          * @type Object
35306          */
35307         buttonText : {
35308             ok : "OK",
35309             cancel : "Cancel",
35310             yes : "Yes",
35311             no : "No"
35312         }
35313     };
35314 }();
35315
35316 /**
35317  * Shorthand for {@link Roo.MessageBox}
35318  */
35319 Roo.Msg = Roo.MessageBox;/*
35320  * Based on:
35321  * Ext JS Library 1.1.1
35322  * Copyright(c) 2006-2007, Ext JS, LLC.
35323  *
35324  * Originally Released Under LGPL - original licence link has changed is not relivant.
35325  *
35326  * Fork - LGPL
35327  * <script type="text/javascript">
35328  */
35329 /**
35330  * @class Roo.QuickTips
35331  * Provides attractive and customizable tooltips for any element.
35332  * @static
35333  */
35334 Roo.QuickTips = function(){
35335     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35336     var ce, bd, xy, dd;
35337     var visible = false, disabled = true, inited = false;
35338     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35339     
35340     var onOver = function(e){
35341         if(disabled){
35342             return;
35343         }
35344         var t = e.getTarget();
35345         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35346             return;
35347         }
35348         if(ce && t == ce.el){
35349             clearTimeout(hideProc);
35350             return;
35351         }
35352         if(t && tagEls[t.id]){
35353             tagEls[t.id].el = t;
35354             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35355             return;
35356         }
35357         var ttp, et = Roo.fly(t);
35358         var ns = cfg.namespace;
35359         if(tm.interceptTitles && t.title){
35360             ttp = t.title;
35361             t.qtip = ttp;
35362             t.removeAttribute("title");
35363             e.preventDefault();
35364         }else{
35365             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35366         }
35367         if(ttp){
35368             showProc = show.defer(tm.showDelay, tm, [{
35369                 el: t, 
35370                 text: ttp.replace(/\\n/g,'<br/>'),
35371                 width: et.getAttributeNS(ns, cfg.width),
35372                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35373                 title: et.getAttributeNS(ns, cfg.title),
35374                     cls: et.getAttributeNS(ns, cfg.cls)
35375             }]);
35376         }
35377     };
35378     
35379     var onOut = function(e){
35380         clearTimeout(showProc);
35381         var t = e.getTarget();
35382         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35383             hideProc = setTimeout(hide, tm.hideDelay);
35384         }
35385     };
35386     
35387     var onMove = function(e){
35388         if(disabled){
35389             return;
35390         }
35391         xy = e.getXY();
35392         xy[1] += 18;
35393         if(tm.trackMouse && ce){
35394             el.setXY(xy);
35395         }
35396     };
35397     
35398     var onDown = function(e){
35399         clearTimeout(showProc);
35400         clearTimeout(hideProc);
35401         if(!e.within(el)){
35402             if(tm.hideOnClick){
35403                 hide();
35404                 tm.disable();
35405                 tm.enable.defer(100, tm);
35406             }
35407         }
35408     };
35409     
35410     var getPad = function(){
35411         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35412     };
35413
35414     var show = function(o){
35415         if(disabled){
35416             return;
35417         }
35418         clearTimeout(dismissProc);
35419         ce = o;
35420         if(removeCls){ // in case manually hidden
35421             el.removeClass(removeCls);
35422             removeCls = null;
35423         }
35424         if(ce.cls){
35425             el.addClass(ce.cls);
35426             removeCls = ce.cls;
35427         }
35428         if(ce.title){
35429             tipTitle.update(ce.title);
35430             tipTitle.show();
35431         }else{
35432             tipTitle.update('');
35433             tipTitle.hide();
35434         }
35435         el.dom.style.width  = tm.maxWidth+'px';
35436         //tipBody.dom.style.width = '';
35437         tipBodyText.update(o.text);
35438         var p = getPad(), w = ce.width;
35439         if(!w){
35440             var td = tipBodyText.dom;
35441             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35442             if(aw > tm.maxWidth){
35443                 w = tm.maxWidth;
35444             }else if(aw < tm.minWidth){
35445                 w = tm.minWidth;
35446             }else{
35447                 w = aw;
35448             }
35449         }
35450         //tipBody.setWidth(w);
35451         el.setWidth(parseInt(w, 10) + p);
35452         if(ce.autoHide === false){
35453             close.setDisplayed(true);
35454             if(dd){
35455                 dd.unlock();
35456             }
35457         }else{
35458             close.setDisplayed(false);
35459             if(dd){
35460                 dd.lock();
35461             }
35462         }
35463         if(xy){
35464             el.avoidY = xy[1]-18;
35465             el.setXY(xy);
35466         }
35467         if(tm.animate){
35468             el.setOpacity(.1);
35469             el.setStyle("visibility", "visible");
35470             el.fadeIn({callback: afterShow});
35471         }else{
35472             afterShow();
35473         }
35474     };
35475     
35476     var afterShow = function(){
35477         if(ce){
35478             el.show();
35479             esc.enable();
35480             if(tm.autoDismiss && ce.autoHide !== false){
35481                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35482             }
35483         }
35484     };
35485     
35486     var hide = function(noanim){
35487         clearTimeout(dismissProc);
35488         clearTimeout(hideProc);
35489         ce = null;
35490         if(el.isVisible()){
35491             esc.disable();
35492             if(noanim !== true && tm.animate){
35493                 el.fadeOut({callback: afterHide});
35494             }else{
35495                 afterHide();
35496             } 
35497         }
35498     };
35499     
35500     var afterHide = function(){
35501         el.hide();
35502         if(removeCls){
35503             el.removeClass(removeCls);
35504             removeCls = null;
35505         }
35506     };
35507     
35508     return {
35509         /**
35510         * @cfg {Number} minWidth
35511         * The minimum width of the quick tip (defaults to 40)
35512         */
35513        minWidth : 40,
35514         /**
35515         * @cfg {Number} maxWidth
35516         * The maximum width of the quick tip (defaults to 300)
35517         */
35518        maxWidth : 300,
35519         /**
35520         * @cfg {Boolean} interceptTitles
35521         * True to automatically use the element's DOM title value if available (defaults to false)
35522         */
35523        interceptTitles : false,
35524         /**
35525         * @cfg {Boolean} trackMouse
35526         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35527         */
35528        trackMouse : false,
35529         /**
35530         * @cfg {Boolean} hideOnClick
35531         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35532         */
35533        hideOnClick : true,
35534         /**
35535         * @cfg {Number} showDelay
35536         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35537         */
35538        showDelay : 500,
35539         /**
35540         * @cfg {Number} hideDelay
35541         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35542         */
35543        hideDelay : 200,
35544         /**
35545         * @cfg {Boolean} autoHide
35546         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35547         * Used in conjunction with hideDelay.
35548         */
35549        autoHide : true,
35550         /**
35551         * @cfg {Boolean}
35552         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35553         * (defaults to true).  Used in conjunction with autoDismissDelay.
35554         */
35555        autoDismiss : true,
35556         /**
35557         * @cfg {Number}
35558         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35559         */
35560        autoDismissDelay : 5000,
35561        /**
35562         * @cfg {Boolean} animate
35563         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35564         */
35565        animate : false,
35566
35567        /**
35568         * @cfg {String} title
35569         * Title text to display (defaults to '').  This can be any valid HTML markup.
35570         */
35571         title: '',
35572        /**
35573         * @cfg {String} text
35574         * Body text to display (defaults to '').  This can be any valid HTML markup.
35575         */
35576         text : '',
35577        /**
35578         * @cfg {String} cls
35579         * A CSS class to apply to the base quick tip element (defaults to '').
35580         */
35581         cls : '',
35582        /**
35583         * @cfg {Number} width
35584         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35585         * minWidth or maxWidth.
35586         */
35587         width : null,
35588
35589     /**
35590      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35591      * or display QuickTips in a page.
35592      */
35593        init : function(){
35594           tm = Roo.QuickTips;
35595           cfg = tm.tagConfig;
35596           if(!inited){
35597               if(!Roo.isReady){ // allow calling of init() before onReady
35598                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35599                   return;
35600               }
35601               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35602               el.fxDefaults = {stopFx: true};
35603               // maximum custom styling
35604               //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>');
35605               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>');              
35606               tipTitle = el.child('h3');
35607               tipTitle.enableDisplayMode("block");
35608               tipBody = el.child('div.x-tip-bd');
35609               tipBodyText = el.child('div.x-tip-bd-inner');
35610               //bdLeft = el.child('div.x-tip-bd-left');
35611               //bdRight = el.child('div.x-tip-bd-right');
35612               close = el.child('div.x-tip-close');
35613               close.enableDisplayMode("block");
35614               close.on("click", hide);
35615               var d = Roo.get(document);
35616               d.on("mousedown", onDown);
35617               d.on("mouseover", onOver);
35618               d.on("mouseout", onOut);
35619               d.on("mousemove", onMove);
35620               esc = d.addKeyListener(27, hide);
35621               esc.disable();
35622               if(Roo.dd.DD){
35623                   dd = el.initDD("default", null, {
35624                       onDrag : function(){
35625                           el.sync();  
35626                       }
35627                   });
35628                   dd.setHandleElId(tipTitle.id);
35629                   dd.lock();
35630               }
35631               inited = true;
35632           }
35633           this.enable(); 
35634        },
35635
35636     /**
35637      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35638      * are supported:
35639      * <pre>
35640 Property    Type                   Description
35641 ----------  ---------------------  ------------------------------------------------------------------------
35642 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35643      * </ul>
35644      * @param {Object} config The config object
35645      */
35646        register : function(config){
35647            var cs = config instanceof Array ? config : arguments;
35648            for(var i = 0, len = cs.length; i < len; i++) {
35649                var c = cs[i];
35650                var target = c.target;
35651                if(target){
35652                    if(target instanceof Array){
35653                        for(var j = 0, jlen = target.length; j < jlen; j++){
35654                            tagEls[target[j]] = c;
35655                        }
35656                    }else{
35657                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35658                    }
35659                }
35660            }
35661        },
35662
35663     /**
35664      * Removes this quick tip from its element and destroys it.
35665      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35666      */
35667        unregister : function(el){
35668            delete tagEls[Roo.id(el)];
35669        },
35670
35671     /**
35672      * Enable this quick tip.
35673      */
35674        enable : function(){
35675            if(inited && disabled){
35676                locks.pop();
35677                if(locks.length < 1){
35678                    disabled = false;
35679                }
35680            }
35681        },
35682
35683     /**
35684      * Disable this quick tip.
35685      */
35686        disable : function(){
35687           disabled = true;
35688           clearTimeout(showProc);
35689           clearTimeout(hideProc);
35690           clearTimeout(dismissProc);
35691           if(ce){
35692               hide(true);
35693           }
35694           locks.push(1);
35695        },
35696
35697     /**
35698      * Returns true if the quick tip is enabled, else false.
35699      */
35700        isEnabled : function(){
35701             return !disabled;
35702        },
35703
35704         // private
35705        tagConfig : {
35706            namespace : "roo", // was ext?? this may break..
35707            alt_namespace : "ext",
35708            attribute : "qtip",
35709            width : "width",
35710            target : "target",
35711            title : "qtitle",
35712            hide : "hide",
35713            cls : "qclass"
35714        }
35715    };
35716 }();
35717
35718 // backwards compat
35719 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35720  * Based on:
35721  * Ext JS Library 1.1.1
35722  * Copyright(c) 2006-2007, Ext JS, LLC.
35723  *
35724  * Originally Released Under LGPL - original licence link has changed is not relivant.
35725  *
35726  * Fork - LGPL
35727  * <script type="text/javascript">
35728  */
35729  
35730
35731 /**
35732  * @class Roo.tree.TreePanel
35733  * @extends Roo.data.Tree
35734  * @cfg {Roo.tree.TreeNode} root The root node
35735  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35736  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35737  * @cfg {Boolean} enableDD true to enable drag and drop
35738  * @cfg {Boolean} enableDrag true to enable just drag
35739  * @cfg {Boolean} enableDrop true to enable just drop
35740  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35741  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35742  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35743  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35744  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35745  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35746  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35747  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35748  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35749  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35750  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35751  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35752  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35753  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35754  * @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>
35755  * @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>
35756  * 
35757  * @constructor
35758  * @param {String/HTMLElement/Element} el The container element
35759  * @param {Object} config
35760  */
35761 Roo.tree.TreePanel = function(el, config){
35762     var root = false;
35763     var loader = false;
35764     if (config.root) {
35765         root = config.root;
35766         delete config.root;
35767     }
35768     if (config.loader) {
35769         loader = config.loader;
35770         delete config.loader;
35771     }
35772     
35773     Roo.apply(this, config);
35774     Roo.tree.TreePanel.superclass.constructor.call(this);
35775     this.el = Roo.get(el);
35776     this.el.addClass('x-tree');
35777     //console.log(root);
35778     if (root) {
35779         this.setRootNode( Roo.factory(root, Roo.tree));
35780     }
35781     if (loader) {
35782         this.loader = Roo.factory(loader, Roo.tree);
35783     }
35784    /**
35785     * Read-only. The id of the container element becomes this TreePanel's id.
35786     */
35787     this.id = this.el.id;
35788     this.addEvents({
35789         /**
35790         * @event beforeload
35791         * Fires before a node is loaded, return false to cancel
35792         * @param {Node} node The node being loaded
35793         */
35794         "beforeload" : true,
35795         /**
35796         * @event load
35797         * Fires when a node is loaded
35798         * @param {Node} node The node that was loaded
35799         */
35800         "load" : true,
35801         /**
35802         * @event textchange
35803         * Fires when the text for a node is changed
35804         * @param {Node} node The node
35805         * @param {String} text The new text
35806         * @param {String} oldText The old text
35807         */
35808         "textchange" : true,
35809         /**
35810         * @event beforeexpand
35811         * Fires before a node is expanded, return false to cancel.
35812         * @param {Node} node The node
35813         * @param {Boolean} deep
35814         * @param {Boolean} anim
35815         */
35816         "beforeexpand" : true,
35817         /**
35818         * @event beforecollapse
35819         * Fires before a node is collapsed, return false to cancel.
35820         * @param {Node} node The node
35821         * @param {Boolean} deep
35822         * @param {Boolean} anim
35823         */
35824         "beforecollapse" : true,
35825         /**
35826         * @event expand
35827         * Fires when a node is expanded
35828         * @param {Node} node The node
35829         */
35830         "expand" : true,
35831         /**
35832         * @event disabledchange
35833         * Fires when the disabled status of a node changes
35834         * @param {Node} node The node
35835         * @param {Boolean} disabled
35836         */
35837         "disabledchange" : true,
35838         /**
35839         * @event collapse
35840         * Fires when a node is collapsed
35841         * @param {Node} node The node
35842         */
35843         "collapse" : true,
35844         /**
35845         * @event beforeclick
35846         * Fires before click processing on a node. Return false to cancel the default action.
35847         * @param {Node} node The node
35848         * @param {Roo.EventObject} e The event object
35849         */
35850         "beforeclick":true,
35851         /**
35852         * @event checkchange
35853         * Fires when a node with a checkbox's checked property changes
35854         * @param {Node} this This node
35855         * @param {Boolean} checked
35856         */
35857         "checkchange":true,
35858         /**
35859         * @event click
35860         * Fires when a node is clicked
35861         * @param {Node} node The node
35862         * @param {Roo.EventObject} e The event object
35863         */
35864         "click":true,
35865         /**
35866         * @event dblclick
35867         * Fires when a node is double clicked
35868         * @param {Node} node The node
35869         * @param {Roo.EventObject} e The event object
35870         */
35871         "dblclick":true,
35872         /**
35873         * @event contextmenu
35874         * Fires when a node is right clicked
35875         * @param {Node} node The node
35876         * @param {Roo.EventObject} e The event object
35877         */
35878         "contextmenu":true,
35879         /**
35880         * @event beforechildrenrendered
35881         * Fires right before the child nodes for a node are rendered
35882         * @param {Node} node The node
35883         */
35884         "beforechildrenrendered":true,
35885         /**
35886         * @event startdrag
35887         * Fires when a node starts being dragged
35888         * @param {Roo.tree.TreePanel} this
35889         * @param {Roo.tree.TreeNode} node
35890         * @param {event} e The raw browser event
35891         */ 
35892        "startdrag" : true,
35893        /**
35894         * @event enddrag
35895         * Fires when a drag operation is complete
35896         * @param {Roo.tree.TreePanel} this
35897         * @param {Roo.tree.TreeNode} node
35898         * @param {event} e The raw browser event
35899         */
35900        "enddrag" : true,
35901        /**
35902         * @event dragdrop
35903         * Fires when a dragged node is dropped on a valid DD target
35904         * @param {Roo.tree.TreePanel} this
35905         * @param {Roo.tree.TreeNode} node
35906         * @param {DD} dd The dd it was dropped on
35907         * @param {event} e The raw browser event
35908         */
35909        "dragdrop" : true,
35910        /**
35911         * @event beforenodedrop
35912         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35913         * passed to handlers has the following properties:<br />
35914         * <ul style="padding:5px;padding-left:16px;">
35915         * <li>tree - The TreePanel</li>
35916         * <li>target - The node being targeted for the drop</li>
35917         * <li>data - The drag data from the drag source</li>
35918         * <li>point - The point of the drop - append, above or below</li>
35919         * <li>source - The drag source</li>
35920         * <li>rawEvent - Raw mouse event</li>
35921         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35922         * to be inserted by setting them on this object.</li>
35923         * <li>cancel - Set this to true to cancel the drop.</li>
35924         * </ul>
35925         * @param {Object} dropEvent
35926         */
35927        "beforenodedrop" : true,
35928        /**
35929         * @event nodedrop
35930         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35931         * passed to handlers has the following properties:<br />
35932         * <ul style="padding:5px;padding-left:16px;">
35933         * <li>tree - The TreePanel</li>
35934         * <li>target - The node being targeted for the drop</li>
35935         * <li>data - The drag data from the drag source</li>
35936         * <li>point - The point of the drop - append, above or below</li>
35937         * <li>source - The drag source</li>
35938         * <li>rawEvent - Raw mouse event</li>
35939         * <li>dropNode - Dropped node(s).</li>
35940         * </ul>
35941         * @param {Object} dropEvent
35942         */
35943        "nodedrop" : true,
35944         /**
35945         * @event nodedragover
35946         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35947         * passed to handlers has the following properties:<br />
35948         * <ul style="padding:5px;padding-left:16px;">
35949         * <li>tree - The TreePanel</li>
35950         * <li>target - The node being targeted for the drop</li>
35951         * <li>data - The drag data from the drag source</li>
35952         * <li>point - The point of the drop - append, above or below</li>
35953         * <li>source - The drag source</li>
35954         * <li>rawEvent - Raw mouse event</li>
35955         * <li>dropNode - Drop node(s) provided by the source.</li>
35956         * <li>cancel - Set this to true to signal drop not allowed.</li>
35957         * </ul>
35958         * @param {Object} dragOverEvent
35959         */
35960        "nodedragover" : true,
35961        /**
35962         * @event appendnode
35963         * Fires when append node to the tree
35964         * @param {Roo.tree.TreePanel} this
35965         * @param {Roo.tree.TreeNode} node
35966         * @param {Number} index The index of the newly appended node
35967         */
35968        "appendnode" : true
35969         
35970     });
35971     if(this.singleExpand){
35972        this.on("beforeexpand", this.restrictExpand, this);
35973     }
35974     if (this.editor) {
35975         this.editor.tree = this;
35976         this.editor = Roo.factory(this.editor, Roo.tree);
35977     }
35978     
35979     if (this.selModel) {
35980         this.selModel = Roo.factory(this.selModel, Roo.tree);
35981     }
35982    
35983 };
35984 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35985     rootVisible : true,
35986     animate: Roo.enableFx,
35987     lines : true,
35988     enableDD : false,
35989     hlDrop : Roo.enableFx,
35990   
35991     renderer: false,
35992     
35993     rendererTip: false,
35994     // private
35995     restrictExpand : function(node){
35996         var p = node.parentNode;
35997         if(p){
35998             if(p.expandedChild && p.expandedChild.parentNode == p){
35999                 p.expandedChild.collapse();
36000             }
36001             p.expandedChild = node;
36002         }
36003     },
36004
36005     // private override
36006     setRootNode : function(node){
36007         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
36008         if(!this.rootVisible){
36009             node.ui = new Roo.tree.RootTreeNodeUI(node);
36010         }
36011         return node;
36012     },
36013
36014     /**
36015      * Returns the container element for this TreePanel
36016      */
36017     getEl : function(){
36018         return this.el;
36019     },
36020
36021     /**
36022      * Returns the default TreeLoader for this TreePanel
36023      */
36024     getLoader : function(){
36025         return this.loader;
36026     },
36027
36028     /**
36029      * Expand all nodes
36030      */
36031     expandAll : function(){
36032         this.root.expand(true);
36033     },
36034
36035     /**
36036      * Collapse all nodes
36037      */
36038     collapseAll : function(){
36039         this.root.collapse(true);
36040     },
36041
36042     /**
36043      * Returns the selection model used by this TreePanel
36044      */
36045     getSelectionModel : function(){
36046         if(!this.selModel){
36047             this.selModel = new Roo.tree.DefaultSelectionModel();
36048         }
36049         return this.selModel;
36050     },
36051
36052     /**
36053      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
36054      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
36055      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
36056      * @return {Array}
36057      */
36058     getChecked : function(a, startNode){
36059         startNode = startNode || this.root;
36060         var r = [];
36061         var f = function(){
36062             if(this.attributes.checked){
36063                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
36064             }
36065         }
36066         startNode.cascade(f);
36067         return r;
36068     },
36069
36070     /**
36071      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36072      * @param {String} path
36073      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36074      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
36075      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
36076      */
36077     expandPath : function(path, attr, callback){
36078         attr = attr || "id";
36079         var keys = path.split(this.pathSeparator);
36080         var curNode = this.root;
36081         if(curNode.attributes[attr] != keys[1]){ // invalid root
36082             if(callback){
36083                 callback(false, null);
36084             }
36085             return;
36086         }
36087         var index = 1;
36088         var f = function(){
36089             if(++index == keys.length){
36090                 if(callback){
36091                     callback(true, curNode);
36092                 }
36093                 return;
36094             }
36095             var c = curNode.findChild(attr, keys[index]);
36096             if(!c){
36097                 if(callback){
36098                     callback(false, curNode);
36099                 }
36100                 return;
36101             }
36102             curNode = c;
36103             c.expand(false, false, f);
36104         };
36105         curNode.expand(false, false, f);
36106     },
36107
36108     /**
36109      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36110      * @param {String} path
36111      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36112      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
36113      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
36114      */
36115     selectPath : function(path, attr, callback){
36116         attr = attr || "id";
36117         var keys = path.split(this.pathSeparator);
36118         var v = keys.pop();
36119         if(keys.length > 0){
36120             var f = function(success, node){
36121                 if(success && node){
36122                     var n = node.findChild(attr, v);
36123                     if(n){
36124                         n.select();
36125                         if(callback){
36126                             callback(true, n);
36127                         }
36128                     }else if(callback){
36129                         callback(false, n);
36130                     }
36131                 }else{
36132                     if(callback){
36133                         callback(false, n);
36134                     }
36135                 }
36136             };
36137             this.expandPath(keys.join(this.pathSeparator), attr, f);
36138         }else{
36139             this.root.select();
36140             if(callback){
36141                 callback(true, this.root);
36142             }
36143         }
36144     },
36145
36146     getTreeEl : function(){
36147         return this.el;
36148     },
36149
36150     /**
36151      * Trigger rendering of this TreePanel
36152      */
36153     render : function(){
36154         if (this.innerCt) {
36155             return this; // stop it rendering more than once!!
36156         }
36157         
36158         this.innerCt = this.el.createChild({tag:"ul",
36159                cls:"x-tree-root-ct " +
36160                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
36161
36162         if(this.containerScroll){
36163             Roo.dd.ScrollManager.register(this.el);
36164         }
36165         if((this.enableDD || this.enableDrop) && !this.dropZone){
36166            /**
36167             * The dropZone used by this tree if drop is enabled
36168             * @type Roo.tree.TreeDropZone
36169             */
36170              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
36171                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
36172            });
36173         }
36174         if((this.enableDD || this.enableDrag) && !this.dragZone){
36175            /**
36176             * The dragZone used by this tree if drag is enabled
36177             * @type Roo.tree.TreeDragZone
36178             */
36179             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
36180                ddGroup: this.ddGroup || "TreeDD",
36181                scroll: this.ddScroll
36182            });
36183         }
36184         this.getSelectionModel().init(this);
36185         if (!this.root) {
36186             Roo.log("ROOT not set in tree");
36187             return this;
36188         }
36189         this.root.render();
36190         if(!this.rootVisible){
36191             this.root.renderChildren();
36192         }
36193         return this;
36194     }
36195 });/*
36196  * Based on:
36197  * Ext JS Library 1.1.1
36198  * Copyright(c) 2006-2007, Ext JS, LLC.
36199  *
36200  * Originally Released Under LGPL - original licence link has changed is not relivant.
36201  *
36202  * Fork - LGPL
36203  * <script type="text/javascript">
36204  */
36205  
36206
36207 /**
36208  * @class Roo.tree.DefaultSelectionModel
36209  * @extends Roo.util.Observable
36210  * The default single selection for a TreePanel.
36211  * @param {Object} cfg Configuration
36212  */
36213 Roo.tree.DefaultSelectionModel = function(cfg){
36214    this.selNode = null;
36215    
36216    
36217    
36218    this.addEvents({
36219        /**
36220         * @event selectionchange
36221         * Fires when the selected node changes
36222         * @param {DefaultSelectionModel} this
36223         * @param {TreeNode} node the new selection
36224         */
36225        "selectionchange" : true,
36226
36227        /**
36228         * @event beforeselect
36229         * Fires before the selected node changes, return false to cancel the change
36230         * @param {DefaultSelectionModel} this
36231         * @param {TreeNode} node the new selection
36232         * @param {TreeNode} node the old selection
36233         */
36234        "beforeselect" : true
36235    });
36236    
36237     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36238 };
36239
36240 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36241     init : function(tree){
36242         this.tree = tree;
36243         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36244         tree.on("click", this.onNodeClick, this);
36245     },
36246     
36247     onNodeClick : function(node, e){
36248         if (e.ctrlKey && this.selNode == node)  {
36249             this.unselect(node);
36250             return;
36251         }
36252         this.select(node);
36253     },
36254     
36255     /**
36256      * Select a node.
36257      * @param {TreeNode} node The node to select
36258      * @return {TreeNode} The selected node
36259      */
36260     select : function(node){
36261         var last = this.selNode;
36262         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36263             if(last){
36264                 last.ui.onSelectedChange(false);
36265             }
36266             this.selNode = node;
36267             node.ui.onSelectedChange(true);
36268             this.fireEvent("selectionchange", this, node, last);
36269         }
36270         return node;
36271     },
36272     
36273     /**
36274      * Deselect a node.
36275      * @param {TreeNode} node The node to unselect
36276      */
36277     unselect : function(node){
36278         if(this.selNode == node){
36279             this.clearSelections();
36280         }    
36281     },
36282     
36283     /**
36284      * Clear all selections
36285      */
36286     clearSelections : function(){
36287         var n = this.selNode;
36288         if(n){
36289             n.ui.onSelectedChange(false);
36290             this.selNode = null;
36291             this.fireEvent("selectionchange", this, null);
36292         }
36293         return n;
36294     },
36295     
36296     /**
36297      * Get the selected node
36298      * @return {TreeNode} The selected node
36299      */
36300     getSelectedNode : function(){
36301         return this.selNode;    
36302     },
36303     
36304     /**
36305      * Returns true if the node is selected
36306      * @param {TreeNode} node The node to check
36307      * @return {Boolean}
36308      */
36309     isSelected : function(node){
36310         return this.selNode == node;  
36311     },
36312
36313     /**
36314      * Selects the node above the selected node in the tree, intelligently walking the nodes
36315      * @return TreeNode The new selection
36316      */
36317     selectPrevious : function(){
36318         var s = this.selNode || this.lastSelNode;
36319         if(!s){
36320             return null;
36321         }
36322         var ps = s.previousSibling;
36323         if(ps){
36324             if(!ps.isExpanded() || ps.childNodes.length < 1){
36325                 return this.select(ps);
36326             } else{
36327                 var lc = ps.lastChild;
36328                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36329                     lc = lc.lastChild;
36330                 }
36331                 return this.select(lc);
36332             }
36333         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36334             return this.select(s.parentNode);
36335         }
36336         return null;
36337     },
36338
36339     /**
36340      * Selects the node above the selected node in the tree, intelligently walking the nodes
36341      * @return TreeNode The new selection
36342      */
36343     selectNext : function(){
36344         var s = this.selNode || this.lastSelNode;
36345         if(!s){
36346             return null;
36347         }
36348         if(s.firstChild && s.isExpanded()){
36349              return this.select(s.firstChild);
36350          }else if(s.nextSibling){
36351              return this.select(s.nextSibling);
36352          }else if(s.parentNode){
36353             var newS = null;
36354             s.parentNode.bubble(function(){
36355                 if(this.nextSibling){
36356                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36357                     return false;
36358                 }
36359             });
36360             return newS;
36361          }
36362         return null;
36363     },
36364
36365     onKeyDown : function(e){
36366         var s = this.selNode || this.lastSelNode;
36367         // undesirable, but required
36368         var sm = this;
36369         if(!s){
36370             return;
36371         }
36372         var k = e.getKey();
36373         switch(k){
36374              case e.DOWN:
36375                  e.stopEvent();
36376                  this.selectNext();
36377              break;
36378              case e.UP:
36379                  e.stopEvent();
36380                  this.selectPrevious();
36381              break;
36382              case e.RIGHT:
36383                  e.preventDefault();
36384                  if(s.hasChildNodes()){
36385                      if(!s.isExpanded()){
36386                          s.expand();
36387                      }else if(s.firstChild){
36388                          this.select(s.firstChild, e);
36389                      }
36390                  }
36391              break;
36392              case e.LEFT:
36393                  e.preventDefault();
36394                  if(s.hasChildNodes() && s.isExpanded()){
36395                      s.collapse();
36396                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36397                      this.select(s.parentNode, e);
36398                  }
36399              break;
36400         };
36401     }
36402 });
36403
36404 /**
36405  * @class Roo.tree.MultiSelectionModel
36406  * @extends Roo.util.Observable
36407  * Multi selection for a TreePanel.
36408  * @param {Object} cfg Configuration
36409  */
36410 Roo.tree.MultiSelectionModel = function(){
36411    this.selNodes = [];
36412    this.selMap = {};
36413    this.addEvents({
36414        /**
36415         * @event selectionchange
36416         * Fires when the selected nodes change
36417         * @param {MultiSelectionModel} this
36418         * @param {Array} nodes Array of the selected nodes
36419         */
36420        "selectionchange" : true
36421    });
36422    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36423    
36424 };
36425
36426 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36427     init : function(tree){
36428         this.tree = tree;
36429         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36430         tree.on("click", this.onNodeClick, this);
36431     },
36432     
36433     onNodeClick : function(node, e){
36434         this.select(node, e, e.ctrlKey);
36435     },
36436     
36437     /**
36438      * Select a node.
36439      * @param {TreeNode} node The node to select
36440      * @param {EventObject} e (optional) An event associated with the selection
36441      * @param {Boolean} keepExisting True to retain existing selections
36442      * @return {TreeNode} The selected node
36443      */
36444     select : function(node, e, keepExisting){
36445         if(keepExisting !== true){
36446             this.clearSelections(true);
36447         }
36448         if(this.isSelected(node)){
36449             this.lastSelNode = node;
36450             return node;
36451         }
36452         this.selNodes.push(node);
36453         this.selMap[node.id] = node;
36454         this.lastSelNode = node;
36455         node.ui.onSelectedChange(true);
36456         this.fireEvent("selectionchange", this, this.selNodes);
36457         return node;
36458     },
36459     
36460     /**
36461      * Deselect a node.
36462      * @param {TreeNode} node The node to unselect
36463      */
36464     unselect : function(node){
36465         if(this.selMap[node.id]){
36466             node.ui.onSelectedChange(false);
36467             var sn = this.selNodes;
36468             var index = -1;
36469             if(sn.indexOf){
36470                 index = sn.indexOf(node);
36471             }else{
36472                 for(var i = 0, len = sn.length; i < len; i++){
36473                     if(sn[i] == node){
36474                         index = i;
36475                         break;
36476                     }
36477                 }
36478             }
36479             if(index != -1){
36480                 this.selNodes.splice(index, 1);
36481             }
36482             delete this.selMap[node.id];
36483             this.fireEvent("selectionchange", this, this.selNodes);
36484         }
36485     },
36486     
36487     /**
36488      * Clear all selections
36489      */
36490     clearSelections : function(suppressEvent){
36491         var sn = this.selNodes;
36492         if(sn.length > 0){
36493             for(var i = 0, len = sn.length; i < len; i++){
36494                 sn[i].ui.onSelectedChange(false);
36495             }
36496             this.selNodes = [];
36497             this.selMap = {};
36498             if(suppressEvent !== true){
36499                 this.fireEvent("selectionchange", this, this.selNodes);
36500             }
36501         }
36502     },
36503     
36504     /**
36505      * Returns true if the node is selected
36506      * @param {TreeNode} node The node to check
36507      * @return {Boolean}
36508      */
36509     isSelected : function(node){
36510         return this.selMap[node.id] ? true : false;  
36511     },
36512     
36513     /**
36514      * Returns an array of the selected nodes
36515      * @return {Array}
36516      */
36517     getSelectedNodes : function(){
36518         return this.selNodes;    
36519     },
36520
36521     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36522
36523     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36524
36525     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36526 });/*
36527  * Based on:
36528  * Ext JS Library 1.1.1
36529  * Copyright(c) 2006-2007, Ext JS, LLC.
36530  *
36531  * Originally Released Under LGPL - original licence link has changed is not relivant.
36532  *
36533  * Fork - LGPL
36534  * <script type="text/javascript">
36535  */
36536  
36537 /**
36538  * @class Roo.tree.TreeNode
36539  * @extends Roo.data.Node
36540  * @cfg {String} text The text for this node
36541  * @cfg {Boolean} expanded true to start the node expanded
36542  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36543  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36544  * @cfg {Boolean} disabled true to start the node disabled
36545  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36546  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36547  * @cfg {String} cls A css class to be added to the node
36548  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36549  * @cfg {String} href URL of the link used for the node (defaults to #)
36550  * @cfg {String} hrefTarget target frame for the link
36551  * @cfg {String} qtip An Ext QuickTip for the node
36552  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36553  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36554  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36555  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36556  * (defaults to undefined with no checkbox rendered)
36557  * @constructor
36558  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36559  */
36560 Roo.tree.TreeNode = function(attributes){
36561     attributes = attributes || {};
36562     if(typeof attributes == "string"){
36563         attributes = {text: attributes};
36564     }
36565     this.childrenRendered = false;
36566     this.rendered = false;
36567     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36568     this.expanded = attributes.expanded === true;
36569     this.isTarget = attributes.isTarget !== false;
36570     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36571     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36572
36573     /**
36574      * Read-only. The text for this node. To change it use setText().
36575      * @type String
36576      */
36577     this.text = attributes.text;
36578     /**
36579      * True if this node is disabled.
36580      * @type Boolean
36581      */
36582     this.disabled = attributes.disabled === true;
36583
36584     this.addEvents({
36585         /**
36586         * @event textchange
36587         * Fires when the text for this node is changed
36588         * @param {Node} this This node
36589         * @param {String} text The new text
36590         * @param {String} oldText The old text
36591         */
36592         "textchange" : true,
36593         /**
36594         * @event beforeexpand
36595         * Fires before this node is expanded, return false to cancel.
36596         * @param {Node} this This node
36597         * @param {Boolean} deep
36598         * @param {Boolean} anim
36599         */
36600         "beforeexpand" : true,
36601         /**
36602         * @event beforecollapse
36603         * Fires before this node is collapsed, return false to cancel.
36604         * @param {Node} this This node
36605         * @param {Boolean} deep
36606         * @param {Boolean} anim
36607         */
36608         "beforecollapse" : true,
36609         /**
36610         * @event expand
36611         * Fires when this node is expanded
36612         * @param {Node} this This node
36613         */
36614         "expand" : true,
36615         /**
36616         * @event disabledchange
36617         * Fires when the disabled status of this node changes
36618         * @param {Node} this This node
36619         * @param {Boolean} disabled
36620         */
36621         "disabledchange" : true,
36622         /**
36623         * @event collapse
36624         * Fires when this node is collapsed
36625         * @param {Node} this This node
36626         */
36627         "collapse" : true,
36628         /**
36629         * @event beforeclick
36630         * Fires before click processing. Return false to cancel the default action.
36631         * @param {Node} this This node
36632         * @param {Roo.EventObject} e The event object
36633         */
36634         "beforeclick":true,
36635         /**
36636         * @event checkchange
36637         * Fires when a node with a checkbox's checked property changes
36638         * @param {Node} this This node
36639         * @param {Boolean} checked
36640         */
36641         "checkchange":true,
36642         /**
36643         * @event click
36644         * Fires when this node is clicked
36645         * @param {Node} this This node
36646         * @param {Roo.EventObject} e The event object
36647         */
36648         "click":true,
36649         /**
36650         * @event dblclick
36651         * Fires when this node is double clicked
36652         * @param {Node} this This node
36653         * @param {Roo.EventObject} e The event object
36654         */
36655         "dblclick":true,
36656         /**
36657         * @event contextmenu
36658         * Fires when this node is right clicked
36659         * @param {Node} this This node
36660         * @param {Roo.EventObject} e The event object
36661         */
36662         "contextmenu":true,
36663         /**
36664         * @event beforechildrenrendered
36665         * Fires right before the child nodes for this node are rendered
36666         * @param {Node} this This node
36667         */
36668         "beforechildrenrendered":true
36669     });
36670
36671     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36672
36673     /**
36674      * Read-only. The UI for this node
36675      * @type TreeNodeUI
36676      */
36677     this.ui = new uiClass(this);
36678     
36679     // finally support items[]
36680     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36681         return;
36682     }
36683     
36684     
36685     Roo.each(this.attributes.items, function(c) {
36686         this.appendChild(Roo.factory(c,Roo.Tree));
36687     }, this);
36688     delete this.attributes.items;
36689     
36690     
36691     
36692 };
36693 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36694     preventHScroll: true,
36695     /**
36696      * Returns true if this node is expanded
36697      * @return {Boolean}
36698      */
36699     isExpanded : function(){
36700         return this.expanded;
36701     },
36702
36703     /**
36704      * Returns the UI object for this node
36705      * @return {TreeNodeUI}
36706      */
36707     getUI : function(){
36708         return this.ui;
36709     },
36710
36711     // private override
36712     setFirstChild : function(node){
36713         var of = this.firstChild;
36714         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36715         if(this.childrenRendered && of && node != of){
36716             of.renderIndent(true, true);
36717         }
36718         if(this.rendered){
36719             this.renderIndent(true, true);
36720         }
36721     },
36722
36723     // private override
36724     setLastChild : function(node){
36725         var ol = this.lastChild;
36726         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36727         if(this.childrenRendered && ol && node != ol){
36728             ol.renderIndent(true, true);
36729         }
36730         if(this.rendered){
36731             this.renderIndent(true, true);
36732         }
36733     },
36734
36735     // these methods are overridden to provide lazy rendering support
36736     // private override
36737     appendChild : function()
36738     {
36739         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36740         if(node && this.childrenRendered){
36741             node.render();
36742         }
36743         this.ui.updateExpandIcon();
36744         return node;
36745     },
36746
36747     // private override
36748     removeChild : function(node){
36749         this.ownerTree.getSelectionModel().unselect(node);
36750         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36751         // if it's been rendered remove dom node
36752         if(this.childrenRendered){
36753             node.ui.remove();
36754         }
36755         if(this.childNodes.length < 1){
36756             this.collapse(false, false);
36757         }else{
36758             this.ui.updateExpandIcon();
36759         }
36760         if(!this.firstChild) {
36761             this.childrenRendered = false;
36762         }
36763         return node;
36764     },
36765
36766     // private override
36767     insertBefore : function(node, refNode){
36768         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36769         if(newNode && refNode && this.childrenRendered){
36770             node.render();
36771         }
36772         this.ui.updateExpandIcon();
36773         return newNode;
36774     },
36775
36776     /**
36777      * Sets the text for this node
36778      * @param {String} text
36779      */
36780     setText : function(text){
36781         var oldText = this.text;
36782         this.text = text;
36783         this.attributes.text = text;
36784         if(this.rendered){ // event without subscribing
36785             this.ui.onTextChange(this, text, oldText);
36786         }
36787         this.fireEvent("textchange", this, text, oldText);
36788     },
36789
36790     /**
36791      * Triggers selection of this node
36792      */
36793     select : function(){
36794         this.getOwnerTree().getSelectionModel().select(this);
36795     },
36796
36797     /**
36798      * Triggers deselection of this node
36799      */
36800     unselect : function(){
36801         this.getOwnerTree().getSelectionModel().unselect(this);
36802     },
36803
36804     /**
36805      * Returns true if this node is selected
36806      * @return {Boolean}
36807      */
36808     isSelected : function(){
36809         return this.getOwnerTree().getSelectionModel().isSelected(this);
36810     },
36811
36812     /**
36813      * Expand this node.
36814      * @param {Boolean} deep (optional) True to expand all children as well
36815      * @param {Boolean} anim (optional) false to cancel the default animation
36816      * @param {Function} callback (optional) A callback to be called when
36817      * expanding this node completes (does not wait for deep expand to complete).
36818      * Called with 1 parameter, this node.
36819      */
36820     expand : function(deep, anim, callback){
36821         if(!this.expanded){
36822             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36823                 return;
36824             }
36825             if(!this.childrenRendered){
36826                 this.renderChildren();
36827             }
36828             this.expanded = true;
36829             
36830             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36831                 this.ui.animExpand(function(){
36832                     this.fireEvent("expand", this);
36833                     if(typeof callback == "function"){
36834                         callback(this);
36835                     }
36836                     if(deep === true){
36837                         this.expandChildNodes(true);
36838                     }
36839                 }.createDelegate(this));
36840                 return;
36841             }else{
36842                 this.ui.expand();
36843                 this.fireEvent("expand", this);
36844                 if(typeof callback == "function"){
36845                     callback(this);
36846                 }
36847             }
36848         }else{
36849            if(typeof callback == "function"){
36850                callback(this);
36851            }
36852         }
36853         if(deep === true){
36854             this.expandChildNodes(true);
36855         }
36856     },
36857
36858     isHiddenRoot : function(){
36859         return this.isRoot && !this.getOwnerTree().rootVisible;
36860     },
36861
36862     /**
36863      * Collapse this node.
36864      * @param {Boolean} deep (optional) True to collapse all children as well
36865      * @param {Boolean} anim (optional) false to cancel the default animation
36866      */
36867     collapse : function(deep, anim){
36868         if(this.expanded && !this.isHiddenRoot()){
36869             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36870                 return;
36871             }
36872             this.expanded = false;
36873             if((this.getOwnerTree().animate && anim !== false) || anim){
36874                 this.ui.animCollapse(function(){
36875                     this.fireEvent("collapse", this);
36876                     if(deep === true){
36877                         this.collapseChildNodes(true);
36878                     }
36879                 }.createDelegate(this));
36880                 return;
36881             }else{
36882                 this.ui.collapse();
36883                 this.fireEvent("collapse", this);
36884             }
36885         }
36886         if(deep === true){
36887             var cs = this.childNodes;
36888             for(var i = 0, len = cs.length; i < len; i++) {
36889                 cs[i].collapse(true, false);
36890             }
36891         }
36892     },
36893
36894     // private
36895     delayedExpand : function(delay){
36896         if(!this.expandProcId){
36897             this.expandProcId = this.expand.defer(delay, this);
36898         }
36899     },
36900
36901     // private
36902     cancelExpand : function(){
36903         if(this.expandProcId){
36904             clearTimeout(this.expandProcId);
36905         }
36906         this.expandProcId = false;
36907     },
36908
36909     /**
36910      * Toggles expanded/collapsed state of the node
36911      */
36912     toggle : function(){
36913         if(this.expanded){
36914             this.collapse();
36915         }else{
36916             this.expand();
36917         }
36918     },
36919
36920     /**
36921      * Ensures all parent nodes are expanded
36922      */
36923     ensureVisible : function(callback){
36924         var tree = this.getOwnerTree();
36925         tree.expandPath(this.parentNode.getPath(), false, function(){
36926             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36927             Roo.callback(callback);
36928         }.createDelegate(this));
36929     },
36930
36931     /**
36932      * Expand all child nodes
36933      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36934      */
36935     expandChildNodes : function(deep){
36936         var cs = this.childNodes;
36937         for(var i = 0, len = cs.length; i < len; i++) {
36938                 cs[i].expand(deep);
36939         }
36940     },
36941
36942     /**
36943      * Collapse all child nodes
36944      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36945      */
36946     collapseChildNodes : function(deep){
36947         var cs = this.childNodes;
36948         for(var i = 0, len = cs.length; i < len; i++) {
36949                 cs[i].collapse(deep);
36950         }
36951     },
36952
36953     /**
36954      * Disables this node
36955      */
36956     disable : function(){
36957         this.disabled = true;
36958         this.unselect();
36959         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36960             this.ui.onDisableChange(this, true);
36961         }
36962         this.fireEvent("disabledchange", this, true);
36963     },
36964
36965     /**
36966      * Enables this node
36967      */
36968     enable : function(){
36969         this.disabled = false;
36970         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36971             this.ui.onDisableChange(this, false);
36972         }
36973         this.fireEvent("disabledchange", this, false);
36974     },
36975
36976     // private
36977     renderChildren : function(suppressEvent){
36978         if(suppressEvent !== false){
36979             this.fireEvent("beforechildrenrendered", this);
36980         }
36981         var cs = this.childNodes;
36982         for(var i = 0, len = cs.length; i < len; i++){
36983             cs[i].render(true);
36984         }
36985         this.childrenRendered = true;
36986     },
36987
36988     // private
36989     sort : function(fn, scope){
36990         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36991         if(this.childrenRendered){
36992             var cs = this.childNodes;
36993             for(var i = 0, len = cs.length; i < len; i++){
36994                 cs[i].render(true);
36995             }
36996         }
36997     },
36998
36999     // private
37000     render : function(bulkRender){
37001         this.ui.render(bulkRender);
37002         if(!this.rendered){
37003             this.rendered = true;
37004             if(this.expanded){
37005                 this.expanded = false;
37006                 this.expand(false, false);
37007             }
37008         }
37009     },
37010
37011     // private
37012     renderIndent : function(deep, refresh){
37013         if(refresh){
37014             this.ui.childIndent = null;
37015         }
37016         this.ui.renderIndent();
37017         if(deep === true && this.childrenRendered){
37018             var cs = this.childNodes;
37019             for(var i = 0, len = cs.length; i < len; i++){
37020                 cs[i].renderIndent(true, refresh);
37021             }
37022         }
37023     }
37024 });/*
37025  * Based on:
37026  * Ext JS Library 1.1.1
37027  * Copyright(c) 2006-2007, Ext JS, LLC.
37028  *
37029  * Originally Released Under LGPL - original licence link has changed is not relivant.
37030  *
37031  * Fork - LGPL
37032  * <script type="text/javascript">
37033  */
37034  
37035 /**
37036  * @class Roo.tree.AsyncTreeNode
37037  * @extends Roo.tree.TreeNode
37038  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
37039  * @constructor
37040  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
37041  */
37042  Roo.tree.AsyncTreeNode = function(config){
37043     this.loaded = false;
37044     this.loading = false;
37045     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
37046     /**
37047     * @event beforeload
37048     * Fires before this node is loaded, return false to cancel
37049     * @param {Node} this This node
37050     */
37051     this.addEvents({'beforeload':true, 'load': true});
37052     /**
37053     * @event load
37054     * Fires when this node is loaded
37055     * @param {Node} this This node
37056     */
37057     /**
37058      * The loader used by this node (defaults to using the tree's defined loader)
37059      * @type TreeLoader
37060      * @property loader
37061      */
37062 };
37063 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
37064     expand : function(deep, anim, callback){
37065         if(this.loading){ // if an async load is already running, waiting til it's done
37066             var timer;
37067             var f = function(){
37068                 if(!this.loading){ // done loading
37069                     clearInterval(timer);
37070                     this.expand(deep, anim, callback);
37071                 }
37072             }.createDelegate(this);
37073             timer = setInterval(f, 200);
37074             return;
37075         }
37076         if(!this.loaded){
37077             if(this.fireEvent("beforeload", this) === false){
37078                 return;
37079             }
37080             this.loading = true;
37081             this.ui.beforeLoad(this);
37082             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
37083             if(loader){
37084                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
37085                 return;
37086             }
37087         }
37088         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
37089     },
37090     
37091     /**
37092      * Returns true if this node is currently loading
37093      * @return {Boolean}
37094      */
37095     isLoading : function(){
37096         return this.loading;  
37097     },
37098     
37099     loadComplete : function(deep, anim, callback){
37100         this.loading = false;
37101         this.loaded = true;
37102         this.ui.afterLoad(this);
37103         this.fireEvent("load", this);
37104         this.expand(deep, anim, callback);
37105     },
37106     
37107     /**
37108      * Returns true if this node has been loaded
37109      * @return {Boolean}
37110      */
37111     isLoaded : function(){
37112         return this.loaded;
37113     },
37114     
37115     hasChildNodes : function(){
37116         if(!this.isLeaf() && !this.loaded){
37117             return true;
37118         }else{
37119             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
37120         }
37121     },
37122
37123     /**
37124      * Trigger a reload for this node
37125      * @param {Function} callback
37126      */
37127     reload : function(callback){
37128         this.collapse(false, false);
37129         while(this.firstChild){
37130             this.removeChild(this.firstChild);
37131         }
37132         this.childrenRendered = false;
37133         this.loaded = false;
37134         if(this.isHiddenRoot()){
37135             this.expanded = false;
37136         }
37137         this.expand(false, false, callback);
37138     }
37139 });/*
37140  * Based on:
37141  * Ext JS Library 1.1.1
37142  * Copyright(c) 2006-2007, Ext JS, LLC.
37143  *
37144  * Originally Released Under LGPL - original licence link has changed is not relivant.
37145  *
37146  * Fork - LGPL
37147  * <script type="text/javascript">
37148  */
37149  
37150 /**
37151  * @class Roo.tree.TreeNodeUI
37152  * @constructor
37153  * @param {Object} node The node to render
37154  * The TreeNode UI implementation is separate from the
37155  * tree implementation. Unless you are customizing the tree UI,
37156  * you should never have to use this directly.
37157  */
37158 Roo.tree.TreeNodeUI = function(node){
37159     this.node = node;
37160     this.rendered = false;
37161     this.animating = false;
37162     this.emptyIcon = Roo.BLANK_IMAGE_URL;
37163 };
37164
37165 Roo.tree.TreeNodeUI.prototype = {
37166     removeChild : function(node){
37167         if(this.rendered){
37168             this.ctNode.removeChild(node.ui.getEl());
37169         }
37170     },
37171
37172     beforeLoad : function(){
37173          this.addClass("x-tree-node-loading");
37174     },
37175
37176     afterLoad : function(){
37177          this.removeClass("x-tree-node-loading");
37178     },
37179
37180     onTextChange : function(node, text, oldText){
37181         if(this.rendered){
37182             this.textNode.innerHTML = text;
37183         }
37184     },
37185
37186     onDisableChange : function(node, state){
37187         this.disabled = state;
37188         if(state){
37189             this.addClass("x-tree-node-disabled");
37190         }else{
37191             this.removeClass("x-tree-node-disabled");
37192         }
37193     },
37194
37195     onSelectedChange : function(state){
37196         if(state){
37197             this.focus();
37198             this.addClass("x-tree-selected");
37199         }else{
37200             //this.blur();
37201             this.removeClass("x-tree-selected");
37202         }
37203     },
37204
37205     onMove : function(tree, node, oldParent, newParent, index, refNode){
37206         this.childIndent = null;
37207         if(this.rendered){
37208             var targetNode = newParent.ui.getContainer();
37209             if(!targetNode){//target not rendered
37210                 this.holder = document.createElement("div");
37211                 this.holder.appendChild(this.wrap);
37212                 return;
37213             }
37214             var insertBefore = refNode ? refNode.ui.getEl() : null;
37215             if(insertBefore){
37216                 targetNode.insertBefore(this.wrap, insertBefore);
37217             }else{
37218                 targetNode.appendChild(this.wrap);
37219             }
37220             this.node.renderIndent(true);
37221         }
37222     },
37223
37224     addClass : function(cls){
37225         if(this.elNode){
37226             Roo.fly(this.elNode).addClass(cls);
37227         }
37228     },
37229
37230     removeClass : function(cls){
37231         if(this.elNode){
37232             Roo.fly(this.elNode).removeClass(cls);
37233         }
37234     },
37235
37236     remove : function(){
37237         if(this.rendered){
37238             this.holder = document.createElement("div");
37239             this.holder.appendChild(this.wrap);
37240         }
37241     },
37242
37243     fireEvent : function(){
37244         return this.node.fireEvent.apply(this.node, arguments);
37245     },
37246
37247     initEvents : function(){
37248         this.node.on("move", this.onMove, this);
37249         var E = Roo.EventManager;
37250         var a = this.anchor;
37251
37252         var el = Roo.fly(a, '_treeui');
37253
37254         if(Roo.isOpera){ // opera render bug ignores the CSS
37255             el.setStyle("text-decoration", "none");
37256         }
37257
37258         el.on("click", this.onClick, this);
37259         el.on("dblclick", this.onDblClick, this);
37260
37261         if(this.checkbox){
37262             Roo.EventManager.on(this.checkbox,
37263                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37264         }
37265
37266         el.on("contextmenu", this.onContextMenu, this);
37267
37268         var icon = Roo.fly(this.iconNode);
37269         icon.on("click", this.onClick, this);
37270         icon.on("dblclick", this.onDblClick, this);
37271         icon.on("contextmenu", this.onContextMenu, this);
37272         E.on(this.ecNode, "click", this.ecClick, this, true);
37273
37274         if(this.node.disabled){
37275             this.addClass("x-tree-node-disabled");
37276         }
37277         if(this.node.hidden){
37278             this.addClass("x-tree-node-disabled");
37279         }
37280         var ot = this.node.getOwnerTree();
37281         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37282         if(dd && (!this.node.isRoot || ot.rootVisible)){
37283             Roo.dd.Registry.register(this.elNode, {
37284                 node: this.node,
37285                 handles: this.getDDHandles(),
37286                 isHandle: false
37287             });
37288         }
37289     },
37290
37291     getDDHandles : function(){
37292         return [this.iconNode, this.textNode];
37293     },
37294
37295     hide : function(){
37296         if(this.rendered){
37297             this.wrap.style.display = "none";
37298         }
37299     },
37300
37301     show : function(){
37302         if(this.rendered){
37303             this.wrap.style.display = "";
37304         }
37305     },
37306
37307     onContextMenu : function(e){
37308         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37309             e.preventDefault();
37310             this.focus();
37311             this.fireEvent("contextmenu", this.node, e);
37312         }
37313     },
37314
37315     onClick : function(e){
37316         if(this.dropping){
37317             e.stopEvent();
37318             return;
37319         }
37320         if(this.fireEvent("beforeclick", this.node, e) !== false){
37321             if(!this.disabled && this.node.attributes.href){
37322                 this.fireEvent("click", this.node, e);
37323                 return;
37324             }
37325             e.preventDefault();
37326             if(this.disabled){
37327                 return;
37328             }
37329
37330             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37331                 this.node.toggle();
37332             }
37333
37334             this.fireEvent("click", this.node, e);
37335         }else{
37336             e.stopEvent();
37337         }
37338     },
37339
37340     onDblClick : function(e){
37341         e.preventDefault();
37342         if(this.disabled){
37343             return;
37344         }
37345         if(this.checkbox){
37346             this.toggleCheck();
37347         }
37348         if(!this.animating && this.node.hasChildNodes()){
37349             this.node.toggle();
37350         }
37351         this.fireEvent("dblclick", this.node, e);
37352     },
37353
37354     onCheckChange : function(){
37355         var checked = this.checkbox.checked;
37356         this.node.attributes.checked = checked;
37357         this.fireEvent('checkchange', this.node, checked);
37358     },
37359
37360     ecClick : function(e){
37361         if(!this.animating && this.node.hasChildNodes()){
37362             this.node.toggle();
37363         }
37364     },
37365
37366     startDrop : function(){
37367         this.dropping = true;
37368     },
37369
37370     // delayed drop so the click event doesn't get fired on a drop
37371     endDrop : function(){
37372        setTimeout(function(){
37373            this.dropping = false;
37374        }.createDelegate(this), 50);
37375     },
37376
37377     expand : function(){
37378         this.updateExpandIcon();
37379         this.ctNode.style.display = "";
37380     },
37381
37382     focus : function(){
37383         if(!this.node.preventHScroll){
37384             try{this.anchor.focus();
37385             }catch(e){}
37386         }else if(!Roo.isIE){
37387             try{
37388                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37389                 var l = noscroll.scrollLeft;
37390                 this.anchor.focus();
37391                 noscroll.scrollLeft = l;
37392             }catch(e){}
37393         }
37394     },
37395
37396     toggleCheck : function(value){
37397         var cb = this.checkbox;
37398         if(cb){
37399             cb.checked = (value === undefined ? !cb.checked : value);
37400         }
37401     },
37402
37403     blur : function(){
37404         try{
37405             this.anchor.blur();
37406         }catch(e){}
37407     },
37408
37409     animExpand : function(callback){
37410         var ct = Roo.get(this.ctNode);
37411         ct.stopFx();
37412         if(!this.node.hasChildNodes()){
37413             this.updateExpandIcon();
37414             this.ctNode.style.display = "";
37415             Roo.callback(callback);
37416             return;
37417         }
37418         this.animating = true;
37419         this.updateExpandIcon();
37420
37421         ct.slideIn('t', {
37422            callback : function(){
37423                this.animating = false;
37424                Roo.callback(callback);
37425             },
37426             scope: this,
37427             duration: this.node.ownerTree.duration || .25
37428         });
37429     },
37430
37431     highlight : function(){
37432         var tree = this.node.getOwnerTree();
37433         Roo.fly(this.wrap).highlight(
37434             tree.hlColor || "C3DAF9",
37435             {endColor: tree.hlBaseColor}
37436         );
37437     },
37438
37439     collapse : function(){
37440         this.updateExpandIcon();
37441         this.ctNode.style.display = "none";
37442     },
37443
37444     animCollapse : function(callback){
37445         var ct = Roo.get(this.ctNode);
37446         ct.enableDisplayMode('block');
37447         ct.stopFx();
37448
37449         this.animating = true;
37450         this.updateExpandIcon();
37451
37452         ct.slideOut('t', {
37453             callback : function(){
37454                this.animating = false;
37455                Roo.callback(callback);
37456             },
37457             scope: this,
37458             duration: this.node.ownerTree.duration || .25
37459         });
37460     },
37461
37462     getContainer : function(){
37463         return this.ctNode;
37464     },
37465
37466     getEl : function(){
37467         return this.wrap;
37468     },
37469
37470     appendDDGhost : function(ghostNode){
37471         ghostNode.appendChild(this.elNode.cloneNode(true));
37472     },
37473
37474     getDDRepairXY : function(){
37475         return Roo.lib.Dom.getXY(this.iconNode);
37476     },
37477
37478     onRender : function(){
37479         this.render();
37480     },
37481
37482     render : function(bulkRender){
37483         var n = this.node, a = n.attributes;
37484         var targetNode = n.parentNode ?
37485               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37486
37487         if(!this.rendered){
37488             this.rendered = true;
37489
37490             this.renderElements(n, a, targetNode, bulkRender);
37491
37492             if(a.qtip){
37493                if(this.textNode.setAttributeNS){
37494                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37495                    if(a.qtipTitle){
37496                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37497                    }
37498                }else{
37499                    this.textNode.setAttribute("ext:qtip", a.qtip);
37500                    if(a.qtipTitle){
37501                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37502                    }
37503                }
37504             }else if(a.qtipCfg){
37505                 a.qtipCfg.target = Roo.id(this.textNode);
37506                 Roo.QuickTips.register(a.qtipCfg);
37507             }
37508             this.initEvents();
37509             if(!this.node.expanded){
37510                 this.updateExpandIcon();
37511             }
37512         }else{
37513             if(bulkRender === true) {
37514                 targetNode.appendChild(this.wrap);
37515             }
37516         }
37517     },
37518
37519     renderElements : function(n, a, targetNode, bulkRender)
37520     {
37521         // add some indent caching, this helps performance when rendering a large tree
37522         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37523         var t = n.getOwnerTree();
37524         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37525         if (typeof(n.attributes.html) != 'undefined') {
37526             txt = n.attributes.html;
37527         }
37528         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37529         var cb = typeof a.checked == 'boolean';
37530         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37531         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37532             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37533             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37534             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37535             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37536             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37537              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37538                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37539             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37540             "</li>"];
37541
37542         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37543             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37544                                 n.nextSibling.ui.getEl(), buf.join(""));
37545         }else{
37546             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37547         }
37548
37549         this.elNode = this.wrap.childNodes[0];
37550         this.ctNode = this.wrap.childNodes[1];
37551         var cs = this.elNode.childNodes;
37552         this.indentNode = cs[0];
37553         this.ecNode = cs[1];
37554         this.iconNode = cs[2];
37555         var index = 3;
37556         if(cb){
37557             this.checkbox = cs[3];
37558             index++;
37559         }
37560         this.anchor = cs[index];
37561         this.textNode = cs[index].firstChild;
37562     },
37563
37564     getAnchor : function(){
37565         return this.anchor;
37566     },
37567
37568     getTextEl : function(){
37569         return this.textNode;
37570     },
37571
37572     getIconEl : function(){
37573         return this.iconNode;
37574     },
37575
37576     isChecked : function(){
37577         return this.checkbox ? this.checkbox.checked : false;
37578     },
37579
37580     updateExpandIcon : function(){
37581         if(this.rendered){
37582             var n = this.node, c1, c2;
37583             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37584             var hasChild = n.hasChildNodes();
37585             if(hasChild){
37586                 if(n.expanded){
37587                     cls += "-minus";
37588                     c1 = "x-tree-node-collapsed";
37589                     c2 = "x-tree-node-expanded";
37590                 }else{
37591                     cls += "-plus";
37592                     c1 = "x-tree-node-expanded";
37593                     c2 = "x-tree-node-collapsed";
37594                 }
37595                 if(this.wasLeaf){
37596                     this.removeClass("x-tree-node-leaf");
37597                     this.wasLeaf = false;
37598                 }
37599                 if(this.c1 != c1 || this.c2 != c2){
37600                     Roo.fly(this.elNode).replaceClass(c1, c2);
37601                     this.c1 = c1; this.c2 = c2;
37602                 }
37603             }else{
37604                 // this changes non-leafs into leafs if they have no children.
37605                 // it's not very rational behaviour..
37606                 
37607                 if(!this.wasLeaf && this.node.leaf){
37608                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37609                     delete this.c1;
37610                     delete this.c2;
37611                     this.wasLeaf = true;
37612                 }
37613             }
37614             var ecc = "x-tree-ec-icon "+cls;
37615             if(this.ecc != ecc){
37616                 this.ecNode.className = ecc;
37617                 this.ecc = ecc;
37618             }
37619         }
37620     },
37621
37622     getChildIndent : function(){
37623         if(!this.childIndent){
37624             var buf = [];
37625             var p = this.node;
37626             while(p){
37627                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37628                     if(!p.isLast()) {
37629                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37630                     } else {
37631                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37632                     }
37633                 }
37634                 p = p.parentNode;
37635             }
37636             this.childIndent = buf.join("");
37637         }
37638         return this.childIndent;
37639     },
37640
37641     renderIndent : function(){
37642         if(this.rendered){
37643             var indent = "";
37644             var p = this.node.parentNode;
37645             if(p){
37646                 indent = p.ui.getChildIndent();
37647             }
37648             if(this.indentMarkup != indent){ // don't rerender if not required
37649                 this.indentNode.innerHTML = indent;
37650                 this.indentMarkup = indent;
37651             }
37652             this.updateExpandIcon();
37653         }
37654     }
37655 };
37656
37657 Roo.tree.RootTreeNodeUI = function(){
37658     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37659 };
37660 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37661     render : function(){
37662         if(!this.rendered){
37663             var targetNode = this.node.ownerTree.innerCt.dom;
37664             this.node.expanded = true;
37665             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37666             this.wrap = this.ctNode = targetNode.firstChild;
37667         }
37668     },
37669     collapse : function(){
37670     },
37671     expand : function(){
37672     }
37673 });/*
37674  * Based on:
37675  * Ext JS Library 1.1.1
37676  * Copyright(c) 2006-2007, Ext JS, LLC.
37677  *
37678  * Originally Released Under LGPL - original licence link has changed is not relivant.
37679  *
37680  * Fork - LGPL
37681  * <script type="text/javascript">
37682  */
37683 /**
37684  * @class Roo.tree.TreeLoader
37685  * @extends Roo.util.Observable
37686  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37687  * nodes from a specified URL. The response must be a javascript Array definition
37688  * who's elements are node definition objects. eg:
37689  * <pre><code>
37690 {  success : true,
37691    data :      [
37692    
37693     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37694     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37695     ]
37696 }
37697
37698
37699 </code></pre>
37700  * <br><br>
37701  * The old style respose with just an array is still supported, but not recommended.
37702  * <br><br>
37703  *
37704  * A server request is sent, and child nodes are loaded only when a node is expanded.
37705  * The loading node's id is passed to the server under the parameter name "node" to
37706  * enable the server to produce the correct child nodes.
37707  * <br><br>
37708  * To pass extra parameters, an event handler may be attached to the "beforeload"
37709  * event, and the parameters specified in the TreeLoader's baseParams property:
37710  * <pre><code>
37711     myTreeLoader.on("beforeload", function(treeLoader, node) {
37712         this.baseParams.category = node.attributes.category;
37713     }, this);
37714     
37715 </code></pre>
37716  *
37717  * This would pass an HTTP parameter called "category" to the server containing
37718  * the value of the Node's "category" attribute.
37719  * @constructor
37720  * Creates a new Treeloader.
37721  * @param {Object} config A config object containing config properties.
37722  */
37723 Roo.tree.TreeLoader = function(config){
37724     this.baseParams = {};
37725     this.requestMethod = "POST";
37726     Roo.apply(this, config);
37727
37728     this.addEvents({
37729     
37730         /**
37731          * @event beforeload
37732          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37733          * @param {Object} This TreeLoader object.
37734          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37735          * @param {Object} callback The callback function specified in the {@link #load} call.
37736          */
37737         beforeload : true,
37738         /**
37739          * @event load
37740          * Fires when the node has been successfuly loaded.
37741          * @param {Object} This TreeLoader object.
37742          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37743          * @param {Object} response The response object containing the data from the server.
37744          */
37745         load : true,
37746         /**
37747          * @event loadexception
37748          * Fires if the network request failed.
37749          * @param {Object} This TreeLoader object.
37750          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37751          * @param {Object} response The response object containing the data from the server.
37752          */
37753         loadexception : true,
37754         /**
37755          * @event create
37756          * Fires before a node is created, enabling you to return custom Node types 
37757          * @param {Object} This TreeLoader object.
37758          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37759          */
37760         create : true
37761     });
37762
37763     Roo.tree.TreeLoader.superclass.constructor.call(this);
37764 };
37765
37766 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37767     /**
37768     * @cfg {String} dataUrl The URL from which to request a Json string which
37769     * specifies an array of node definition object representing the child nodes
37770     * to be loaded.
37771     */
37772     /**
37773     * @cfg {String} requestMethod either GET or POST
37774     * defaults to POST (due to BC)
37775     * to be loaded.
37776     */
37777     /**
37778     * @cfg {Object} baseParams (optional) An object containing properties which
37779     * specify HTTP parameters to be passed to each request for child nodes.
37780     */
37781     /**
37782     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37783     * created by this loader. If the attributes sent by the server have an attribute in this object,
37784     * they take priority.
37785     */
37786     /**
37787     * @cfg {Object} uiProviders (optional) An object containing properties which
37788     * 
37789     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37790     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37791     * <i>uiProvider</i> attribute of a returned child node is a string rather
37792     * than a reference to a TreeNodeUI implementation, this that string value
37793     * is used as a property name in the uiProviders object. You can define the provider named
37794     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37795     */
37796     uiProviders : {},
37797
37798     /**
37799     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37800     * child nodes before loading.
37801     */
37802     clearOnLoad : true,
37803
37804     /**
37805     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37806     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37807     * Grid query { data : [ .....] }
37808     */
37809     
37810     root : false,
37811      /**
37812     * @cfg {String} queryParam (optional) 
37813     * Name of the query as it will be passed on the querystring (defaults to 'node')
37814     * eg. the request will be ?node=[id]
37815     */
37816     
37817     
37818     queryParam: false,
37819     
37820     /**
37821      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37822      * This is called automatically when a node is expanded, but may be used to reload
37823      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37824      * @param {Roo.tree.TreeNode} node
37825      * @param {Function} callback
37826      */
37827     load : function(node, callback){
37828         if(this.clearOnLoad){
37829             while(node.firstChild){
37830                 node.removeChild(node.firstChild);
37831             }
37832         }
37833         if(node.attributes.children){ // preloaded json children
37834             var cs = node.attributes.children;
37835             for(var i = 0, len = cs.length; i < len; i++){
37836                 node.appendChild(this.createNode(cs[i]));
37837             }
37838             if(typeof callback == "function"){
37839                 callback();
37840             }
37841         }else if(this.dataUrl){
37842             this.requestData(node, callback);
37843         }
37844     },
37845
37846     getParams: function(node){
37847         var buf = [], bp = this.baseParams;
37848         for(var key in bp){
37849             if(typeof bp[key] != "function"){
37850                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37851             }
37852         }
37853         var n = this.queryParam === false ? 'node' : this.queryParam;
37854         buf.push(n + "=", encodeURIComponent(node.id));
37855         return buf.join("");
37856     },
37857
37858     requestData : function(node, callback){
37859         if(this.fireEvent("beforeload", this, node, callback) !== false){
37860             this.transId = Roo.Ajax.request({
37861                 method:this.requestMethod,
37862                 url: this.dataUrl||this.url,
37863                 success: this.handleResponse,
37864                 failure: this.handleFailure,
37865                 scope: this,
37866                 argument: {callback: callback, node: node},
37867                 params: this.getParams(node)
37868             });
37869         }else{
37870             // if the load is cancelled, make sure we notify
37871             // the node that we are done
37872             if(typeof callback == "function"){
37873                 callback();
37874             }
37875         }
37876     },
37877
37878     isLoading : function(){
37879         return this.transId ? true : false;
37880     },
37881
37882     abort : function(){
37883         if(this.isLoading()){
37884             Roo.Ajax.abort(this.transId);
37885         }
37886     },
37887
37888     // private
37889     createNode : function(attr)
37890     {
37891         // apply baseAttrs, nice idea Corey!
37892         if(this.baseAttrs){
37893             Roo.applyIf(attr, this.baseAttrs);
37894         }
37895         if(this.applyLoader !== false){
37896             attr.loader = this;
37897         }
37898         // uiProvider = depreciated..
37899         
37900         if(typeof(attr.uiProvider) == 'string'){
37901            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37902                 /**  eval:var:attr */ eval(attr.uiProvider);
37903         }
37904         if(typeof(this.uiProviders['default']) != 'undefined') {
37905             attr.uiProvider = this.uiProviders['default'];
37906         }
37907         
37908         this.fireEvent('create', this, attr);
37909         
37910         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37911         return(attr.leaf ?
37912                         new Roo.tree.TreeNode(attr) :
37913                         new Roo.tree.AsyncTreeNode(attr));
37914     },
37915
37916     processResponse : function(response, node, callback)
37917     {
37918         var json = response.responseText;
37919         try {
37920             
37921             var o = Roo.decode(json);
37922             
37923             if (this.root === false && typeof(o.success) != undefined) {
37924                 this.root = 'data'; // the default behaviour for list like data..
37925                 }
37926                 
37927             if (this.root !== false &&  !o.success) {
37928                 // it's a failure condition.
37929                 var a = response.argument;
37930                 this.fireEvent("loadexception", this, a.node, response);
37931                 Roo.log("Load failed - should have a handler really");
37932                 return;
37933             }
37934             
37935             
37936             
37937             if (this.root !== false) {
37938                  o = o[this.root];
37939             }
37940             
37941             for(var i = 0, len = o.length; i < len; i++){
37942                 var n = this.createNode(o[i]);
37943                 if(n){
37944                     node.appendChild(n);
37945                 }
37946             }
37947             if(typeof callback == "function"){
37948                 callback(this, node);
37949             }
37950         }catch(e){
37951             this.handleFailure(response);
37952         }
37953     },
37954
37955     handleResponse : function(response){
37956         this.transId = false;
37957         var a = response.argument;
37958         this.processResponse(response, a.node, a.callback);
37959         this.fireEvent("load", this, a.node, response);
37960     },
37961
37962     handleFailure : function(response)
37963     {
37964         // should handle failure better..
37965         this.transId = false;
37966         var a = response.argument;
37967         this.fireEvent("loadexception", this, a.node, response);
37968         if(typeof a.callback == "function"){
37969             a.callback(this, a.node);
37970         }
37971     }
37972 });/*
37973  * Based on:
37974  * Ext JS Library 1.1.1
37975  * Copyright(c) 2006-2007, Ext JS, LLC.
37976  *
37977  * Originally Released Under LGPL - original licence link has changed is not relivant.
37978  *
37979  * Fork - LGPL
37980  * <script type="text/javascript">
37981  */
37982
37983 /**
37984 * @class Roo.tree.TreeFilter
37985 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37986 * @param {TreePanel} tree
37987 * @param {Object} config (optional)
37988  */
37989 Roo.tree.TreeFilter = function(tree, config){
37990     this.tree = tree;
37991     this.filtered = {};
37992     Roo.apply(this, config);
37993 };
37994
37995 Roo.tree.TreeFilter.prototype = {
37996     clearBlank:false,
37997     reverse:false,
37998     autoClear:false,
37999     remove:false,
38000
38001      /**
38002      * Filter the data by a specific attribute.
38003      * @param {String/RegExp} value Either string that the attribute value
38004      * should start with or a RegExp to test against the attribute
38005      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
38006      * @param {TreeNode} startNode (optional) The node to start the filter at.
38007      */
38008     filter : function(value, attr, startNode){
38009         attr = attr || "text";
38010         var f;
38011         if(typeof value == "string"){
38012             var vlen = value.length;
38013             // auto clear empty filter
38014             if(vlen == 0 && this.clearBlank){
38015                 this.clear();
38016                 return;
38017             }
38018             value = value.toLowerCase();
38019             f = function(n){
38020                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
38021             };
38022         }else if(value.exec){ // regex?
38023             f = function(n){
38024                 return value.test(n.attributes[attr]);
38025             };
38026         }else{
38027             throw 'Illegal filter type, must be string or regex';
38028         }
38029         this.filterBy(f, null, startNode);
38030         },
38031
38032     /**
38033      * Filter by a function. The passed function will be called with each
38034      * node in the tree (or from the startNode). If the function returns true, the node is kept
38035      * otherwise it is filtered. If a node is filtered, its children are also filtered.
38036      * @param {Function} fn The filter function
38037      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
38038      */
38039     filterBy : function(fn, scope, startNode){
38040         startNode = startNode || this.tree.root;
38041         if(this.autoClear){
38042             this.clear();
38043         }
38044         var af = this.filtered, rv = this.reverse;
38045         var f = function(n){
38046             if(n == startNode){
38047                 return true;
38048             }
38049             if(af[n.id]){
38050                 return false;
38051             }
38052             var m = fn.call(scope || n, n);
38053             if(!m || rv){
38054                 af[n.id] = n;
38055                 n.ui.hide();
38056                 return false;
38057             }
38058             return true;
38059         };
38060         startNode.cascade(f);
38061         if(this.remove){
38062            for(var id in af){
38063                if(typeof id != "function"){
38064                    var n = af[id];
38065                    if(n && n.parentNode){
38066                        n.parentNode.removeChild(n);
38067                    }
38068                }
38069            }
38070         }
38071     },
38072
38073     /**
38074      * Clears the current filter. Note: with the "remove" option
38075      * set a filter cannot be cleared.
38076      */
38077     clear : function(){
38078         var t = this.tree;
38079         var af = this.filtered;
38080         for(var id in af){
38081             if(typeof id != "function"){
38082                 var n = af[id];
38083                 if(n){
38084                     n.ui.show();
38085                 }
38086             }
38087         }
38088         this.filtered = {};
38089     }
38090 };
38091 /*
38092  * Based on:
38093  * Ext JS Library 1.1.1
38094  * Copyright(c) 2006-2007, Ext JS, LLC.
38095  *
38096  * Originally Released Under LGPL - original licence link has changed is not relivant.
38097  *
38098  * Fork - LGPL
38099  * <script type="text/javascript">
38100  */
38101  
38102
38103 /**
38104  * @class Roo.tree.TreeSorter
38105  * Provides sorting of nodes in a TreePanel
38106  * 
38107  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
38108  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
38109  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
38110  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
38111  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
38112  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
38113  * @constructor
38114  * @param {TreePanel} tree
38115  * @param {Object} config
38116  */
38117 Roo.tree.TreeSorter = function(tree, config){
38118     Roo.apply(this, config);
38119     tree.on("beforechildrenrendered", this.doSort, this);
38120     tree.on("append", this.updateSort, this);
38121     tree.on("insert", this.updateSort, this);
38122     
38123     var dsc = this.dir && this.dir.toLowerCase() == "desc";
38124     var p = this.property || "text";
38125     var sortType = this.sortType;
38126     var fs = this.folderSort;
38127     var cs = this.caseSensitive === true;
38128     var leafAttr = this.leafAttr || 'leaf';
38129
38130     this.sortFn = function(n1, n2){
38131         if(fs){
38132             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
38133                 return 1;
38134             }
38135             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
38136                 return -1;
38137             }
38138         }
38139         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
38140         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
38141         if(v1 < v2){
38142                         return dsc ? +1 : -1;
38143                 }else if(v1 > v2){
38144                         return dsc ? -1 : +1;
38145         }else{
38146                 return 0;
38147         }
38148     };
38149 };
38150
38151 Roo.tree.TreeSorter.prototype = {
38152     doSort : function(node){
38153         node.sort(this.sortFn);
38154     },
38155     
38156     compareNodes : function(n1, n2){
38157         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
38158     },
38159     
38160     updateSort : function(tree, node){
38161         if(node.childrenRendered){
38162             this.doSort.defer(1, this, [node]);
38163         }
38164     }
38165 };/*
38166  * Based on:
38167  * Ext JS Library 1.1.1
38168  * Copyright(c) 2006-2007, Ext JS, LLC.
38169  *
38170  * Originally Released Under LGPL - original licence link has changed is not relivant.
38171  *
38172  * Fork - LGPL
38173  * <script type="text/javascript">
38174  */
38175
38176 if(Roo.dd.DropZone){
38177     
38178 Roo.tree.TreeDropZone = function(tree, config){
38179     this.allowParentInsert = false;
38180     this.allowContainerDrop = false;
38181     this.appendOnly = false;
38182     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38183     this.tree = tree;
38184     this.lastInsertClass = "x-tree-no-status";
38185     this.dragOverData = {};
38186 };
38187
38188 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38189     ddGroup : "TreeDD",
38190     scroll:  true,
38191     
38192     expandDelay : 1000,
38193     
38194     expandNode : function(node){
38195         if(node.hasChildNodes() && !node.isExpanded()){
38196             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38197         }
38198     },
38199     
38200     queueExpand : function(node){
38201         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38202     },
38203     
38204     cancelExpand : function(){
38205         if(this.expandProcId){
38206             clearTimeout(this.expandProcId);
38207             this.expandProcId = false;
38208         }
38209     },
38210     
38211     isValidDropPoint : function(n, pt, dd, e, data){
38212         if(!n || !data){ return false; }
38213         var targetNode = n.node;
38214         var dropNode = data.node;
38215         // default drop rules
38216         if(!(targetNode && targetNode.isTarget && pt)){
38217             return false;
38218         }
38219         if(pt == "append" && targetNode.allowChildren === false){
38220             return false;
38221         }
38222         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38223             return false;
38224         }
38225         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38226             return false;
38227         }
38228         // reuse the object
38229         var overEvent = this.dragOverData;
38230         overEvent.tree = this.tree;
38231         overEvent.target = targetNode;
38232         overEvent.data = data;
38233         overEvent.point = pt;
38234         overEvent.source = dd;
38235         overEvent.rawEvent = e;
38236         overEvent.dropNode = dropNode;
38237         overEvent.cancel = false;  
38238         var result = this.tree.fireEvent("nodedragover", overEvent);
38239         return overEvent.cancel === false && result !== false;
38240     },
38241     
38242     getDropPoint : function(e, n, dd)
38243     {
38244         var tn = n.node;
38245         if(tn.isRoot){
38246             return tn.allowChildren !== false ? "append" : false; // always append for root
38247         }
38248         var dragEl = n.ddel;
38249         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38250         var y = Roo.lib.Event.getPageY(e);
38251         //var noAppend = tn.allowChildren === false || tn.isLeaf();
38252         
38253         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38254         var noAppend = tn.allowChildren === false;
38255         if(this.appendOnly || tn.parentNode.allowChildren === false){
38256             return noAppend ? false : "append";
38257         }
38258         var noBelow = false;
38259         if(!this.allowParentInsert){
38260             noBelow = tn.hasChildNodes() && tn.isExpanded();
38261         }
38262         var q = (b - t) / (noAppend ? 2 : 3);
38263         if(y >= t && y < (t + q)){
38264             return "above";
38265         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38266             return "below";
38267         }else{
38268             return "append";
38269         }
38270     },
38271     
38272     onNodeEnter : function(n, dd, e, data)
38273     {
38274         this.cancelExpand();
38275     },
38276     
38277     onNodeOver : function(n, dd, e, data)
38278     {
38279        
38280         var pt = this.getDropPoint(e, n, dd);
38281         var node = n.node;
38282         
38283         // auto node expand check
38284         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38285             this.queueExpand(node);
38286         }else if(pt != "append"){
38287             this.cancelExpand();
38288         }
38289         
38290         // set the insert point style on the target node
38291         var returnCls = this.dropNotAllowed;
38292         if(this.isValidDropPoint(n, pt, dd, e, data)){
38293            if(pt){
38294                var el = n.ddel;
38295                var cls;
38296                if(pt == "above"){
38297                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38298                    cls = "x-tree-drag-insert-above";
38299                }else if(pt == "below"){
38300                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38301                    cls = "x-tree-drag-insert-below";
38302                }else{
38303                    returnCls = "x-tree-drop-ok-append";
38304                    cls = "x-tree-drag-append";
38305                }
38306                if(this.lastInsertClass != cls){
38307                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38308                    this.lastInsertClass = cls;
38309                }
38310            }
38311        }
38312        return returnCls;
38313     },
38314     
38315     onNodeOut : function(n, dd, e, data){
38316         
38317         this.cancelExpand();
38318         this.removeDropIndicators(n);
38319     },
38320     
38321     onNodeDrop : function(n, dd, e, data){
38322         var point = this.getDropPoint(e, n, dd);
38323         var targetNode = n.node;
38324         targetNode.ui.startDrop();
38325         if(!this.isValidDropPoint(n, point, dd, e, data)){
38326             targetNode.ui.endDrop();
38327             return false;
38328         }
38329         // first try to find the drop node
38330         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38331         var dropEvent = {
38332             tree : this.tree,
38333             target: targetNode,
38334             data: data,
38335             point: point,
38336             source: dd,
38337             rawEvent: e,
38338             dropNode: dropNode,
38339             cancel: !dropNode   
38340         };
38341         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38342         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38343             targetNode.ui.endDrop();
38344             return false;
38345         }
38346         // allow target changing
38347         targetNode = dropEvent.target;
38348         if(point == "append" && !targetNode.isExpanded()){
38349             targetNode.expand(false, null, function(){
38350                 this.completeDrop(dropEvent);
38351             }.createDelegate(this));
38352         }else{
38353             this.completeDrop(dropEvent);
38354         }
38355         return true;
38356     },
38357     
38358     completeDrop : function(de){
38359         var ns = de.dropNode, p = de.point, t = de.target;
38360         if(!(ns instanceof Array)){
38361             ns = [ns];
38362         }
38363         var n;
38364         for(var i = 0, len = ns.length; i < len; i++){
38365             n = ns[i];
38366             if(p == "above"){
38367                 t.parentNode.insertBefore(n, t);
38368             }else if(p == "below"){
38369                 t.parentNode.insertBefore(n, t.nextSibling);
38370             }else{
38371                 t.appendChild(n);
38372             }
38373         }
38374         n.ui.focus();
38375         if(this.tree.hlDrop){
38376             n.ui.highlight();
38377         }
38378         t.ui.endDrop();
38379         this.tree.fireEvent("nodedrop", de);
38380     },
38381     
38382     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38383         if(this.tree.hlDrop){
38384             dropNode.ui.focus();
38385             dropNode.ui.highlight();
38386         }
38387         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38388     },
38389     
38390     getTree : function(){
38391         return this.tree;
38392     },
38393     
38394     removeDropIndicators : function(n){
38395         if(n && n.ddel){
38396             var el = n.ddel;
38397             Roo.fly(el).removeClass([
38398                     "x-tree-drag-insert-above",
38399                     "x-tree-drag-insert-below",
38400                     "x-tree-drag-append"]);
38401             this.lastInsertClass = "_noclass";
38402         }
38403     },
38404     
38405     beforeDragDrop : function(target, e, id){
38406         this.cancelExpand();
38407         return true;
38408     },
38409     
38410     afterRepair : function(data){
38411         if(data && Roo.enableFx){
38412             data.node.ui.highlight();
38413         }
38414         this.hideProxy();
38415     } 
38416     
38417 });
38418
38419 }
38420 /*
38421  * Based on:
38422  * Ext JS Library 1.1.1
38423  * Copyright(c) 2006-2007, Ext JS, LLC.
38424  *
38425  * Originally Released Under LGPL - original licence link has changed is not relivant.
38426  *
38427  * Fork - LGPL
38428  * <script type="text/javascript">
38429  */
38430  
38431
38432 if(Roo.dd.DragZone){
38433 Roo.tree.TreeDragZone = function(tree, config){
38434     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38435     this.tree = tree;
38436 };
38437
38438 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38439     ddGroup : "TreeDD",
38440    
38441     onBeforeDrag : function(data, e){
38442         var n = data.node;
38443         return n && n.draggable && !n.disabled;
38444     },
38445      
38446     
38447     onInitDrag : function(e){
38448         var data = this.dragData;
38449         this.tree.getSelectionModel().select(data.node);
38450         this.proxy.update("");
38451         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38452         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38453     },
38454     
38455     getRepairXY : function(e, data){
38456         return data.node.ui.getDDRepairXY();
38457     },
38458     
38459     onEndDrag : function(data, e){
38460         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38461         
38462         
38463     },
38464     
38465     onValidDrop : function(dd, e, id){
38466         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38467         this.hideProxy();
38468     },
38469     
38470     beforeInvalidDrop : function(e, id){
38471         // this scrolls the original position back into view
38472         var sm = this.tree.getSelectionModel();
38473         sm.clearSelections();
38474         sm.select(this.dragData.node);
38475     }
38476 });
38477 }/*
38478  * Based on:
38479  * Ext JS Library 1.1.1
38480  * Copyright(c) 2006-2007, Ext JS, LLC.
38481  *
38482  * Originally Released Under LGPL - original licence link has changed is not relivant.
38483  *
38484  * Fork - LGPL
38485  * <script type="text/javascript">
38486  */
38487 /**
38488  * @class Roo.tree.TreeEditor
38489  * @extends Roo.Editor
38490  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38491  * as the editor field.
38492  * @constructor
38493  * @param {Object} config (used to be the tree panel.)
38494  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38495  * 
38496  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38497  * @cfg {Roo.form.TextField} field [required] The field configuration
38498  *
38499  * 
38500  */
38501 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38502     var tree = config;
38503     var field;
38504     if (oldconfig) { // old style..
38505         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38506     } else {
38507         // new style..
38508         tree = config.tree;
38509         config.field = config.field  || {};
38510         config.field.xtype = 'TextField';
38511         field = Roo.factory(config.field, Roo.form);
38512     }
38513     config = config || {};
38514     
38515     
38516     this.addEvents({
38517         /**
38518          * @event beforenodeedit
38519          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38520          * false from the handler of this event.
38521          * @param {Editor} this
38522          * @param {Roo.tree.Node} node 
38523          */
38524         "beforenodeedit" : true
38525     });
38526     
38527     //Roo.log(config);
38528     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38529
38530     this.tree = tree;
38531
38532     tree.on('beforeclick', this.beforeNodeClick, this);
38533     tree.getTreeEl().on('mousedown', this.hide, this);
38534     this.on('complete', this.updateNode, this);
38535     this.on('beforestartedit', this.fitToTree, this);
38536     this.on('startedit', this.bindScroll, this, {delay:10});
38537     this.on('specialkey', this.onSpecialKey, this);
38538 };
38539
38540 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38541     /**
38542      * @cfg {String} alignment
38543      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38544      */
38545     alignment: "l-l",
38546     // inherit
38547     autoSize: false,
38548     /**
38549      * @cfg {Boolean} hideEl
38550      * True to hide the bound element while the editor is displayed (defaults to false)
38551      */
38552     hideEl : false,
38553     /**
38554      * @cfg {String} cls
38555      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38556      */
38557     cls: "x-small-editor x-tree-editor",
38558     /**
38559      * @cfg {Boolean} shim
38560      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38561      */
38562     shim:false,
38563     // inherit
38564     shadow:"frame",
38565     /**
38566      * @cfg {Number} maxWidth
38567      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38568      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38569      * scroll and client offsets into account prior to each edit.
38570      */
38571     maxWidth: 250,
38572
38573     editDelay : 350,
38574
38575     // private
38576     fitToTree : function(ed, el){
38577         var td = this.tree.getTreeEl().dom, nd = el.dom;
38578         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38579             td.scrollLeft = nd.offsetLeft;
38580         }
38581         var w = Math.min(
38582                 this.maxWidth,
38583                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38584         this.setSize(w, '');
38585         
38586         return this.fireEvent('beforenodeedit', this, this.editNode);
38587         
38588     },
38589
38590     // private
38591     triggerEdit : function(node){
38592         this.completeEdit();
38593         this.editNode = node;
38594         this.startEdit(node.ui.textNode, node.text);
38595     },
38596
38597     // private
38598     bindScroll : function(){
38599         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38600     },
38601
38602     // private
38603     beforeNodeClick : function(node, e){
38604         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38605         this.lastClick = new Date();
38606         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38607             e.stopEvent();
38608             this.triggerEdit(node);
38609             return false;
38610         }
38611         return true;
38612     },
38613
38614     // private
38615     updateNode : function(ed, value){
38616         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38617         this.editNode.setText(value);
38618     },
38619
38620     // private
38621     onHide : function(){
38622         Roo.tree.TreeEditor.superclass.onHide.call(this);
38623         if(this.editNode){
38624             this.editNode.ui.focus();
38625         }
38626     },
38627
38628     // private
38629     onSpecialKey : function(field, e){
38630         var k = e.getKey();
38631         if(k == e.ESC){
38632             e.stopEvent();
38633             this.cancelEdit();
38634         }else if(k == e.ENTER && !e.hasModifier()){
38635             e.stopEvent();
38636             this.completeEdit();
38637         }
38638     }
38639 });//<Script type="text/javascript">
38640 /*
38641  * Based on:
38642  * Ext JS Library 1.1.1
38643  * Copyright(c) 2006-2007, Ext JS, LLC.
38644  *
38645  * Originally Released Under LGPL - original licence link has changed is not relivant.
38646  *
38647  * Fork - LGPL
38648  * <script type="text/javascript">
38649  */
38650  
38651 /**
38652  * Not documented??? - probably should be...
38653  */
38654
38655 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38656     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38657     
38658     renderElements : function(n, a, targetNode, bulkRender){
38659         //consel.log("renderElements?");
38660         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38661
38662         var t = n.getOwnerTree();
38663         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38664         
38665         var cols = t.columns;
38666         var bw = t.borderWidth;
38667         var c = cols[0];
38668         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38669          var cb = typeof a.checked == "boolean";
38670         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38671         var colcls = 'x-t-' + tid + '-c0';
38672         var buf = [
38673             '<li class="x-tree-node">',
38674             
38675                 
38676                 '<div class="x-tree-node-el ', a.cls,'">',
38677                     // extran...
38678                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38679                 
38680                 
38681                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38682                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38683                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38684                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38685                            (a.iconCls ? ' '+a.iconCls : ''),
38686                            '" unselectable="on" />',
38687                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38688                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38689                              
38690                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38691                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38692                             '<span unselectable="on" qtip="' + tx + '">',
38693                              tx,
38694                              '</span></a>' ,
38695                     '</div>',
38696                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38697                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38698                  ];
38699         for(var i = 1, len = cols.length; i < len; i++){
38700             c = cols[i];
38701             colcls = 'x-t-' + tid + '-c' +i;
38702             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38703             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38704                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38705                       "</div>");
38706          }
38707          
38708          buf.push(
38709             '</a>',
38710             '<div class="x-clear"></div></div>',
38711             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38712             "</li>");
38713         
38714         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38715             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38716                                 n.nextSibling.ui.getEl(), buf.join(""));
38717         }else{
38718             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38719         }
38720         var el = this.wrap.firstChild;
38721         this.elRow = el;
38722         this.elNode = el.firstChild;
38723         this.ranchor = el.childNodes[1];
38724         this.ctNode = this.wrap.childNodes[1];
38725         var cs = el.firstChild.childNodes;
38726         this.indentNode = cs[0];
38727         this.ecNode = cs[1];
38728         this.iconNode = cs[2];
38729         var index = 3;
38730         if(cb){
38731             this.checkbox = cs[3];
38732             index++;
38733         }
38734         this.anchor = cs[index];
38735         
38736         this.textNode = cs[index].firstChild;
38737         
38738         //el.on("click", this.onClick, this);
38739         //el.on("dblclick", this.onDblClick, this);
38740         
38741         
38742        // console.log(this);
38743     },
38744     initEvents : function(){
38745         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38746         
38747             
38748         var a = this.ranchor;
38749
38750         var el = Roo.get(a);
38751
38752         if(Roo.isOpera){ // opera render bug ignores the CSS
38753             el.setStyle("text-decoration", "none");
38754         }
38755
38756         el.on("click", this.onClick, this);
38757         el.on("dblclick", this.onDblClick, this);
38758         el.on("contextmenu", this.onContextMenu, this);
38759         
38760     },
38761     
38762     /*onSelectedChange : function(state){
38763         if(state){
38764             this.focus();
38765             this.addClass("x-tree-selected");
38766         }else{
38767             //this.blur();
38768             this.removeClass("x-tree-selected");
38769         }
38770     },*/
38771     addClass : function(cls){
38772         if(this.elRow){
38773             Roo.fly(this.elRow).addClass(cls);
38774         }
38775         
38776     },
38777     
38778     
38779     removeClass : function(cls){
38780         if(this.elRow){
38781             Roo.fly(this.elRow).removeClass(cls);
38782         }
38783     }
38784
38785     
38786     
38787 });//<Script type="text/javascript">
38788
38789 /*
38790  * Based on:
38791  * Ext JS Library 1.1.1
38792  * Copyright(c) 2006-2007, Ext JS, LLC.
38793  *
38794  * Originally Released Under LGPL - original licence link has changed is not relivant.
38795  *
38796  * Fork - LGPL
38797  * <script type="text/javascript">
38798  */
38799  
38800
38801 /**
38802  * @class Roo.tree.ColumnTree
38803  * @extends Roo.tree.TreePanel
38804  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38805  * @cfg {int} borderWidth  compined right/left border allowance
38806  * @constructor
38807  * @param {String/HTMLElement/Element} el The container element
38808  * @param {Object} config
38809  */
38810 Roo.tree.ColumnTree =  function(el, config)
38811 {
38812    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38813    this.addEvents({
38814         /**
38815         * @event resize
38816         * Fire this event on a container when it resizes
38817         * @param {int} w Width
38818         * @param {int} h Height
38819         */
38820        "resize" : true
38821     });
38822     this.on('resize', this.onResize, this);
38823 };
38824
38825 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38826     //lines:false,
38827     
38828     
38829     borderWidth: Roo.isBorderBox ? 0 : 2, 
38830     headEls : false,
38831     
38832     render : function(){
38833         // add the header.....
38834        
38835         Roo.tree.ColumnTree.superclass.render.apply(this);
38836         
38837         this.el.addClass('x-column-tree');
38838         
38839         this.headers = this.el.createChild(
38840             {cls:'x-tree-headers'},this.innerCt.dom);
38841    
38842         var cols = this.columns, c;
38843         var totalWidth = 0;
38844         this.headEls = [];
38845         var  len = cols.length;
38846         for(var i = 0; i < len; i++){
38847              c = cols[i];
38848              totalWidth += c.width;
38849             this.headEls.push(this.headers.createChild({
38850                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38851                  cn: {
38852                      cls:'x-tree-hd-text',
38853                      html: c.header
38854                  },
38855                  style:'width:'+(c.width-this.borderWidth)+'px;'
38856              }));
38857         }
38858         this.headers.createChild({cls:'x-clear'});
38859         // prevent floats from wrapping when clipped
38860         this.headers.setWidth(totalWidth);
38861         //this.innerCt.setWidth(totalWidth);
38862         this.innerCt.setStyle({ overflow: 'auto' });
38863         this.onResize(this.width, this.height);
38864              
38865         
38866     },
38867     onResize : function(w,h)
38868     {
38869         this.height = h;
38870         this.width = w;
38871         // resize cols..
38872         this.innerCt.setWidth(this.width);
38873         this.innerCt.setHeight(this.height-20);
38874         
38875         // headers...
38876         var cols = this.columns, c;
38877         var totalWidth = 0;
38878         var expEl = false;
38879         var len = cols.length;
38880         for(var i = 0; i < len; i++){
38881             c = cols[i];
38882             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38883                 // it's the expander..
38884                 expEl  = this.headEls[i];
38885                 continue;
38886             }
38887             totalWidth += c.width;
38888             
38889         }
38890         if (expEl) {
38891             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38892         }
38893         this.headers.setWidth(w-20);
38894
38895         
38896         
38897         
38898     }
38899 });
38900 /*
38901  * Based on:
38902  * Ext JS Library 1.1.1
38903  * Copyright(c) 2006-2007, Ext JS, LLC.
38904  *
38905  * Originally Released Under LGPL - original licence link has changed is not relivant.
38906  *
38907  * Fork - LGPL
38908  * <script type="text/javascript">
38909  */
38910  
38911 /**
38912  * @class Roo.menu.Menu
38913  * @extends Roo.util.Observable
38914  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38915  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38916  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38917  * @constructor
38918  * Creates a new Menu
38919  * @param {Object} config Configuration options
38920  */
38921 Roo.menu.Menu = function(config){
38922     
38923     Roo.menu.Menu.superclass.constructor.call(this, config);
38924     
38925     this.id = this.id || Roo.id();
38926     this.addEvents({
38927         /**
38928          * @event beforeshow
38929          * Fires before this menu is displayed
38930          * @param {Roo.menu.Menu} this
38931          */
38932         beforeshow : true,
38933         /**
38934          * @event beforehide
38935          * Fires before this menu is hidden
38936          * @param {Roo.menu.Menu} this
38937          */
38938         beforehide : true,
38939         /**
38940          * @event show
38941          * Fires after this menu is displayed
38942          * @param {Roo.menu.Menu} this
38943          */
38944         show : true,
38945         /**
38946          * @event hide
38947          * Fires after this menu is hidden
38948          * @param {Roo.menu.Menu} this
38949          */
38950         hide : true,
38951         /**
38952          * @event click
38953          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38954          * @param {Roo.menu.Menu} this
38955          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38956          * @param {Roo.EventObject} e
38957          */
38958         click : true,
38959         /**
38960          * @event mouseover
38961          * Fires when the mouse is hovering over this menu
38962          * @param {Roo.menu.Menu} this
38963          * @param {Roo.EventObject} e
38964          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38965          */
38966         mouseover : true,
38967         /**
38968          * @event mouseout
38969          * Fires when the mouse exits this menu
38970          * @param {Roo.menu.Menu} this
38971          * @param {Roo.EventObject} e
38972          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38973          */
38974         mouseout : true,
38975         /**
38976          * @event itemclick
38977          * Fires when a menu item contained in this menu is clicked
38978          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38979          * @param {Roo.EventObject} e
38980          */
38981         itemclick: true
38982     });
38983     if (this.registerMenu) {
38984         Roo.menu.MenuMgr.register(this);
38985     }
38986     
38987     var mis = this.items;
38988     this.items = new Roo.util.MixedCollection();
38989     if(mis){
38990         this.add.apply(this, mis);
38991     }
38992 };
38993
38994 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38995     /**
38996      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38997      */
38998     minWidth : 120,
38999     /**
39000      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
39001      * for bottom-right shadow (defaults to "sides")
39002      */
39003     shadow : "sides",
39004     /**
39005      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
39006      * this menu (defaults to "tl-tr?")
39007      */
39008     subMenuAlign : "tl-tr?",
39009     /**
39010      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
39011      * relative to its element of origin (defaults to "tl-bl?")
39012      */
39013     defaultAlign : "tl-bl?",
39014     /**
39015      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
39016      */
39017     allowOtherMenus : false,
39018     /**
39019      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
39020      */
39021     registerMenu : true,
39022
39023     hidden:true,
39024
39025     // private
39026     render : function(){
39027         if(this.el){
39028             return;
39029         }
39030         var el = this.el = new Roo.Layer({
39031             cls: "x-menu",
39032             shadow:this.shadow,
39033             constrain: false,
39034             parentEl: this.parentEl || document.body,
39035             zindex:15000
39036         });
39037
39038         this.keyNav = new Roo.menu.MenuNav(this);
39039
39040         if(this.plain){
39041             el.addClass("x-menu-plain");
39042         }
39043         if(this.cls){
39044             el.addClass(this.cls);
39045         }
39046         // generic focus element
39047         this.focusEl = el.createChild({
39048             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
39049         });
39050         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
39051         //disabling touch- as it's causing issues ..
39052         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
39053         ul.on('click'   , this.onClick, this);
39054         
39055         
39056         ul.on("mouseover", this.onMouseOver, this);
39057         ul.on("mouseout", this.onMouseOut, this);
39058         this.items.each(function(item){
39059             if (item.hidden) {
39060                 return;
39061             }
39062             
39063             var li = document.createElement("li");
39064             li.className = "x-menu-list-item";
39065             ul.dom.appendChild(li);
39066             item.render(li, this);
39067         }, this);
39068         this.ul = ul;
39069         this.autoWidth();
39070     },
39071
39072     // private
39073     autoWidth : function(){
39074         var el = this.el, ul = this.ul;
39075         if(!el){
39076             return;
39077         }
39078         var w = this.width;
39079         if(w){
39080             el.setWidth(w);
39081         }else if(Roo.isIE){
39082             el.setWidth(this.minWidth);
39083             var t = el.dom.offsetWidth; // force recalc
39084             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
39085         }
39086     },
39087
39088     // private
39089     delayAutoWidth : function(){
39090         if(this.rendered){
39091             if(!this.awTask){
39092                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
39093             }
39094             this.awTask.delay(20);
39095         }
39096     },
39097
39098     // private
39099     findTargetItem : function(e){
39100         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
39101         if(t && t.menuItemId){
39102             return this.items.get(t.menuItemId);
39103         }
39104     },
39105
39106     // private
39107     onClick : function(e){
39108         Roo.log("menu.onClick");
39109         var t = this.findTargetItem(e);
39110         if(!t){
39111             return;
39112         }
39113         Roo.log(e);
39114         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
39115             if(t == this.activeItem && t.shouldDeactivate(e)){
39116                 this.activeItem.deactivate();
39117                 delete this.activeItem;
39118                 return;
39119             }
39120             if(t.canActivate){
39121                 this.setActiveItem(t, true);
39122             }
39123             return;
39124             
39125             
39126         }
39127         
39128         t.onClick(e);
39129         this.fireEvent("click", this, t, e);
39130     },
39131
39132     // private
39133     setActiveItem : function(item, autoExpand){
39134         if(item != this.activeItem){
39135             if(this.activeItem){
39136                 this.activeItem.deactivate();
39137             }
39138             this.activeItem = item;
39139             item.activate(autoExpand);
39140         }else if(autoExpand){
39141             item.expandMenu();
39142         }
39143     },
39144
39145     // private
39146     tryActivate : function(start, step){
39147         var items = this.items;
39148         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
39149             var item = items.get(i);
39150             if(!item.disabled && item.canActivate){
39151                 this.setActiveItem(item, false);
39152                 return item;
39153             }
39154         }
39155         return false;
39156     },
39157
39158     // private
39159     onMouseOver : function(e){
39160         var t;
39161         if(t = this.findTargetItem(e)){
39162             if(t.canActivate && !t.disabled){
39163                 this.setActiveItem(t, true);
39164             }
39165         }
39166         this.fireEvent("mouseover", this, e, t);
39167     },
39168
39169     // private
39170     onMouseOut : function(e){
39171         var t;
39172         if(t = this.findTargetItem(e)){
39173             if(t == this.activeItem && t.shouldDeactivate(e)){
39174                 this.activeItem.deactivate();
39175                 delete this.activeItem;
39176             }
39177         }
39178         this.fireEvent("mouseout", this, e, t);
39179     },
39180
39181     /**
39182      * Read-only.  Returns true if the menu is currently displayed, else false.
39183      * @type Boolean
39184      */
39185     isVisible : function(){
39186         return this.el && !this.hidden;
39187     },
39188
39189     /**
39190      * Displays this menu relative to another element
39191      * @param {String/HTMLElement/Roo.Element} element The element to align to
39192      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39193      * the element (defaults to this.defaultAlign)
39194      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39195      */
39196     show : function(el, pos, parentMenu){
39197         this.parentMenu = parentMenu;
39198         if(!this.el){
39199             this.render();
39200         }
39201         this.fireEvent("beforeshow", this);
39202         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39203     },
39204
39205     /**
39206      * Displays this menu at a specific xy position
39207      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39208      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39209      */
39210     showAt : function(xy, parentMenu, /* private: */_e){
39211         this.parentMenu = parentMenu;
39212         if(!this.el){
39213             this.render();
39214         }
39215         if(_e !== false){
39216             this.fireEvent("beforeshow", this);
39217             xy = this.el.adjustForConstraints(xy);
39218         }
39219         this.el.setXY(xy);
39220         this.el.show();
39221         this.hidden = false;
39222         this.focus();
39223         this.fireEvent("show", this);
39224     },
39225
39226     focus : function(){
39227         if(!this.hidden){
39228             this.doFocus.defer(50, this);
39229         }
39230     },
39231
39232     doFocus : function(){
39233         if(!this.hidden){
39234             this.focusEl.focus();
39235         }
39236     },
39237
39238     /**
39239      * Hides this menu and optionally all parent menus
39240      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39241      */
39242     hide : function(deep){
39243         if(this.el && this.isVisible()){
39244             this.fireEvent("beforehide", this);
39245             if(this.activeItem){
39246                 this.activeItem.deactivate();
39247                 this.activeItem = null;
39248             }
39249             this.el.hide();
39250             this.hidden = true;
39251             this.fireEvent("hide", this);
39252         }
39253         if(deep === true && this.parentMenu){
39254             this.parentMenu.hide(true);
39255         }
39256     },
39257
39258     /**
39259      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39260      * Any of the following are valid:
39261      * <ul>
39262      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39263      * <li>An HTMLElement object which will be converted to a menu item</li>
39264      * <li>A menu item config object that will be created as a new menu item</li>
39265      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39266      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39267      * </ul>
39268      * Usage:
39269      * <pre><code>
39270 // Create the menu
39271 var menu = new Roo.menu.Menu();
39272
39273 // Create a menu item to add by reference
39274 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39275
39276 // Add a bunch of items at once using different methods.
39277 // Only the last item added will be returned.
39278 var item = menu.add(
39279     menuItem,                // add existing item by ref
39280     'Dynamic Item',          // new TextItem
39281     '-',                     // new separator
39282     { text: 'Config Item' }  // new item by config
39283 );
39284 </code></pre>
39285      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39286      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39287      */
39288     add : function(){
39289         var a = arguments, l = a.length, item;
39290         for(var i = 0; i < l; i++){
39291             var el = a[i];
39292             if ((typeof(el) == "object") && el.xtype && el.xns) {
39293                 el = Roo.factory(el, Roo.menu);
39294             }
39295             
39296             if(el.render){ // some kind of Item
39297                 item = this.addItem(el);
39298             }else if(typeof el == "string"){ // string
39299                 if(el == "separator" || el == "-"){
39300                     item = this.addSeparator();
39301                 }else{
39302                     item = this.addText(el);
39303                 }
39304             }else if(el.tagName || el.el){ // element
39305                 item = this.addElement(el);
39306             }else if(typeof el == "object"){ // must be menu item config?
39307                 item = this.addMenuItem(el);
39308             }
39309         }
39310         return item;
39311     },
39312
39313     /**
39314      * Returns this menu's underlying {@link Roo.Element} object
39315      * @return {Roo.Element} The element
39316      */
39317     getEl : function(){
39318         if(!this.el){
39319             this.render();
39320         }
39321         return this.el;
39322     },
39323
39324     /**
39325      * Adds a separator bar to the menu
39326      * @return {Roo.menu.Item} The menu item that was added
39327      */
39328     addSeparator : function(){
39329         return this.addItem(new Roo.menu.Separator());
39330     },
39331
39332     /**
39333      * Adds an {@link Roo.Element} object to the menu
39334      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39335      * @return {Roo.menu.Item} The menu item that was added
39336      */
39337     addElement : function(el){
39338         return this.addItem(new Roo.menu.BaseItem(el));
39339     },
39340
39341     /**
39342      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39343      * @param {Roo.menu.Item} item The menu item to add
39344      * @return {Roo.menu.Item} The menu item that was added
39345      */
39346     addItem : function(item){
39347         this.items.add(item);
39348         if(this.ul){
39349             var li = document.createElement("li");
39350             li.className = "x-menu-list-item";
39351             this.ul.dom.appendChild(li);
39352             item.render(li, this);
39353             this.delayAutoWidth();
39354         }
39355         return item;
39356     },
39357
39358     /**
39359      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39360      * @param {Object} config A MenuItem config object
39361      * @return {Roo.menu.Item} The menu item that was added
39362      */
39363     addMenuItem : function(config){
39364         if(!(config instanceof Roo.menu.Item)){
39365             if(typeof config.checked == "boolean"){ // must be check menu item config?
39366                 config = new Roo.menu.CheckItem(config);
39367             }else{
39368                 config = new Roo.menu.Item(config);
39369             }
39370         }
39371         return this.addItem(config);
39372     },
39373
39374     /**
39375      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39376      * @param {String} text The text to display in the menu item
39377      * @return {Roo.menu.Item} The menu item that was added
39378      */
39379     addText : function(text){
39380         return this.addItem(new Roo.menu.TextItem({ text : text }));
39381     },
39382
39383     /**
39384      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39385      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39386      * @param {Roo.menu.Item} item The menu item to add
39387      * @return {Roo.menu.Item} The menu item that was added
39388      */
39389     insert : function(index, item){
39390         this.items.insert(index, item);
39391         if(this.ul){
39392             var li = document.createElement("li");
39393             li.className = "x-menu-list-item";
39394             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39395             item.render(li, this);
39396             this.delayAutoWidth();
39397         }
39398         return item;
39399     },
39400
39401     /**
39402      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39403      * @param {Roo.menu.Item} item The menu item to remove
39404      */
39405     remove : function(item){
39406         this.items.removeKey(item.id);
39407         item.destroy();
39408     },
39409
39410     /**
39411      * Removes and destroys all items in the menu
39412      */
39413     removeAll : function(){
39414         var f;
39415         while(f = this.items.first()){
39416             this.remove(f);
39417         }
39418     }
39419 });
39420
39421 // MenuNav is a private utility class used internally by the Menu
39422 Roo.menu.MenuNav = function(menu){
39423     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39424     this.scope = this.menu = menu;
39425 };
39426
39427 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39428     doRelay : function(e, h){
39429         var k = e.getKey();
39430         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39431             this.menu.tryActivate(0, 1);
39432             return false;
39433         }
39434         return h.call(this.scope || this, e, this.menu);
39435     },
39436
39437     up : function(e, m){
39438         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39439             m.tryActivate(m.items.length-1, -1);
39440         }
39441     },
39442
39443     down : function(e, m){
39444         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39445             m.tryActivate(0, 1);
39446         }
39447     },
39448
39449     right : function(e, m){
39450         if(m.activeItem){
39451             m.activeItem.expandMenu(true);
39452         }
39453     },
39454
39455     left : function(e, m){
39456         m.hide();
39457         if(m.parentMenu && m.parentMenu.activeItem){
39458             m.parentMenu.activeItem.activate();
39459         }
39460     },
39461
39462     enter : function(e, m){
39463         if(m.activeItem){
39464             e.stopPropagation();
39465             m.activeItem.onClick(e);
39466             m.fireEvent("click", this, m.activeItem);
39467             return true;
39468         }
39469     }
39470 });/*
39471  * Based on:
39472  * Ext JS Library 1.1.1
39473  * Copyright(c) 2006-2007, Ext JS, LLC.
39474  *
39475  * Originally Released Under LGPL - original licence link has changed is not relivant.
39476  *
39477  * Fork - LGPL
39478  * <script type="text/javascript">
39479  */
39480  
39481 /**
39482  * @class Roo.menu.MenuMgr
39483  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39484  * @static
39485  */
39486 Roo.menu.MenuMgr = function(){
39487    var menus, active, groups = {}, attached = false, lastShow = new Date();
39488
39489    // private - called when first menu is created
39490    function init(){
39491        menus = {};
39492        active = new Roo.util.MixedCollection();
39493        Roo.get(document).addKeyListener(27, function(){
39494            if(active.length > 0){
39495                hideAll();
39496            }
39497        });
39498    }
39499
39500    // private
39501    function hideAll(){
39502        if(active && active.length > 0){
39503            var c = active.clone();
39504            c.each(function(m){
39505                m.hide();
39506            });
39507        }
39508    }
39509
39510    // private
39511    function onHide(m){
39512        active.remove(m);
39513        if(active.length < 1){
39514            Roo.get(document).un("mousedown", onMouseDown);
39515            attached = false;
39516        }
39517    }
39518
39519    // private
39520    function onShow(m){
39521        var last = active.last();
39522        lastShow = new Date();
39523        active.add(m);
39524        if(!attached){
39525            Roo.get(document).on("mousedown", onMouseDown);
39526            attached = true;
39527        }
39528        if(m.parentMenu){
39529           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39530           m.parentMenu.activeChild = m;
39531        }else if(last && last.isVisible()){
39532           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39533        }
39534    }
39535
39536    // private
39537    function onBeforeHide(m){
39538        if(m.activeChild){
39539            m.activeChild.hide();
39540        }
39541        if(m.autoHideTimer){
39542            clearTimeout(m.autoHideTimer);
39543            delete m.autoHideTimer;
39544        }
39545    }
39546
39547    // private
39548    function onBeforeShow(m){
39549        var pm = m.parentMenu;
39550        if(!pm && !m.allowOtherMenus){
39551            hideAll();
39552        }else if(pm && pm.activeChild && active != m){
39553            pm.activeChild.hide();
39554        }
39555    }
39556
39557    // private
39558    function onMouseDown(e){
39559        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39560            hideAll();
39561        }
39562    }
39563
39564    // private
39565    function onBeforeCheck(mi, state){
39566        if(state){
39567            var g = groups[mi.group];
39568            for(var i = 0, l = g.length; i < l; i++){
39569                if(g[i] != mi){
39570                    g[i].setChecked(false);
39571                }
39572            }
39573        }
39574    }
39575
39576    return {
39577
39578        /**
39579         * Hides all menus that are currently visible
39580         */
39581        hideAll : function(){
39582             hideAll();  
39583        },
39584
39585        // private
39586        register : function(menu){
39587            if(!menus){
39588                init();
39589            }
39590            menus[menu.id] = menu;
39591            menu.on("beforehide", onBeforeHide);
39592            menu.on("hide", onHide);
39593            menu.on("beforeshow", onBeforeShow);
39594            menu.on("show", onShow);
39595            var g = menu.group;
39596            if(g && menu.events["checkchange"]){
39597                if(!groups[g]){
39598                    groups[g] = [];
39599                }
39600                groups[g].push(menu);
39601                menu.on("checkchange", onCheck);
39602            }
39603        },
39604
39605         /**
39606          * Returns a {@link Roo.menu.Menu} object
39607          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39608          * be used to generate and return a new Menu instance.
39609          */
39610        get : function(menu){
39611            if(typeof menu == "string"){ // menu id
39612                return menus[menu];
39613            }else if(menu.events){  // menu instance
39614                return menu;
39615            }else if(typeof menu.length == 'number'){ // array of menu items?
39616                return new Roo.menu.Menu({items:menu});
39617            }else{ // otherwise, must be a config
39618                return new Roo.menu.Menu(menu);
39619            }
39620        },
39621
39622        // private
39623        unregister : function(menu){
39624            delete menus[menu.id];
39625            menu.un("beforehide", onBeforeHide);
39626            menu.un("hide", onHide);
39627            menu.un("beforeshow", onBeforeShow);
39628            menu.un("show", onShow);
39629            var g = menu.group;
39630            if(g && menu.events["checkchange"]){
39631                groups[g].remove(menu);
39632                menu.un("checkchange", onCheck);
39633            }
39634        },
39635
39636        // private
39637        registerCheckable : function(menuItem){
39638            var g = menuItem.group;
39639            if(g){
39640                if(!groups[g]){
39641                    groups[g] = [];
39642                }
39643                groups[g].push(menuItem);
39644                menuItem.on("beforecheckchange", onBeforeCheck);
39645            }
39646        },
39647
39648        // private
39649        unregisterCheckable : function(menuItem){
39650            var g = menuItem.group;
39651            if(g){
39652                groups[g].remove(menuItem);
39653                menuItem.un("beforecheckchange", onBeforeCheck);
39654            }
39655        }
39656    };
39657 }();/*
39658  * Based on:
39659  * Ext JS Library 1.1.1
39660  * Copyright(c) 2006-2007, Ext JS, LLC.
39661  *
39662  * Originally Released Under LGPL - original licence link has changed is not relivant.
39663  *
39664  * Fork - LGPL
39665  * <script type="text/javascript">
39666  */
39667  
39668
39669 /**
39670  * @class Roo.menu.BaseItem
39671  * @extends Roo.Component
39672  * @abstract
39673  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39674  * management and base configuration options shared by all menu components.
39675  * @constructor
39676  * Creates a new BaseItem
39677  * @param {Object} config Configuration options
39678  */
39679 Roo.menu.BaseItem = function(config){
39680     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39681
39682     this.addEvents({
39683         /**
39684          * @event click
39685          * Fires when this item is clicked
39686          * @param {Roo.menu.BaseItem} this
39687          * @param {Roo.EventObject} e
39688          */
39689         click: true,
39690         /**
39691          * @event activate
39692          * Fires when this item is activated
39693          * @param {Roo.menu.BaseItem} this
39694          */
39695         activate : true,
39696         /**
39697          * @event deactivate
39698          * Fires when this item is deactivated
39699          * @param {Roo.menu.BaseItem} this
39700          */
39701         deactivate : true
39702     });
39703
39704     if(this.handler){
39705         this.on("click", this.handler, this.scope, true);
39706     }
39707 };
39708
39709 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39710     /**
39711      * @cfg {Function} handler
39712      * A function that will handle the click event of this menu item (defaults to undefined)
39713      */
39714     /**
39715      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39716      */
39717     canActivate : false,
39718     
39719      /**
39720      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39721      */
39722     hidden: false,
39723     
39724     /**
39725      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39726      */
39727     activeClass : "x-menu-item-active",
39728     /**
39729      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39730      */
39731     hideOnClick : true,
39732     /**
39733      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39734      */
39735     hideDelay : 100,
39736
39737     // private
39738     ctype: "Roo.menu.BaseItem",
39739
39740     // private
39741     actionMode : "container",
39742
39743     // private
39744     render : function(container, parentMenu){
39745         this.parentMenu = parentMenu;
39746         Roo.menu.BaseItem.superclass.render.call(this, container);
39747         this.container.menuItemId = this.id;
39748     },
39749
39750     // private
39751     onRender : function(container, position){
39752         this.el = Roo.get(this.el);
39753         container.dom.appendChild(this.el.dom);
39754     },
39755
39756     // private
39757     onClick : function(e){
39758         if(!this.disabled && this.fireEvent("click", this, e) !== false
39759                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39760             this.handleClick(e);
39761         }else{
39762             e.stopEvent();
39763         }
39764     },
39765
39766     // private
39767     activate : function(){
39768         if(this.disabled){
39769             return false;
39770         }
39771         var li = this.container;
39772         li.addClass(this.activeClass);
39773         this.region = li.getRegion().adjust(2, 2, -2, -2);
39774         this.fireEvent("activate", this);
39775         return true;
39776     },
39777
39778     // private
39779     deactivate : function(){
39780         this.container.removeClass(this.activeClass);
39781         this.fireEvent("deactivate", this);
39782     },
39783
39784     // private
39785     shouldDeactivate : function(e){
39786         return !this.region || !this.region.contains(e.getPoint());
39787     },
39788
39789     // private
39790     handleClick : function(e){
39791         if(this.hideOnClick){
39792             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39793         }
39794     },
39795
39796     // private
39797     expandMenu : function(autoActivate){
39798         // do nothing
39799     },
39800
39801     // private
39802     hideMenu : function(){
39803         // do nothing
39804     }
39805 });/*
39806  * Based on:
39807  * Ext JS Library 1.1.1
39808  * Copyright(c) 2006-2007, Ext JS, LLC.
39809  *
39810  * Originally Released Under LGPL - original licence link has changed is not relivant.
39811  *
39812  * Fork - LGPL
39813  * <script type="text/javascript">
39814  */
39815  
39816 /**
39817  * @class Roo.menu.Adapter
39818  * @extends Roo.menu.BaseItem
39819  * @abstract
39820  * 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.
39821  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39822  * @constructor
39823  * Creates a new Adapter
39824  * @param {Object} config Configuration options
39825  */
39826 Roo.menu.Adapter = function(component, config){
39827     Roo.menu.Adapter.superclass.constructor.call(this, config);
39828     this.component = component;
39829 };
39830 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39831     // private
39832     canActivate : true,
39833
39834     // private
39835     onRender : function(container, position){
39836         this.component.render(container);
39837         this.el = this.component.getEl();
39838     },
39839
39840     // private
39841     activate : function(){
39842         if(this.disabled){
39843             return false;
39844         }
39845         this.component.focus();
39846         this.fireEvent("activate", this);
39847         return true;
39848     },
39849
39850     // private
39851     deactivate : function(){
39852         this.fireEvent("deactivate", this);
39853     },
39854
39855     // private
39856     disable : function(){
39857         this.component.disable();
39858         Roo.menu.Adapter.superclass.disable.call(this);
39859     },
39860
39861     // private
39862     enable : function(){
39863         this.component.enable();
39864         Roo.menu.Adapter.superclass.enable.call(this);
39865     }
39866 });/*
39867  * Based on:
39868  * Ext JS Library 1.1.1
39869  * Copyright(c) 2006-2007, Ext JS, LLC.
39870  *
39871  * Originally Released Under LGPL - original licence link has changed is not relivant.
39872  *
39873  * Fork - LGPL
39874  * <script type="text/javascript">
39875  */
39876
39877 /**
39878  * @class Roo.menu.TextItem
39879  * @extends Roo.menu.BaseItem
39880  * Adds a static text string to a menu, usually used as either a heading or group separator.
39881  * Note: old style constructor with text is still supported.
39882  * 
39883  * @constructor
39884  * Creates a new TextItem
39885  * @param {Object} cfg Configuration
39886  */
39887 Roo.menu.TextItem = function(cfg){
39888     if (typeof(cfg) == 'string') {
39889         this.text = cfg;
39890     } else {
39891         Roo.apply(this,cfg);
39892     }
39893     
39894     Roo.menu.TextItem.superclass.constructor.call(this);
39895 };
39896
39897 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39898     /**
39899      * @cfg {String} text Text to show on item.
39900      */
39901     text : '',
39902     
39903     /**
39904      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39905      */
39906     hideOnClick : false,
39907     /**
39908      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39909      */
39910     itemCls : "x-menu-text",
39911
39912     // private
39913     onRender : function(){
39914         var s = document.createElement("span");
39915         s.className = this.itemCls;
39916         s.innerHTML = this.text;
39917         this.el = s;
39918         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39919     }
39920 });/*
39921  * Based on:
39922  * Ext JS Library 1.1.1
39923  * Copyright(c) 2006-2007, Ext JS, LLC.
39924  *
39925  * Originally Released Under LGPL - original licence link has changed is not relivant.
39926  *
39927  * Fork - LGPL
39928  * <script type="text/javascript">
39929  */
39930
39931 /**
39932  * @class Roo.menu.Separator
39933  * @extends Roo.menu.BaseItem
39934  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39935  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39936  * @constructor
39937  * @param {Object} config Configuration options
39938  */
39939 Roo.menu.Separator = function(config){
39940     Roo.menu.Separator.superclass.constructor.call(this, config);
39941 };
39942
39943 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39944     /**
39945      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39946      */
39947     itemCls : "x-menu-sep",
39948     /**
39949      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39950      */
39951     hideOnClick : false,
39952
39953     // private
39954     onRender : function(li){
39955         var s = document.createElement("span");
39956         s.className = this.itemCls;
39957         s.innerHTML = "&#160;";
39958         this.el = s;
39959         li.addClass("x-menu-sep-li");
39960         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39961     }
39962 });/*
39963  * Based on:
39964  * Ext JS Library 1.1.1
39965  * Copyright(c) 2006-2007, Ext JS, LLC.
39966  *
39967  * Originally Released Under LGPL - original licence link has changed is not relivant.
39968  *
39969  * Fork - LGPL
39970  * <script type="text/javascript">
39971  */
39972 /**
39973  * @class Roo.menu.Item
39974  * @extends Roo.menu.BaseItem
39975  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39976  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39977  * activation and click handling.
39978  * @constructor
39979  * Creates a new Item
39980  * @param {Object} config Configuration options
39981  */
39982 Roo.menu.Item = function(config){
39983     Roo.menu.Item.superclass.constructor.call(this, config);
39984     if(this.menu){
39985         this.menu = Roo.menu.MenuMgr.get(this.menu);
39986     }
39987 };
39988 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39989     /**
39990      * @cfg {Roo.menu.Menu} menu
39991      * A Sub menu
39992      */
39993     /**
39994      * @cfg {String} text
39995      * The text to show on the menu item.
39996      */
39997     text: '',
39998      /**
39999      * @cfg {String} html to render in menu
40000      * The text to show on the menu item (HTML version).
40001      */
40002     html: '',
40003     /**
40004      * @cfg {String} icon
40005      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
40006      */
40007     icon: undefined,
40008     /**
40009      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
40010      */
40011     itemCls : "x-menu-item",
40012     /**
40013      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
40014      */
40015     canActivate : true,
40016     /**
40017      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
40018      */
40019     showDelay: 200,
40020     // doc'd in BaseItem
40021     hideDelay: 200,
40022
40023     // private
40024     ctype: "Roo.menu.Item",
40025     
40026     // private
40027     onRender : function(container, position){
40028         var el = document.createElement("a");
40029         el.hideFocus = true;
40030         el.unselectable = "on";
40031         el.href = this.href || "#";
40032         if(this.hrefTarget){
40033             el.target = this.hrefTarget;
40034         }
40035         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
40036         
40037         var html = this.html.length ? this.html  : String.format('{0}',this.text);
40038         
40039         el.innerHTML = String.format(
40040                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
40041                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
40042         this.el = el;
40043         Roo.menu.Item.superclass.onRender.call(this, container, position);
40044     },
40045
40046     /**
40047      * Sets the text to display in this menu item
40048      * @param {String} text The text to display
40049      * @param {Boolean} isHTML true to indicate text is pure html.
40050      */
40051     setText : function(text, isHTML){
40052         if (isHTML) {
40053             this.html = text;
40054         } else {
40055             this.text = text;
40056             this.html = '';
40057         }
40058         if(this.rendered){
40059             var html = this.html.length ? this.html  : String.format('{0}',this.text);
40060      
40061             this.el.update(String.format(
40062                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
40063                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
40064             this.parentMenu.autoWidth();
40065         }
40066     },
40067
40068     // private
40069     handleClick : function(e){
40070         if(!this.href){ // if no link defined, stop the event automatically
40071             e.stopEvent();
40072         }
40073         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
40074     },
40075
40076     // private
40077     activate : function(autoExpand){
40078         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
40079             this.focus();
40080             if(autoExpand){
40081                 this.expandMenu();
40082             }
40083         }
40084         return true;
40085     },
40086
40087     // private
40088     shouldDeactivate : function(e){
40089         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
40090             if(this.menu && this.menu.isVisible()){
40091                 return !this.menu.getEl().getRegion().contains(e.getPoint());
40092             }
40093             return true;
40094         }
40095         return false;
40096     },
40097
40098     // private
40099     deactivate : function(){
40100         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
40101         this.hideMenu();
40102     },
40103
40104     // private
40105     expandMenu : function(autoActivate){
40106         if(!this.disabled && this.menu){
40107             clearTimeout(this.hideTimer);
40108             delete this.hideTimer;
40109             if(!this.menu.isVisible() && !this.showTimer){
40110                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
40111             }else if (this.menu.isVisible() && autoActivate){
40112                 this.menu.tryActivate(0, 1);
40113             }
40114         }
40115     },
40116
40117     // private
40118     deferExpand : function(autoActivate){
40119         delete this.showTimer;
40120         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
40121         if(autoActivate){
40122             this.menu.tryActivate(0, 1);
40123         }
40124     },
40125
40126     // private
40127     hideMenu : function(){
40128         clearTimeout(this.showTimer);
40129         delete this.showTimer;
40130         if(!this.hideTimer && this.menu && this.menu.isVisible()){
40131             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
40132         }
40133     },
40134
40135     // private
40136     deferHide : function(){
40137         delete this.hideTimer;
40138         this.menu.hide();
40139     }
40140 });/*
40141  * Based on:
40142  * Ext JS Library 1.1.1
40143  * Copyright(c) 2006-2007, Ext JS, LLC.
40144  *
40145  * Originally Released Under LGPL - original licence link has changed is not relivant.
40146  *
40147  * Fork - LGPL
40148  * <script type="text/javascript">
40149  */
40150  
40151 /**
40152  * @class Roo.menu.CheckItem
40153  * @extends Roo.menu.Item
40154  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
40155  * @constructor
40156  * Creates a new CheckItem
40157  * @param {Object} config Configuration options
40158  */
40159 Roo.menu.CheckItem = function(config){
40160     Roo.menu.CheckItem.superclass.constructor.call(this, config);
40161     this.addEvents({
40162         /**
40163          * @event beforecheckchange
40164          * Fires before the checked value is set, providing an opportunity to cancel if needed
40165          * @param {Roo.menu.CheckItem} this
40166          * @param {Boolean} checked The new checked value that will be set
40167          */
40168         "beforecheckchange" : true,
40169         /**
40170          * @event checkchange
40171          * Fires after the checked value has been set
40172          * @param {Roo.menu.CheckItem} this
40173          * @param {Boolean} checked The checked value that was set
40174          */
40175         "checkchange" : true
40176     });
40177     if(this.checkHandler){
40178         this.on('checkchange', this.checkHandler, this.scope);
40179     }
40180 };
40181 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
40182     /**
40183      * @cfg {String} group
40184      * All check items with the same group name will automatically be grouped into a single-select
40185      * radio button group (defaults to '')
40186      */
40187     /**
40188      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40189      */
40190     itemCls : "x-menu-item x-menu-check-item",
40191     /**
40192      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40193      */
40194     groupClass : "x-menu-group-item",
40195
40196     /**
40197      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
40198      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40199      * initialized with checked = true will be rendered as checked.
40200      */
40201     checked: false,
40202
40203     // private
40204     ctype: "Roo.menu.CheckItem",
40205
40206     // private
40207     onRender : function(c){
40208         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40209         if(this.group){
40210             this.el.addClass(this.groupClass);
40211         }
40212         Roo.menu.MenuMgr.registerCheckable(this);
40213         if(this.checked){
40214             this.checked = false;
40215             this.setChecked(true, true);
40216         }
40217     },
40218
40219     // private
40220     destroy : function(){
40221         if(this.rendered){
40222             Roo.menu.MenuMgr.unregisterCheckable(this);
40223         }
40224         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40225     },
40226
40227     /**
40228      * Set the checked state of this item
40229      * @param {Boolean} checked The new checked value
40230      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40231      */
40232     setChecked : function(state, suppressEvent){
40233         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40234             if(this.container){
40235                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40236             }
40237             this.checked = state;
40238             if(suppressEvent !== true){
40239                 this.fireEvent("checkchange", this, state);
40240             }
40241         }
40242     },
40243
40244     // private
40245     handleClick : function(e){
40246        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40247            this.setChecked(!this.checked);
40248        }
40249        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40250     }
40251 });/*
40252  * Based on:
40253  * Ext JS Library 1.1.1
40254  * Copyright(c) 2006-2007, Ext JS, LLC.
40255  *
40256  * Originally Released Under LGPL - original licence link has changed is not relivant.
40257  *
40258  * Fork - LGPL
40259  * <script type="text/javascript">
40260  */
40261  
40262 /**
40263  * @class Roo.menu.DateItem
40264  * @extends Roo.menu.Adapter
40265  * A menu item that wraps the {@link Roo.DatPicker} component.
40266  * @constructor
40267  * Creates a new DateItem
40268  * @param {Object} config Configuration options
40269  */
40270 Roo.menu.DateItem = function(config){
40271     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40272     /** The Roo.DatePicker object @type Roo.DatePicker */
40273     this.picker = this.component;
40274     this.addEvents({select: true});
40275     
40276     this.picker.on("render", function(picker){
40277         picker.getEl().swallowEvent("click");
40278         picker.container.addClass("x-menu-date-item");
40279     });
40280
40281     this.picker.on("select", this.onSelect, this);
40282 };
40283
40284 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40285     // private
40286     onSelect : function(picker, date){
40287         this.fireEvent("select", this, date, picker);
40288         Roo.menu.DateItem.superclass.handleClick.call(this);
40289     }
40290 });/*
40291  * Based on:
40292  * Ext JS Library 1.1.1
40293  * Copyright(c) 2006-2007, Ext JS, LLC.
40294  *
40295  * Originally Released Under LGPL - original licence link has changed is not relivant.
40296  *
40297  * Fork - LGPL
40298  * <script type="text/javascript">
40299  */
40300  
40301 /**
40302  * @class Roo.menu.ColorItem
40303  * @extends Roo.menu.Adapter
40304  * A menu item that wraps the {@link Roo.ColorPalette} component.
40305  * @constructor
40306  * Creates a new ColorItem
40307  * @param {Object} config Configuration options
40308  */
40309 Roo.menu.ColorItem = function(config){
40310     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40311     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40312     this.palette = this.component;
40313     this.relayEvents(this.palette, ["select"]);
40314     if(this.selectHandler){
40315         this.on('select', this.selectHandler, this.scope);
40316     }
40317 };
40318 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40319  * Based on:
40320  * Ext JS Library 1.1.1
40321  * Copyright(c) 2006-2007, Ext JS, LLC.
40322  *
40323  * Originally Released Under LGPL - original licence link has changed is not relivant.
40324  *
40325  * Fork - LGPL
40326  * <script type="text/javascript">
40327  */
40328  
40329
40330 /**
40331  * @class Roo.menu.DateMenu
40332  * @extends Roo.menu.Menu
40333  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40334  * @constructor
40335  * Creates a new DateMenu
40336  * @param {Object} config Configuration options
40337  */
40338 Roo.menu.DateMenu = function(config){
40339     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40340     this.plain = true;
40341     var di = new Roo.menu.DateItem(config);
40342     this.add(di);
40343     /**
40344      * The {@link Roo.DatePicker} instance for this DateMenu
40345      * @type DatePicker
40346      */
40347     this.picker = di.picker;
40348     /**
40349      * @event select
40350      * @param {DatePicker} picker
40351      * @param {Date} date
40352      */
40353     this.relayEvents(di, ["select"]);
40354     this.on('beforeshow', function(){
40355         if(this.picker){
40356             this.picker.hideMonthPicker(false);
40357         }
40358     }, this);
40359 };
40360 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40361     cls:'x-date-menu'
40362 });/*
40363  * Based on:
40364  * Ext JS Library 1.1.1
40365  * Copyright(c) 2006-2007, Ext JS, LLC.
40366  *
40367  * Originally Released Under LGPL - original licence link has changed is not relivant.
40368  *
40369  * Fork - LGPL
40370  * <script type="text/javascript">
40371  */
40372  
40373
40374 /**
40375  * @class Roo.menu.ColorMenu
40376  * @extends Roo.menu.Menu
40377  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40378  * @constructor
40379  * Creates a new ColorMenu
40380  * @param {Object} config Configuration options
40381  */
40382 Roo.menu.ColorMenu = function(config){
40383     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40384     this.plain = true;
40385     var ci = new Roo.menu.ColorItem(config);
40386     this.add(ci);
40387     /**
40388      * The {@link Roo.ColorPalette} instance for this ColorMenu
40389      * @type ColorPalette
40390      */
40391     this.palette = ci.palette;
40392     /**
40393      * @event select
40394      * @param {ColorPalette} palette
40395      * @param {String} color
40396      */
40397     this.relayEvents(ci, ["select"]);
40398 };
40399 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40400  * Based on:
40401  * Ext JS Library 1.1.1
40402  * Copyright(c) 2006-2007, Ext JS, LLC.
40403  *
40404  * Originally Released Under LGPL - original licence link has changed is not relivant.
40405  *
40406  * Fork - LGPL
40407  * <script type="text/javascript">
40408  */
40409  
40410 /**
40411  * @class Roo.form.TextItem
40412  * @extends Roo.BoxComponent
40413  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40414  * @constructor
40415  * Creates a new TextItem
40416  * @param {Object} config Configuration options
40417  */
40418 Roo.form.TextItem = function(config){
40419     Roo.form.TextItem.superclass.constructor.call(this, config);
40420 };
40421
40422 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40423     
40424     /**
40425      * @cfg {String} tag the tag for this item (default div)
40426      */
40427     tag : 'div',
40428     /**
40429      * @cfg {String} html the content for this item
40430      */
40431     html : '',
40432     
40433     getAutoCreate : function()
40434     {
40435         var cfg = {
40436             id: this.id,
40437             tag: this.tag,
40438             html: this.html,
40439             cls: 'x-form-item'
40440         };
40441         
40442         return cfg;
40443         
40444     },
40445     
40446     onRender : function(ct, position)
40447     {
40448         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40449         
40450         if(!this.el){
40451             var cfg = this.getAutoCreate();
40452             if(!cfg.name){
40453                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40454             }
40455             if (!cfg.name.length) {
40456                 delete cfg.name;
40457             }
40458             this.el = ct.createChild(cfg, position);
40459         }
40460     },
40461     /*
40462      * setHTML
40463      * @param {String} html update the Contents of the element.
40464      */
40465     setHTML : function(html)
40466     {
40467         this.fieldEl.dom.innerHTML = html;
40468     }
40469     
40470 });/*
40471  * Based on:
40472  * Ext JS Library 1.1.1
40473  * Copyright(c) 2006-2007, Ext JS, LLC.
40474  *
40475  * Originally Released Under LGPL - original licence link has changed is not relivant.
40476  *
40477  * Fork - LGPL
40478  * <script type="text/javascript">
40479  */
40480  
40481 /**
40482  * @class Roo.form.Field
40483  * @extends Roo.BoxComponent
40484  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40485  * @constructor
40486  * Creates a new Field
40487  * @param {Object} config Configuration options
40488  */
40489 Roo.form.Field = function(config){
40490     Roo.form.Field.superclass.constructor.call(this, config);
40491 };
40492
40493 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40494     /**
40495      * @cfg {String} fieldLabel Label to use when rendering a form.
40496      */
40497        /**
40498      * @cfg {String} qtip Mouse over tip
40499      */
40500      
40501     /**
40502      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40503      */
40504     invalidClass : "x-form-invalid",
40505     /**
40506      * @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")
40507      */
40508     invalidText : "The value in this field is invalid",
40509     /**
40510      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40511      */
40512     focusClass : "x-form-focus",
40513     /**
40514      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40515       automatic validation (defaults to "keyup").
40516      */
40517     validationEvent : "keyup",
40518     /**
40519      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40520      */
40521     validateOnBlur : true,
40522     /**
40523      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40524      */
40525     validationDelay : 250,
40526     /**
40527      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40528      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40529      */
40530     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40531     /**
40532      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40533      */
40534     fieldClass : "x-form-field",
40535     /**
40536      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40537      *<pre>
40538 Value         Description
40539 -----------   ----------------------------------------------------------------------
40540 qtip          Display a quick tip when the user hovers over the field
40541 title         Display a default browser title attribute popup
40542 under         Add a block div beneath the field containing the error text
40543 side          Add an error icon to the right of the field with a popup on hover
40544 [element id]  Add the error text directly to the innerHTML of the specified element
40545 </pre>
40546      */
40547     msgTarget : 'qtip',
40548     /**
40549      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40550      */
40551     msgFx : 'normal',
40552
40553     /**
40554      * @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.
40555      */
40556     readOnly : false,
40557
40558     /**
40559      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40560      */
40561     disabled : false,
40562
40563     /**
40564      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40565      */
40566     inputType : undefined,
40567     
40568     /**
40569      * @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).
40570          */
40571         tabIndex : undefined,
40572         
40573     // private
40574     isFormField : true,
40575
40576     // private
40577     hasFocus : false,
40578     /**
40579      * @property {Roo.Element} fieldEl
40580      * Element Containing the rendered Field (with label etc.)
40581      */
40582     /**
40583      * @cfg {Mixed} value A value to initialize this field with.
40584      */
40585     value : undefined,
40586
40587     /**
40588      * @cfg {String} name The field's HTML name attribute.
40589      */
40590     /**
40591      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40592      */
40593     // private
40594     loadedValue : false,
40595      
40596      
40597         // private ??
40598         initComponent : function(){
40599         Roo.form.Field.superclass.initComponent.call(this);
40600         this.addEvents({
40601             /**
40602              * @event focus
40603              * Fires when this field receives input focus.
40604              * @param {Roo.form.Field} this
40605              */
40606             focus : true,
40607             /**
40608              * @event blur
40609              * Fires when this field loses input focus.
40610              * @param {Roo.form.Field} this
40611              */
40612             blur : true,
40613             /**
40614              * @event specialkey
40615              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40616              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40617              * @param {Roo.form.Field} this
40618              * @param {Roo.EventObject} e The event object
40619              */
40620             specialkey : true,
40621             /**
40622              * @event change
40623              * Fires just before the field blurs if the field value has changed.
40624              * @param {Roo.form.Field} this
40625              * @param {Mixed} newValue The new value
40626              * @param {Mixed} oldValue The original value
40627              */
40628             change : true,
40629             /**
40630              * @event invalid
40631              * Fires after the field has been marked as invalid.
40632              * @param {Roo.form.Field} this
40633              * @param {String} msg The validation message
40634              */
40635             invalid : true,
40636             /**
40637              * @event valid
40638              * Fires after the field has been validated with no errors.
40639              * @param {Roo.form.Field} this
40640              */
40641             valid : true,
40642              /**
40643              * @event keyup
40644              * Fires after the key up
40645              * @param {Roo.form.Field} this
40646              * @param {Roo.EventObject}  e The event Object
40647              */
40648             keyup : true
40649         });
40650     },
40651
40652     /**
40653      * Returns the name attribute of the field if available
40654      * @return {String} name The field name
40655      */
40656     getName: function(){
40657          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40658     },
40659
40660     // private
40661     onRender : function(ct, position){
40662         Roo.form.Field.superclass.onRender.call(this, ct, position);
40663         if(!this.el){
40664             var cfg = this.getAutoCreate();
40665             if(!cfg.name){
40666                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40667             }
40668             if (!cfg.name.length) {
40669                 delete cfg.name;
40670             }
40671             if(this.inputType){
40672                 cfg.type = this.inputType;
40673             }
40674             this.el = ct.createChild(cfg, position);
40675         }
40676         var type = this.el.dom.type;
40677         if(type){
40678             if(type == 'password'){
40679                 type = 'text';
40680             }
40681             this.el.addClass('x-form-'+type);
40682         }
40683         if(this.readOnly){
40684             this.el.dom.readOnly = true;
40685         }
40686         if(this.tabIndex !== undefined){
40687             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40688         }
40689
40690         this.el.addClass([this.fieldClass, this.cls]);
40691         this.initValue();
40692     },
40693
40694     /**
40695      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40696      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40697      * @return {Roo.form.Field} this
40698      */
40699     applyTo : function(target){
40700         this.allowDomMove = false;
40701         this.el = Roo.get(target);
40702         this.render(this.el.dom.parentNode);
40703         return this;
40704     },
40705
40706     // private
40707     initValue : function(){
40708         if(this.value !== undefined){
40709             this.setValue(this.value);
40710         }else if(this.el.dom.value.length > 0){
40711             this.setValue(this.el.dom.value);
40712         }
40713     },
40714
40715     /**
40716      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40717      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40718      */
40719     isDirty : function() {
40720         if(this.disabled) {
40721             return false;
40722         }
40723         return String(this.getValue()) !== String(this.originalValue);
40724     },
40725
40726     /**
40727      * stores the current value in loadedValue
40728      */
40729     resetHasChanged : function()
40730     {
40731         this.loadedValue = String(this.getValue());
40732     },
40733     /**
40734      * checks the current value against the 'loaded' value.
40735      * Note - will return false if 'resetHasChanged' has not been called first.
40736      */
40737     hasChanged : function()
40738     {
40739         if(this.disabled || this.readOnly) {
40740             return false;
40741         }
40742         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40743     },
40744     
40745     
40746     
40747     // private
40748     afterRender : function(){
40749         Roo.form.Field.superclass.afterRender.call(this);
40750         this.initEvents();
40751     },
40752
40753     // private
40754     fireKey : function(e){
40755         //Roo.log('field ' + e.getKey());
40756         if(e.isNavKeyPress()){
40757             this.fireEvent("specialkey", this, e);
40758         }
40759     },
40760
40761     /**
40762      * Resets the current field value to the originally loaded value and clears any validation messages
40763      */
40764     reset : function(){
40765         this.setValue(this.resetValue);
40766         this.originalValue = this.getValue();
40767         this.clearInvalid();
40768     },
40769
40770     // private
40771     initEvents : function(){
40772         // safari killled keypress - so keydown is now used..
40773         this.el.on("keydown" , this.fireKey,  this);
40774         this.el.on("focus", this.onFocus,  this);
40775         this.el.on("blur", this.onBlur,  this);
40776         this.el.relayEvent('keyup', this);
40777
40778         // reference to original value for reset
40779         this.originalValue = this.getValue();
40780         this.resetValue =  this.getValue();
40781     },
40782
40783     // private
40784     onFocus : function(){
40785         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40786             this.el.addClass(this.focusClass);
40787         }
40788         if(!this.hasFocus){
40789             this.hasFocus = true;
40790             this.startValue = this.getValue();
40791             this.fireEvent("focus", this);
40792         }
40793     },
40794
40795     beforeBlur : Roo.emptyFn,
40796
40797     // private
40798     onBlur : function(){
40799         this.beforeBlur();
40800         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40801             this.el.removeClass(this.focusClass);
40802         }
40803         this.hasFocus = false;
40804         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40805             this.validate();
40806         }
40807         var v = this.getValue();
40808         if(String(v) !== String(this.startValue)){
40809             this.fireEvent('change', this, v, this.startValue);
40810         }
40811         this.fireEvent("blur", this);
40812     },
40813
40814     /**
40815      * Returns whether or not the field value is currently valid
40816      * @param {Boolean} preventMark True to disable marking the field invalid
40817      * @return {Boolean} True if the value is valid, else false
40818      */
40819     isValid : function(preventMark){
40820         if(this.disabled){
40821             return true;
40822         }
40823         var restore = this.preventMark;
40824         this.preventMark = preventMark === true;
40825         var v = this.validateValue(this.processValue(this.getRawValue()));
40826         this.preventMark = restore;
40827         return v;
40828     },
40829
40830     /**
40831      * Validates the field value
40832      * @return {Boolean} True if the value is valid, else false
40833      */
40834     validate : function(){
40835         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40836             this.clearInvalid();
40837             return true;
40838         }
40839         return false;
40840     },
40841
40842     processValue : function(value){
40843         return value;
40844     },
40845
40846     // private
40847     // Subclasses should provide the validation implementation by overriding this
40848     validateValue : function(value){
40849         return true;
40850     },
40851
40852     /**
40853      * Mark this field as invalid
40854      * @param {String} msg The validation message
40855      */
40856     markInvalid : function(msg){
40857         if(!this.rendered || this.preventMark){ // not rendered
40858             return;
40859         }
40860         
40861         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40862         
40863         obj.el.addClass(this.invalidClass);
40864         msg = msg || this.invalidText;
40865         switch(this.msgTarget){
40866             case 'qtip':
40867                 obj.el.dom.qtip = msg;
40868                 obj.el.dom.qclass = 'x-form-invalid-tip';
40869                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40870                     Roo.QuickTips.enable();
40871                 }
40872                 break;
40873             case 'title':
40874                 this.el.dom.title = msg;
40875                 break;
40876             case 'under':
40877                 if(!this.errorEl){
40878                     var elp = this.el.findParent('.x-form-element', 5, true);
40879                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40880                     this.errorEl.setWidth(elp.getWidth(true)-20);
40881                 }
40882                 this.errorEl.update(msg);
40883                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40884                 break;
40885             case 'side':
40886                 if(!this.errorIcon){
40887                     var elp = this.el.findParent('.x-form-element', 5, true);
40888                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40889                 }
40890                 this.alignErrorIcon();
40891                 this.errorIcon.dom.qtip = msg;
40892                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40893                 this.errorIcon.show();
40894                 this.on('resize', this.alignErrorIcon, this);
40895                 break;
40896             default:
40897                 var t = Roo.getDom(this.msgTarget);
40898                 t.innerHTML = msg;
40899                 t.style.display = this.msgDisplay;
40900                 break;
40901         }
40902         this.fireEvent('invalid', this, msg);
40903     },
40904
40905     // private
40906     alignErrorIcon : function(){
40907         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40908     },
40909
40910     /**
40911      * Clear any invalid styles/messages for this field
40912      */
40913     clearInvalid : function(){
40914         if(!this.rendered || this.preventMark){ // not rendered
40915             return;
40916         }
40917         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40918         
40919         obj.el.removeClass(this.invalidClass);
40920         switch(this.msgTarget){
40921             case 'qtip':
40922                 obj.el.dom.qtip = '';
40923                 break;
40924             case 'title':
40925                 this.el.dom.title = '';
40926                 break;
40927             case 'under':
40928                 if(this.errorEl){
40929                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40930                 }
40931                 break;
40932             case 'side':
40933                 if(this.errorIcon){
40934                     this.errorIcon.dom.qtip = '';
40935                     this.errorIcon.hide();
40936                     this.un('resize', this.alignErrorIcon, this);
40937                 }
40938                 break;
40939             default:
40940                 var t = Roo.getDom(this.msgTarget);
40941                 t.innerHTML = '';
40942                 t.style.display = 'none';
40943                 break;
40944         }
40945         this.fireEvent('valid', this);
40946     },
40947
40948     /**
40949      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40950      * @return {Mixed} value The field value
40951      */
40952     getRawValue : function(){
40953         var v = this.el.getValue();
40954         
40955         return v;
40956     },
40957
40958     /**
40959      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40960      * @return {Mixed} value The field value
40961      */
40962     getValue : function(){
40963         var v = this.el.getValue();
40964          
40965         return v;
40966     },
40967
40968     /**
40969      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40970      * @param {Mixed} value The value to set
40971      */
40972     setRawValue : function(v){
40973         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40974     },
40975
40976     /**
40977      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40978      * @param {Mixed} value The value to set
40979      */
40980     setValue : function(v){
40981         this.value = v;
40982         if(this.rendered){
40983             this.el.dom.value = (v === null || v === undefined ? '' : v);
40984              this.validate();
40985         }
40986     },
40987
40988     adjustSize : function(w, h){
40989         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40990         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40991         return s;
40992     },
40993
40994     adjustWidth : function(tag, w){
40995         tag = tag.toLowerCase();
40996         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40997             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40998                 if(tag == 'input'){
40999                     return w + 2;
41000                 }
41001                 if(tag == 'textarea'){
41002                     return w-2;
41003                 }
41004             }else if(Roo.isOpera){
41005                 if(tag == 'input'){
41006                     return w + 2;
41007                 }
41008                 if(tag == 'textarea'){
41009                     return w-2;
41010                 }
41011             }
41012         }
41013         return w;
41014     }
41015 });
41016
41017
41018 // anything other than normal should be considered experimental
41019 Roo.form.Field.msgFx = {
41020     normal : {
41021         show: function(msgEl, f){
41022             msgEl.setDisplayed('block');
41023         },
41024
41025         hide : function(msgEl, f){
41026             msgEl.setDisplayed(false).update('');
41027         }
41028     },
41029
41030     slide : {
41031         show: function(msgEl, f){
41032             msgEl.slideIn('t', {stopFx:true});
41033         },
41034
41035         hide : function(msgEl, f){
41036             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
41037         }
41038     },
41039
41040     slideRight : {
41041         show: function(msgEl, f){
41042             msgEl.fixDisplay();
41043             msgEl.alignTo(f.el, 'tl-tr');
41044             msgEl.slideIn('l', {stopFx:true});
41045         },
41046
41047         hide : function(msgEl, f){
41048             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
41049         }
41050     }
41051 };/*
41052  * Based on:
41053  * Ext JS Library 1.1.1
41054  * Copyright(c) 2006-2007, Ext JS, LLC.
41055  *
41056  * Originally Released Under LGPL - original licence link has changed is not relivant.
41057  *
41058  * Fork - LGPL
41059  * <script type="text/javascript">
41060  */
41061  
41062
41063 /**
41064  * @class Roo.form.TextField
41065  * @extends Roo.form.Field
41066  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
41067  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
41068  * @constructor
41069  * Creates a new TextField
41070  * @param {Object} config Configuration options
41071  */
41072 Roo.form.TextField = function(config){
41073     Roo.form.TextField.superclass.constructor.call(this, config);
41074     this.addEvents({
41075         /**
41076          * @event autosize
41077          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
41078          * according to the default logic, but this event provides a hook for the developer to apply additional
41079          * logic at runtime to resize the field if needed.
41080              * @param {Roo.form.Field} this This text field
41081              * @param {Number} width The new field width
41082              */
41083         autosize : true
41084     });
41085 };
41086
41087 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
41088     /**
41089      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
41090      */
41091     grow : false,
41092     /**
41093      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
41094      */
41095     growMin : 30,
41096     /**
41097      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
41098      */
41099     growMax : 800,
41100     /**
41101      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
41102      */
41103     vtype : null,
41104     /**
41105      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
41106      */
41107     maskRe : null,
41108     /**
41109      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
41110      */
41111     disableKeyFilter : false,
41112     /**
41113      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
41114      */
41115     allowBlank : true,
41116     /**
41117      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
41118      */
41119     minLength : 0,
41120     /**
41121      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
41122      */
41123     maxLength : Number.MAX_VALUE,
41124     /**
41125      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
41126      */
41127     minLengthText : "The minimum length for this field is {0}",
41128     /**
41129      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
41130      */
41131     maxLengthText : "The maximum length for this field is {0}",
41132     /**
41133      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
41134      */
41135     selectOnFocus : false,
41136     /**
41137      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
41138      */    
41139     allowLeadingSpace : false,
41140     /**
41141      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
41142      */
41143     blankText : "This field is required",
41144     /**
41145      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
41146      * If available, this function will be called only after the basic validators all return true, and will be passed the
41147      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
41148      */
41149     validator : null,
41150     /**
41151      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
41152      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
41153      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
41154      */
41155     regex : null,
41156     /**
41157      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
41158      */
41159     regexText : "",
41160     /**
41161      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
41162      */
41163     emptyText : null,
41164    
41165
41166     // private
41167     initEvents : function()
41168     {
41169         if (this.emptyText) {
41170             this.el.attr('placeholder', this.emptyText);
41171         }
41172         
41173         Roo.form.TextField.superclass.initEvents.call(this);
41174         if(this.validationEvent == 'keyup'){
41175             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41176             this.el.on('keyup', this.filterValidation, this);
41177         }
41178         else if(this.validationEvent !== false){
41179             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41180         }
41181         
41182         if(this.selectOnFocus){
41183             this.on("focus", this.preFocus, this);
41184         }
41185         if (!this.allowLeadingSpace) {
41186             this.on('blur', this.cleanLeadingSpace, this);
41187         }
41188         
41189         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41190             this.el.on("keypress", this.filterKeys, this);
41191         }
41192         if(this.grow){
41193             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
41194             this.el.on("click", this.autoSize,  this);
41195         }
41196         if(this.el.is('input[type=password]') && Roo.isSafari){
41197             this.el.on('keydown', this.SafariOnKeyDown, this);
41198         }
41199     },
41200
41201     processValue : function(value){
41202         if(this.stripCharsRe){
41203             var newValue = value.replace(this.stripCharsRe, '');
41204             if(newValue !== value){
41205                 this.setRawValue(newValue);
41206                 return newValue;
41207             }
41208         }
41209         return value;
41210     },
41211
41212     filterValidation : function(e){
41213         if(!e.isNavKeyPress()){
41214             this.validationTask.delay(this.validationDelay);
41215         }
41216     },
41217
41218     // private
41219     onKeyUp : function(e){
41220         if(!e.isNavKeyPress()){
41221             this.autoSize();
41222         }
41223     },
41224     // private - clean the leading white space
41225     cleanLeadingSpace : function(e)
41226     {
41227         if ( this.inputType == 'file') {
41228             return;
41229         }
41230         
41231         this.setValue((this.getValue() + '').replace(/^\s+/,''));
41232     },
41233     /**
41234      * Resets the current field value to the originally-loaded value and clears any validation messages.
41235      *  
41236      */
41237     reset : function(){
41238         Roo.form.TextField.superclass.reset.call(this);
41239        
41240     }, 
41241     // private
41242     preFocus : function(){
41243         
41244         if(this.selectOnFocus){
41245             this.el.dom.select();
41246         }
41247     },
41248
41249     
41250     // private
41251     filterKeys : function(e){
41252         var k = e.getKey();
41253         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41254             return;
41255         }
41256         var c = e.getCharCode(), cc = String.fromCharCode(c);
41257         if(Roo.isIE && (e.isSpecialKey() || !cc)){
41258             return;
41259         }
41260         if(!this.maskRe.test(cc)){
41261             e.stopEvent();
41262         }
41263     },
41264
41265     setValue : function(v){
41266         
41267         Roo.form.TextField.superclass.setValue.apply(this, arguments);
41268         
41269         this.autoSize();
41270     },
41271
41272     /**
41273      * Validates a value according to the field's validation rules and marks the field as invalid
41274      * if the validation fails
41275      * @param {Mixed} value The value to validate
41276      * @return {Boolean} True if the value is valid, else false
41277      */
41278     validateValue : function(value){
41279         if(value.length < 1)  { // if it's blank
41280              if(this.allowBlank){
41281                 this.clearInvalid();
41282                 return true;
41283              }else{
41284                 this.markInvalid(this.blankText);
41285                 return false;
41286              }
41287         }
41288         if(value.length < this.minLength){
41289             this.markInvalid(String.format(this.minLengthText, this.minLength));
41290             return false;
41291         }
41292         if(value.length > this.maxLength){
41293             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41294             return false;
41295         }
41296         if(this.vtype){
41297             var vt = Roo.form.VTypes;
41298             if(!vt[this.vtype](value, this)){
41299                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41300                 return false;
41301             }
41302         }
41303         if(typeof this.validator == "function"){
41304             var msg = this.validator(value);
41305             if(msg !== true){
41306                 this.markInvalid(msg);
41307                 return false;
41308             }
41309         }
41310         if(this.regex && !this.regex.test(value)){
41311             this.markInvalid(this.regexText);
41312             return false;
41313         }
41314         return true;
41315     },
41316
41317     /**
41318      * Selects text in this field
41319      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41320      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41321      */
41322     selectText : function(start, end){
41323         var v = this.getRawValue();
41324         if(v.length > 0){
41325             start = start === undefined ? 0 : start;
41326             end = end === undefined ? v.length : end;
41327             var d = this.el.dom;
41328             if(d.setSelectionRange){
41329                 d.setSelectionRange(start, end);
41330             }else if(d.createTextRange){
41331                 var range = d.createTextRange();
41332                 range.moveStart("character", start);
41333                 range.moveEnd("character", v.length-end);
41334                 range.select();
41335             }
41336         }
41337     },
41338
41339     /**
41340      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41341      * This only takes effect if grow = true, and fires the autosize event.
41342      */
41343     autoSize : function(){
41344         if(!this.grow || !this.rendered){
41345             return;
41346         }
41347         if(!this.metrics){
41348             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41349         }
41350         var el = this.el;
41351         var v = el.dom.value;
41352         var d = document.createElement('div');
41353         d.appendChild(document.createTextNode(v));
41354         v = d.innerHTML;
41355         d = null;
41356         v += "&#160;";
41357         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41358         this.el.setWidth(w);
41359         this.fireEvent("autosize", this, w);
41360     },
41361     
41362     // private
41363     SafariOnKeyDown : function(event)
41364     {
41365         // this is a workaround for a password hang bug on chrome/ webkit.
41366         
41367         var isSelectAll = false;
41368         
41369         if(this.el.dom.selectionEnd > 0){
41370             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41371         }
41372         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41373             event.preventDefault();
41374             this.setValue('');
41375             return;
41376         }
41377         
41378         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41379             
41380             event.preventDefault();
41381             // this is very hacky as keydown always get's upper case.
41382             
41383             var cc = String.fromCharCode(event.getCharCode());
41384             
41385             
41386             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41387             
41388         }
41389         
41390         
41391     }
41392 });/*
41393  * Based on:
41394  * Ext JS Library 1.1.1
41395  * Copyright(c) 2006-2007, Ext JS, LLC.
41396  *
41397  * Originally Released Under LGPL - original licence link has changed is not relivant.
41398  *
41399  * Fork - LGPL
41400  * <script type="text/javascript">
41401  */
41402  
41403 /**
41404  * @class Roo.form.Hidden
41405  * @extends Roo.form.TextField
41406  * Simple Hidden element used on forms 
41407  * 
41408  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41409  * 
41410  * @constructor
41411  * Creates a new Hidden form element.
41412  * @param {Object} config Configuration options
41413  */
41414
41415
41416
41417 // easy hidden field...
41418 Roo.form.Hidden = function(config){
41419     Roo.form.Hidden.superclass.constructor.call(this, config);
41420 };
41421   
41422 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41423     fieldLabel:      '',
41424     inputType:      'hidden',
41425     width:          50,
41426     allowBlank:     true,
41427     labelSeparator: '',
41428     hidden:         true,
41429     itemCls :       'x-form-item-display-none'
41430
41431
41432 });
41433
41434
41435 /*
41436  * Based on:
41437  * Ext JS Library 1.1.1
41438  * Copyright(c) 2006-2007, Ext JS, LLC.
41439  *
41440  * Originally Released Under LGPL - original licence link has changed is not relivant.
41441  *
41442  * Fork - LGPL
41443  * <script type="text/javascript">
41444  */
41445  
41446 /**
41447  * @class Roo.form.TriggerField
41448  * @extends Roo.form.TextField
41449  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41450  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41451  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41452  * for which you can provide a custom implementation.  For example:
41453  * <pre><code>
41454 var trigger = new Roo.form.TriggerField();
41455 trigger.onTriggerClick = myTriggerFn;
41456 trigger.applyTo('my-field');
41457 </code></pre>
41458  *
41459  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41460  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41461  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41462  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41463  * @constructor
41464  * Create a new TriggerField.
41465  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41466  * to the base TextField)
41467  */
41468 Roo.form.TriggerField = function(config){
41469     this.mimicing = false;
41470     Roo.form.TriggerField.superclass.constructor.call(this, config);
41471 };
41472
41473 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41474     /**
41475      * @cfg {String} triggerClass A CSS class to apply to the trigger
41476      */
41477     /**
41478      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41479      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41480      */
41481     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41482     /**
41483      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41484      */
41485     hideTrigger:false,
41486
41487     /** @cfg {Boolean} grow @hide */
41488     /** @cfg {Number} growMin @hide */
41489     /** @cfg {Number} growMax @hide */
41490
41491     /**
41492      * @hide 
41493      * @method
41494      */
41495     autoSize: Roo.emptyFn,
41496     // private
41497     monitorTab : true,
41498     // private
41499     deferHeight : true,
41500
41501     
41502     actionMode : 'wrap',
41503     // private
41504     onResize : function(w, h){
41505         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41506         if(typeof w == 'number'){
41507             var x = w - this.trigger.getWidth();
41508             this.el.setWidth(this.adjustWidth('input', x));
41509             this.trigger.setStyle('left', x+'px');
41510         }
41511     },
41512
41513     // private
41514     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41515
41516     // private
41517     getResizeEl : function(){
41518         return this.wrap;
41519     },
41520
41521     // private
41522     getPositionEl : function(){
41523         return this.wrap;
41524     },
41525
41526     // private
41527     alignErrorIcon : function(){
41528         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41529     },
41530
41531     // private
41532     onRender : function(ct, position){
41533         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41534         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41535         this.trigger = this.wrap.createChild(this.triggerConfig ||
41536                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41537         if(this.hideTrigger){
41538             this.trigger.setDisplayed(false);
41539         }
41540         this.initTrigger();
41541         if(!this.width){
41542             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41543         }
41544     },
41545
41546     // private
41547     initTrigger : function(){
41548         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41549         this.trigger.addClassOnOver('x-form-trigger-over');
41550         this.trigger.addClassOnClick('x-form-trigger-click');
41551     },
41552
41553     // private
41554     onDestroy : function(){
41555         if(this.trigger){
41556             this.trigger.removeAllListeners();
41557             this.trigger.remove();
41558         }
41559         if(this.wrap){
41560             this.wrap.remove();
41561         }
41562         Roo.form.TriggerField.superclass.onDestroy.call(this);
41563     },
41564
41565     // private
41566     onFocus : function(){
41567         Roo.form.TriggerField.superclass.onFocus.call(this);
41568         if(!this.mimicing){
41569             this.wrap.addClass('x-trigger-wrap-focus');
41570             this.mimicing = true;
41571             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41572             if(this.monitorTab){
41573                 this.el.on("keydown", this.checkTab, this);
41574             }
41575         }
41576     },
41577
41578     // private
41579     checkTab : function(e){
41580         if(e.getKey() == e.TAB){
41581             this.triggerBlur();
41582         }
41583     },
41584
41585     // private
41586     onBlur : function(){
41587         // do nothing
41588     },
41589
41590     // private
41591     mimicBlur : function(e, t){
41592         if(!this.wrap.contains(t) && this.validateBlur()){
41593             this.triggerBlur();
41594         }
41595     },
41596
41597     // private
41598     triggerBlur : function(){
41599         this.mimicing = false;
41600         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41601         if(this.monitorTab){
41602             this.el.un("keydown", this.checkTab, this);
41603         }
41604         this.wrap.removeClass('x-trigger-wrap-focus');
41605         Roo.form.TriggerField.superclass.onBlur.call(this);
41606     },
41607
41608     // private
41609     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41610     validateBlur : function(e, t){
41611         return true;
41612     },
41613
41614     // private
41615     onDisable : function(){
41616         Roo.form.TriggerField.superclass.onDisable.call(this);
41617         if(this.wrap){
41618             this.wrap.addClass('x-item-disabled');
41619         }
41620     },
41621
41622     // private
41623     onEnable : function(){
41624         Roo.form.TriggerField.superclass.onEnable.call(this);
41625         if(this.wrap){
41626             this.wrap.removeClass('x-item-disabled');
41627         }
41628     },
41629
41630     // private
41631     onShow : function(){
41632         var ae = this.getActionEl();
41633         
41634         if(ae){
41635             ae.dom.style.display = '';
41636             ae.dom.style.visibility = 'visible';
41637         }
41638     },
41639
41640     // private
41641     
41642     onHide : function(){
41643         var ae = this.getActionEl();
41644         ae.dom.style.display = 'none';
41645     },
41646
41647     /**
41648      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41649      * by an implementing function.
41650      * @method
41651      * @param {EventObject} e
41652      */
41653     onTriggerClick : Roo.emptyFn
41654 });
41655
41656 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41657 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41658 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41659 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41660     initComponent : function(){
41661         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41662
41663         this.triggerConfig = {
41664             tag:'span', cls:'x-form-twin-triggers', cn:[
41665             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41666             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41667         ]};
41668     },
41669
41670     getTrigger : function(index){
41671         return this.triggers[index];
41672     },
41673
41674     initTrigger : function(){
41675         var ts = this.trigger.select('.x-form-trigger', true);
41676         this.wrap.setStyle('overflow', 'hidden');
41677         var triggerField = this;
41678         ts.each(function(t, all, index){
41679             t.hide = function(){
41680                 var w = triggerField.wrap.getWidth();
41681                 this.dom.style.display = 'none';
41682                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41683             };
41684             t.show = function(){
41685                 var w = triggerField.wrap.getWidth();
41686                 this.dom.style.display = '';
41687                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41688             };
41689             var triggerIndex = 'Trigger'+(index+1);
41690
41691             if(this['hide'+triggerIndex]){
41692                 t.dom.style.display = 'none';
41693             }
41694             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41695             t.addClassOnOver('x-form-trigger-over');
41696             t.addClassOnClick('x-form-trigger-click');
41697         }, this);
41698         this.triggers = ts.elements;
41699     },
41700
41701     onTrigger1Click : Roo.emptyFn,
41702     onTrigger2Click : Roo.emptyFn
41703 });/*
41704  * Based on:
41705  * Ext JS Library 1.1.1
41706  * Copyright(c) 2006-2007, Ext JS, LLC.
41707  *
41708  * Originally Released Under LGPL - original licence link has changed is not relivant.
41709  *
41710  * Fork - LGPL
41711  * <script type="text/javascript">
41712  */
41713  
41714 /**
41715  * @class Roo.form.TextArea
41716  * @extends Roo.form.TextField
41717  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41718  * support for auto-sizing.
41719  * @constructor
41720  * Creates a new TextArea
41721  * @param {Object} config Configuration options
41722  */
41723 Roo.form.TextArea = function(config){
41724     Roo.form.TextArea.superclass.constructor.call(this, config);
41725     // these are provided exchanges for backwards compat
41726     // minHeight/maxHeight were replaced by growMin/growMax to be
41727     // compatible with TextField growing config values
41728     if(this.minHeight !== undefined){
41729         this.growMin = this.minHeight;
41730     }
41731     if(this.maxHeight !== undefined){
41732         this.growMax = this.maxHeight;
41733     }
41734 };
41735
41736 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41737     /**
41738      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41739      */
41740     growMin : 60,
41741     /**
41742      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41743      */
41744     growMax: 1000,
41745     /**
41746      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41747      * in the field (equivalent to setting overflow: hidden, defaults to false)
41748      */
41749     preventScrollbars: false,
41750     /**
41751      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41752      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41753      */
41754
41755     // private
41756     onRender : function(ct, position){
41757         if(!this.el){
41758             this.defaultAutoCreate = {
41759                 tag: "textarea",
41760                 style:"width:300px;height:60px;",
41761                 autocomplete: "new-password"
41762             };
41763         }
41764         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41765         if(this.grow){
41766             this.textSizeEl = Roo.DomHelper.append(document.body, {
41767                 tag: "pre", cls: "x-form-grow-sizer"
41768             });
41769             if(this.preventScrollbars){
41770                 this.el.setStyle("overflow", "hidden");
41771             }
41772             this.el.setHeight(this.growMin);
41773         }
41774     },
41775
41776     onDestroy : function(){
41777         if(this.textSizeEl){
41778             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41779         }
41780         Roo.form.TextArea.superclass.onDestroy.call(this);
41781     },
41782
41783     // private
41784     onKeyUp : function(e){
41785         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41786             this.autoSize();
41787         }
41788     },
41789
41790     /**
41791      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41792      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41793      */
41794     autoSize : function(){
41795         if(!this.grow || !this.textSizeEl){
41796             return;
41797         }
41798         var el = this.el;
41799         var v = el.dom.value;
41800         var ts = this.textSizeEl;
41801
41802         ts.innerHTML = '';
41803         ts.appendChild(document.createTextNode(v));
41804         v = ts.innerHTML;
41805
41806         Roo.fly(ts).setWidth(this.el.getWidth());
41807         if(v.length < 1){
41808             v = "&#160;&#160;";
41809         }else{
41810             if(Roo.isIE){
41811                 v = v.replace(/\n/g, '<p>&#160;</p>');
41812             }
41813             v += "&#160;\n&#160;";
41814         }
41815         ts.innerHTML = v;
41816         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41817         if(h != this.lastHeight){
41818             this.lastHeight = h;
41819             this.el.setHeight(h);
41820             this.fireEvent("autosize", this, h);
41821         }
41822     }
41823 });/*
41824  * Based on:
41825  * Ext JS Library 1.1.1
41826  * Copyright(c) 2006-2007, Ext JS, LLC.
41827  *
41828  * Originally Released Under LGPL - original licence link has changed is not relivant.
41829  *
41830  * Fork - LGPL
41831  * <script type="text/javascript">
41832  */
41833  
41834
41835 /**
41836  * @class Roo.form.NumberField
41837  * @extends Roo.form.TextField
41838  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41839  * @constructor
41840  * Creates a new NumberField
41841  * @param {Object} config Configuration options
41842  */
41843 Roo.form.NumberField = function(config){
41844     Roo.form.NumberField.superclass.constructor.call(this, config);
41845 };
41846
41847 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41848     /**
41849      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41850      */
41851     fieldClass: "x-form-field x-form-num-field",
41852     /**
41853      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41854      */
41855     allowDecimals : true,
41856     /**
41857      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41858      */
41859     decimalSeparator : ".",
41860     /**
41861      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41862      */
41863     decimalPrecision : 2,
41864     /**
41865      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41866      */
41867     allowNegative : true,
41868     /**
41869      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41870      */
41871     minValue : Number.NEGATIVE_INFINITY,
41872     /**
41873      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41874      */
41875     maxValue : Number.MAX_VALUE,
41876     /**
41877      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41878      */
41879     minText : "The minimum value for this field is {0}",
41880     /**
41881      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41882      */
41883     maxText : "The maximum value for this field is {0}",
41884     /**
41885      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41886      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41887      */
41888     nanText : "{0} is not a valid number",
41889
41890     // private
41891     initEvents : function(){
41892         Roo.form.NumberField.superclass.initEvents.call(this);
41893         var allowed = "0123456789";
41894         if(this.allowDecimals){
41895             allowed += this.decimalSeparator;
41896         }
41897         if(this.allowNegative){
41898             allowed += "-";
41899         }
41900         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41901         var keyPress = function(e){
41902             var k = e.getKey();
41903             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41904                 return;
41905             }
41906             var c = e.getCharCode();
41907             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41908                 e.stopEvent();
41909             }
41910         };
41911         this.el.on("keypress", keyPress, this);
41912     },
41913
41914     // private
41915     validateValue : function(value){
41916         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41917             return false;
41918         }
41919         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41920              return true;
41921         }
41922         var num = this.parseValue(value);
41923         if(isNaN(num)){
41924             this.markInvalid(String.format(this.nanText, value));
41925             return false;
41926         }
41927         if(num < this.minValue){
41928             this.markInvalid(String.format(this.minText, this.minValue));
41929             return false;
41930         }
41931         if(num > this.maxValue){
41932             this.markInvalid(String.format(this.maxText, this.maxValue));
41933             return false;
41934         }
41935         return true;
41936     },
41937
41938     getValue : function(){
41939         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41940     },
41941
41942     // private
41943     parseValue : function(value){
41944         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41945         return isNaN(value) ? '' : value;
41946     },
41947
41948     // private
41949     fixPrecision : function(value){
41950         var nan = isNaN(value);
41951         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41952             return nan ? '' : value;
41953         }
41954         return parseFloat(value).toFixed(this.decimalPrecision);
41955     },
41956
41957     setValue : function(v){
41958         v = this.fixPrecision(v);
41959         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41960     },
41961
41962     // private
41963     decimalPrecisionFcn : function(v){
41964         return Math.floor(v);
41965     },
41966
41967     beforeBlur : function(){
41968         var v = this.parseValue(this.getRawValue());
41969         if(v){
41970             this.setValue(v);
41971         }
41972     }
41973 });/*
41974  * Based on:
41975  * Ext JS Library 1.1.1
41976  * Copyright(c) 2006-2007, Ext JS, LLC.
41977  *
41978  * Originally Released Under LGPL - original licence link has changed is not relivant.
41979  *
41980  * Fork - LGPL
41981  * <script type="text/javascript">
41982  */
41983  
41984 /**
41985  * @class Roo.form.DateField
41986  * @extends Roo.form.TriggerField
41987  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41988 * @constructor
41989 * Create a new DateField
41990 * @param {Object} config
41991  */
41992 Roo.form.DateField = function(config)
41993 {
41994     Roo.form.DateField.superclass.constructor.call(this, config);
41995     
41996       this.addEvents({
41997          
41998         /**
41999          * @event select
42000          * Fires when a date is selected
42001              * @param {Roo.form.DateField} combo This combo box
42002              * @param {Date} date The date selected
42003              */
42004         'select' : true
42005          
42006     });
42007     
42008     
42009     if(typeof this.minValue == "string") {
42010         this.minValue = this.parseDate(this.minValue);
42011     }
42012     if(typeof this.maxValue == "string") {
42013         this.maxValue = this.parseDate(this.maxValue);
42014     }
42015     this.ddMatch = null;
42016     if(this.disabledDates){
42017         var dd = this.disabledDates;
42018         var re = "(?:";
42019         for(var i = 0; i < dd.length; i++){
42020             re += dd[i];
42021             if(i != dd.length-1) {
42022                 re += "|";
42023             }
42024         }
42025         this.ddMatch = new RegExp(re + ")");
42026     }
42027 };
42028
42029 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
42030     /**
42031      * @cfg {String} format
42032      * The default date format string which can be overriden for localization support.  The format must be
42033      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42034      */
42035     format : "m/d/y",
42036     /**
42037      * @cfg {String} altFormats
42038      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42039      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42040      */
42041     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
42042     /**
42043      * @cfg {Array} disabledDays
42044      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42045      */
42046     disabledDays : null,
42047     /**
42048      * @cfg {String} disabledDaysText
42049      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42050      */
42051     disabledDaysText : "Disabled",
42052     /**
42053      * @cfg {Array} disabledDates
42054      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42055      * expression so they are very powerful. Some examples:
42056      * <ul>
42057      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42058      * <li>["03/08", "09/16"] would disable those days for every year</li>
42059      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42060      * <li>["03/../2006"] would disable every day in March 2006</li>
42061      * <li>["^03"] would disable every day in every March</li>
42062      * </ul>
42063      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42064      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42065      */
42066     disabledDates : null,
42067     /**
42068      * @cfg {String} disabledDatesText
42069      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42070      */
42071     disabledDatesText : "Disabled",
42072         
42073         
42074         /**
42075      * @cfg {Date/String} zeroValue
42076      * if the date is less that this number, then the field is rendered as empty
42077      * default is 1800
42078      */
42079         zeroValue : '1800-01-01',
42080         
42081         
42082     /**
42083      * @cfg {Date/String} minValue
42084      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42085      * valid format (defaults to null).
42086      */
42087     minValue : null,
42088     /**
42089      * @cfg {Date/String} maxValue
42090      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42091      * valid format (defaults to null).
42092      */
42093     maxValue : null,
42094     /**
42095      * @cfg {String} minText
42096      * The error text to display when the date in the cell is before minValue (defaults to
42097      * 'The date in this field must be after {minValue}').
42098      */
42099     minText : "The date in this field must be equal to or after {0}",
42100     /**
42101      * @cfg {String} maxText
42102      * The error text to display when the date in the cell is after maxValue (defaults to
42103      * 'The date in this field must be before {maxValue}').
42104      */
42105     maxText : "The date in this field must be equal to or before {0}",
42106     /**
42107      * @cfg {String} invalidText
42108      * The error text to display when the date in the field is invalid (defaults to
42109      * '{value} is not a valid date - it must be in the format {format}').
42110      */
42111     invalidText : "{0} is not a valid date - it must be in the format {1}",
42112     /**
42113      * @cfg {String} triggerClass
42114      * An additional CSS class used to style the trigger button.  The trigger will always get the
42115      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42116      * which displays a calendar icon).
42117      */
42118     triggerClass : 'x-form-date-trigger',
42119     
42120
42121     /**
42122      * @cfg {Boolean} useIso
42123      * if enabled, then the date field will use a hidden field to store the 
42124      * real value as iso formated date. default (false)
42125      */ 
42126     useIso : false,
42127     /**
42128      * @cfg {String/Object} autoCreate
42129      * A DomHelper element spec, or true for a default element spec (defaults to
42130      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42131      */ 
42132     // private
42133     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
42134     
42135     // private
42136     hiddenField: false,
42137     
42138     onRender : function(ct, position)
42139     {
42140         Roo.form.DateField.superclass.onRender.call(this, ct, position);
42141         if (this.useIso) {
42142             //this.el.dom.removeAttribute('name'); 
42143             Roo.log("Changing name?");
42144             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
42145             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42146                     'before', true);
42147             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42148             // prevent input submission
42149             this.hiddenName = this.name;
42150         }
42151             
42152             
42153     },
42154     
42155     // private
42156     validateValue : function(value)
42157     {
42158         value = this.formatDate(value);
42159         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
42160             Roo.log('super failed');
42161             return false;
42162         }
42163         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42164              return true;
42165         }
42166         var svalue = value;
42167         value = this.parseDate(value);
42168         if(!value){
42169             Roo.log('parse date failed' + svalue);
42170             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42171             return false;
42172         }
42173         var time = value.getTime();
42174         if(this.minValue && time < this.minValue.getTime()){
42175             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42176             return false;
42177         }
42178         if(this.maxValue && time > this.maxValue.getTime()){
42179             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42180             return false;
42181         }
42182         if(this.disabledDays){
42183             var day = value.getDay();
42184             for(var i = 0; i < this.disabledDays.length; i++) {
42185                 if(day === this.disabledDays[i]){
42186                     this.markInvalid(this.disabledDaysText);
42187                     return false;
42188                 }
42189             }
42190         }
42191         var fvalue = this.formatDate(value);
42192         if(this.ddMatch && this.ddMatch.test(fvalue)){
42193             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42194             return false;
42195         }
42196         return true;
42197     },
42198
42199     // private
42200     // Provides logic to override the default TriggerField.validateBlur which just returns true
42201     validateBlur : function(){
42202         return !this.menu || !this.menu.isVisible();
42203     },
42204     
42205     getName: function()
42206     {
42207         // returns hidden if it's set..
42208         if (!this.rendered) {return ''};
42209         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42210         
42211     },
42212
42213     /**
42214      * Returns the current date value of the date field.
42215      * @return {Date} The date value
42216      */
42217     getValue : function(){
42218         
42219         return  this.hiddenField ?
42220                 this.hiddenField.value :
42221                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42222     },
42223
42224     /**
42225      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42226      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42227      * (the default format used is "m/d/y").
42228      * <br />Usage:
42229      * <pre><code>
42230 //All of these calls set the same date value (May 4, 2006)
42231
42232 //Pass a date object:
42233 var dt = new Date('5/4/06');
42234 dateField.setValue(dt);
42235
42236 //Pass a date string (default format):
42237 dateField.setValue('5/4/06');
42238
42239 //Pass a date string (custom format):
42240 dateField.format = 'Y-m-d';
42241 dateField.setValue('2006-5-4');
42242 </code></pre>
42243      * @param {String/Date} date The date or valid date string
42244      */
42245     setValue : function(date){
42246         if (this.hiddenField) {
42247             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42248         }
42249         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42250         // make sure the value field is always stored as a date..
42251         this.value = this.parseDate(date);
42252         
42253         
42254     },
42255
42256     // private
42257     parseDate : function(value){
42258                 
42259                 if (value instanceof Date) {
42260                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42261                                 return  '';
42262                         }
42263                         return value;
42264                 }
42265                 
42266                 
42267         if(!value || value instanceof Date){
42268             return value;
42269         }
42270         var v = Date.parseDate(value, this.format);
42271          if (!v && this.useIso) {
42272             v = Date.parseDate(value, 'Y-m-d');
42273         }
42274         if(!v && this.altFormats){
42275             if(!this.altFormatsArray){
42276                 this.altFormatsArray = this.altFormats.split("|");
42277             }
42278             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42279                 v = Date.parseDate(value, this.altFormatsArray[i]);
42280             }
42281         }
42282                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42283                         v = '';
42284                 }
42285         return v;
42286     },
42287
42288     // private
42289     formatDate : function(date, fmt){
42290         return (!date || !(date instanceof Date)) ?
42291                date : date.dateFormat(fmt || this.format);
42292     },
42293
42294     // private
42295     menuListeners : {
42296         select: function(m, d){
42297             
42298             this.setValue(d);
42299             this.fireEvent('select', this, d);
42300         },
42301         show : function(){ // retain focus styling
42302             this.onFocus();
42303         },
42304         hide : function(){
42305             this.focus.defer(10, this);
42306             var ml = this.menuListeners;
42307             this.menu.un("select", ml.select,  this);
42308             this.menu.un("show", ml.show,  this);
42309             this.menu.un("hide", ml.hide,  this);
42310         }
42311     },
42312
42313     // private
42314     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42315     onTriggerClick : function(){
42316         if(this.disabled){
42317             return;
42318         }
42319         if(this.menu == null){
42320             this.menu = new Roo.menu.DateMenu();
42321         }
42322         Roo.apply(this.menu.picker,  {
42323             showClear: this.allowBlank,
42324             minDate : this.minValue,
42325             maxDate : this.maxValue,
42326             disabledDatesRE : this.ddMatch,
42327             disabledDatesText : this.disabledDatesText,
42328             disabledDays : this.disabledDays,
42329             disabledDaysText : this.disabledDaysText,
42330             format : this.useIso ? 'Y-m-d' : this.format,
42331             minText : String.format(this.minText, this.formatDate(this.minValue)),
42332             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42333         });
42334         this.menu.on(Roo.apply({}, this.menuListeners, {
42335             scope:this
42336         }));
42337         this.menu.picker.setValue(this.getValue() || new Date());
42338         this.menu.show(this.el, "tl-bl?");
42339     },
42340
42341     beforeBlur : function(){
42342         var v = this.parseDate(this.getRawValue());
42343         if(v){
42344             this.setValue(v);
42345         }
42346     },
42347
42348     /*@
42349      * overide
42350      * 
42351      */
42352     isDirty : function() {
42353         if(this.disabled) {
42354             return false;
42355         }
42356         
42357         if(typeof(this.startValue) === 'undefined'){
42358             return false;
42359         }
42360         
42361         return String(this.getValue()) !== String(this.startValue);
42362         
42363     },
42364     // @overide
42365     cleanLeadingSpace : function(e)
42366     {
42367        return;
42368     }
42369     
42370 });/*
42371  * Based on:
42372  * Ext JS Library 1.1.1
42373  * Copyright(c) 2006-2007, Ext JS, LLC.
42374  *
42375  * Originally Released Under LGPL - original licence link has changed is not relivant.
42376  *
42377  * Fork - LGPL
42378  * <script type="text/javascript">
42379  */
42380  
42381 /**
42382  * @class Roo.form.MonthField
42383  * @extends Roo.form.TriggerField
42384  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42385 * @constructor
42386 * Create a new MonthField
42387 * @param {Object} config
42388  */
42389 Roo.form.MonthField = function(config){
42390     
42391     Roo.form.MonthField.superclass.constructor.call(this, config);
42392     
42393       this.addEvents({
42394          
42395         /**
42396          * @event select
42397          * Fires when a date is selected
42398              * @param {Roo.form.MonthFieeld} combo This combo box
42399              * @param {Date} date The date selected
42400              */
42401         'select' : true
42402          
42403     });
42404     
42405     
42406     if(typeof this.minValue == "string") {
42407         this.minValue = this.parseDate(this.minValue);
42408     }
42409     if(typeof this.maxValue == "string") {
42410         this.maxValue = this.parseDate(this.maxValue);
42411     }
42412     this.ddMatch = null;
42413     if(this.disabledDates){
42414         var dd = this.disabledDates;
42415         var re = "(?:";
42416         for(var i = 0; i < dd.length; i++){
42417             re += dd[i];
42418             if(i != dd.length-1) {
42419                 re += "|";
42420             }
42421         }
42422         this.ddMatch = new RegExp(re + ")");
42423     }
42424 };
42425
42426 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42427     /**
42428      * @cfg {String} format
42429      * The default date format string which can be overriden for localization support.  The format must be
42430      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42431      */
42432     format : "M Y",
42433     /**
42434      * @cfg {String} altFormats
42435      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42436      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42437      */
42438     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42439     /**
42440      * @cfg {Array} disabledDays
42441      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42442      */
42443     disabledDays : [0,1,2,3,4,5,6],
42444     /**
42445      * @cfg {String} disabledDaysText
42446      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42447      */
42448     disabledDaysText : "Disabled",
42449     /**
42450      * @cfg {Array} disabledDates
42451      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42452      * expression so they are very powerful. Some examples:
42453      * <ul>
42454      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42455      * <li>["03/08", "09/16"] would disable those days for every year</li>
42456      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42457      * <li>["03/../2006"] would disable every day in March 2006</li>
42458      * <li>["^03"] would disable every day in every March</li>
42459      * </ul>
42460      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42461      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42462      */
42463     disabledDates : null,
42464     /**
42465      * @cfg {String} disabledDatesText
42466      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42467      */
42468     disabledDatesText : "Disabled",
42469     /**
42470      * @cfg {Date/String} minValue
42471      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42472      * valid format (defaults to null).
42473      */
42474     minValue : null,
42475     /**
42476      * @cfg {Date/String} maxValue
42477      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42478      * valid format (defaults to null).
42479      */
42480     maxValue : null,
42481     /**
42482      * @cfg {String} minText
42483      * The error text to display when the date in the cell is before minValue (defaults to
42484      * 'The date in this field must be after {minValue}').
42485      */
42486     minText : "The date in this field must be equal to or after {0}",
42487     /**
42488      * @cfg {String} maxTextf
42489      * The error text to display when the date in the cell is after maxValue (defaults to
42490      * 'The date in this field must be before {maxValue}').
42491      */
42492     maxText : "The date in this field must be equal to or before {0}",
42493     /**
42494      * @cfg {String} invalidText
42495      * The error text to display when the date in the field is invalid (defaults to
42496      * '{value} is not a valid date - it must be in the format {format}').
42497      */
42498     invalidText : "{0} is not a valid date - it must be in the format {1}",
42499     /**
42500      * @cfg {String} triggerClass
42501      * An additional CSS class used to style the trigger button.  The trigger will always get the
42502      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42503      * which displays a calendar icon).
42504      */
42505     triggerClass : 'x-form-date-trigger',
42506     
42507
42508     /**
42509      * @cfg {Boolean} useIso
42510      * if enabled, then the date field will use a hidden field to store the 
42511      * real value as iso formated date. default (true)
42512      */ 
42513     useIso : true,
42514     /**
42515      * @cfg {String/Object} autoCreate
42516      * A DomHelper element spec, or true for a default element spec (defaults to
42517      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42518      */ 
42519     // private
42520     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42521     
42522     // private
42523     hiddenField: false,
42524     
42525     hideMonthPicker : false,
42526     
42527     onRender : function(ct, position)
42528     {
42529         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42530         if (this.useIso) {
42531             this.el.dom.removeAttribute('name'); 
42532             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42533                     'before', true);
42534             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42535             // prevent input submission
42536             this.hiddenName = this.name;
42537         }
42538             
42539             
42540     },
42541     
42542     // private
42543     validateValue : function(value)
42544     {
42545         value = this.formatDate(value);
42546         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42547             return false;
42548         }
42549         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42550              return true;
42551         }
42552         var svalue = value;
42553         value = this.parseDate(value);
42554         if(!value){
42555             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42556             return false;
42557         }
42558         var time = value.getTime();
42559         if(this.minValue && time < this.minValue.getTime()){
42560             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42561             return false;
42562         }
42563         if(this.maxValue && time > this.maxValue.getTime()){
42564             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42565             return false;
42566         }
42567         /*if(this.disabledDays){
42568             var day = value.getDay();
42569             for(var i = 0; i < this.disabledDays.length; i++) {
42570                 if(day === this.disabledDays[i]){
42571                     this.markInvalid(this.disabledDaysText);
42572                     return false;
42573                 }
42574             }
42575         }
42576         */
42577         var fvalue = this.formatDate(value);
42578         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42579             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42580             return false;
42581         }
42582         */
42583         return true;
42584     },
42585
42586     // private
42587     // Provides logic to override the default TriggerField.validateBlur which just returns true
42588     validateBlur : function(){
42589         return !this.menu || !this.menu.isVisible();
42590     },
42591
42592     /**
42593      * Returns the current date value of the date field.
42594      * @return {Date} The date value
42595      */
42596     getValue : function(){
42597         
42598         
42599         
42600         return  this.hiddenField ?
42601                 this.hiddenField.value :
42602                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42603     },
42604
42605     /**
42606      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42607      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42608      * (the default format used is "m/d/y").
42609      * <br />Usage:
42610      * <pre><code>
42611 //All of these calls set the same date value (May 4, 2006)
42612
42613 //Pass a date object:
42614 var dt = new Date('5/4/06');
42615 monthField.setValue(dt);
42616
42617 //Pass a date string (default format):
42618 monthField.setValue('5/4/06');
42619
42620 //Pass a date string (custom format):
42621 monthField.format = 'Y-m-d';
42622 monthField.setValue('2006-5-4');
42623 </code></pre>
42624      * @param {String/Date} date The date or valid date string
42625      */
42626     setValue : function(date){
42627         Roo.log('month setValue' + date);
42628         // can only be first of month..
42629         
42630         var val = this.parseDate(date);
42631         
42632         if (this.hiddenField) {
42633             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42634         }
42635         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42636         this.value = this.parseDate(date);
42637     },
42638
42639     // private
42640     parseDate : function(value){
42641         if(!value || value instanceof Date){
42642             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42643             return value;
42644         }
42645         var v = Date.parseDate(value, this.format);
42646         if (!v && this.useIso) {
42647             v = Date.parseDate(value, 'Y-m-d');
42648         }
42649         if (v) {
42650             // 
42651             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42652         }
42653         
42654         
42655         if(!v && this.altFormats){
42656             if(!this.altFormatsArray){
42657                 this.altFormatsArray = this.altFormats.split("|");
42658             }
42659             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42660                 v = Date.parseDate(value, this.altFormatsArray[i]);
42661             }
42662         }
42663         return v;
42664     },
42665
42666     // private
42667     formatDate : function(date, fmt){
42668         return (!date || !(date instanceof Date)) ?
42669                date : date.dateFormat(fmt || this.format);
42670     },
42671
42672     // private
42673     menuListeners : {
42674         select: function(m, d){
42675             this.setValue(d);
42676             this.fireEvent('select', this, d);
42677         },
42678         show : function(){ // retain focus styling
42679             this.onFocus();
42680         },
42681         hide : function(){
42682             this.focus.defer(10, this);
42683             var ml = this.menuListeners;
42684             this.menu.un("select", ml.select,  this);
42685             this.menu.un("show", ml.show,  this);
42686             this.menu.un("hide", ml.hide,  this);
42687         }
42688     },
42689     // private
42690     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42691     onTriggerClick : function(){
42692         if(this.disabled){
42693             return;
42694         }
42695         if(this.menu == null){
42696             this.menu = new Roo.menu.DateMenu();
42697            
42698         }
42699         
42700         Roo.apply(this.menu.picker,  {
42701             
42702             showClear: this.allowBlank,
42703             minDate : this.minValue,
42704             maxDate : this.maxValue,
42705             disabledDatesRE : this.ddMatch,
42706             disabledDatesText : this.disabledDatesText,
42707             
42708             format : this.useIso ? 'Y-m-d' : this.format,
42709             minText : String.format(this.minText, this.formatDate(this.minValue)),
42710             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42711             
42712         });
42713          this.menu.on(Roo.apply({}, this.menuListeners, {
42714             scope:this
42715         }));
42716        
42717         
42718         var m = this.menu;
42719         var p = m.picker;
42720         
42721         // hide month picker get's called when we called by 'before hide';
42722         
42723         var ignorehide = true;
42724         p.hideMonthPicker  = function(disableAnim){
42725             if (ignorehide) {
42726                 return;
42727             }
42728              if(this.monthPicker){
42729                 Roo.log("hideMonthPicker called");
42730                 if(disableAnim === true){
42731                     this.monthPicker.hide();
42732                 }else{
42733                     this.monthPicker.slideOut('t', {duration:.2});
42734                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42735                     p.fireEvent("select", this, this.value);
42736                     m.hide();
42737                 }
42738             }
42739         }
42740         
42741         Roo.log('picker set value');
42742         Roo.log(this.getValue());
42743         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42744         m.show(this.el, 'tl-bl?');
42745         ignorehide  = false;
42746         // this will trigger hideMonthPicker..
42747         
42748         
42749         // hidden the day picker
42750         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42751         
42752         
42753         
42754       
42755         
42756         p.showMonthPicker.defer(100, p);
42757     
42758         
42759        
42760     },
42761
42762     beforeBlur : function(){
42763         var v = this.parseDate(this.getRawValue());
42764         if(v){
42765             this.setValue(v);
42766         }
42767     }
42768
42769     /** @cfg {Boolean} grow @hide */
42770     /** @cfg {Number} growMin @hide */
42771     /** @cfg {Number} growMax @hide */
42772     /**
42773      * @hide
42774      * @method autoSize
42775      */
42776 });/*
42777  * Based on:
42778  * Ext JS Library 1.1.1
42779  * Copyright(c) 2006-2007, Ext JS, LLC.
42780  *
42781  * Originally Released Under LGPL - original licence link has changed is not relivant.
42782  *
42783  * Fork - LGPL
42784  * <script type="text/javascript">
42785  */
42786  
42787
42788 /**
42789  * @class Roo.form.ComboBox
42790  * @extends Roo.form.TriggerField
42791  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42792  * @constructor
42793  * Create a new ComboBox.
42794  * @param {Object} config Configuration options
42795  */
42796 Roo.form.ComboBox = function(config){
42797     Roo.form.ComboBox.superclass.constructor.call(this, config);
42798     this.addEvents({
42799         /**
42800          * @event expand
42801          * Fires when the dropdown list is expanded
42802              * @param {Roo.form.ComboBox} combo This combo box
42803              */
42804         'expand' : true,
42805         /**
42806          * @event collapse
42807          * Fires when the dropdown list is collapsed
42808              * @param {Roo.form.ComboBox} combo This combo box
42809              */
42810         'collapse' : true,
42811         /**
42812          * @event beforeselect
42813          * Fires before a list item is selected. Return false to cancel the selection.
42814              * @param {Roo.form.ComboBox} combo This combo box
42815              * @param {Roo.data.Record} record The data record returned from the underlying store
42816              * @param {Number} index The index of the selected item in the dropdown list
42817              */
42818         'beforeselect' : true,
42819         /**
42820          * @event select
42821          * Fires when a list item is selected
42822              * @param {Roo.form.ComboBox} combo This combo box
42823              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42824              * @param {Number} index The index of the selected item in the dropdown list
42825              */
42826         'select' : true,
42827         /**
42828          * @event beforequery
42829          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42830          * The event object passed has these properties:
42831              * @param {Roo.form.ComboBox} combo This combo box
42832              * @param {String} query The query
42833              * @param {Boolean} forceAll true to force "all" query
42834              * @param {Boolean} cancel true to cancel the query
42835              * @param {Object} e The query event object
42836              */
42837         'beforequery': true,
42838          /**
42839          * @event add
42840          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42841              * @param {Roo.form.ComboBox} combo This combo box
42842              */
42843         'add' : true,
42844         /**
42845          * @event edit
42846          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42847              * @param {Roo.form.ComboBox} combo This combo box
42848              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42849              */
42850         'edit' : true
42851         
42852         
42853     });
42854     if(this.transform){
42855         this.allowDomMove = false;
42856         var s = Roo.getDom(this.transform);
42857         if(!this.hiddenName){
42858             this.hiddenName = s.name;
42859         }
42860         if(!this.store){
42861             this.mode = 'local';
42862             var d = [], opts = s.options;
42863             for(var i = 0, len = opts.length;i < len; i++){
42864                 var o = opts[i];
42865                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42866                 if(o.selected) {
42867                     this.value = value;
42868                 }
42869                 d.push([value, o.text]);
42870             }
42871             this.store = new Roo.data.SimpleStore({
42872                 'id': 0,
42873                 fields: ['value', 'text'],
42874                 data : d
42875             });
42876             this.valueField = 'value';
42877             this.displayField = 'text';
42878         }
42879         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42880         if(!this.lazyRender){
42881             this.target = true;
42882             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42883             s.parentNode.removeChild(s); // remove it
42884             this.render(this.el.parentNode);
42885         }else{
42886             s.parentNode.removeChild(s); // remove it
42887         }
42888
42889     }
42890     if (this.store) {
42891         this.store = Roo.factory(this.store, Roo.data);
42892     }
42893     
42894     this.selectedIndex = -1;
42895     if(this.mode == 'local'){
42896         if(config.queryDelay === undefined){
42897             this.queryDelay = 10;
42898         }
42899         if(config.minChars === undefined){
42900             this.minChars = 0;
42901         }
42902     }
42903 };
42904
42905 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42906     /**
42907      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42908      */
42909     /**
42910      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42911      * rendering into an Roo.Editor, defaults to false)
42912      */
42913     /**
42914      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42915      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42916      */
42917     /**
42918      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42919      */
42920     /**
42921      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42922      * the dropdown list (defaults to undefined, with no header element)
42923      */
42924
42925      /**
42926      * @cfg {String/Roo.Template} tpl The template to use to render the output
42927      */
42928      
42929     // private
42930     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42931     /**
42932      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42933      */
42934     listWidth: undefined,
42935     /**
42936      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42937      * mode = 'remote' or 'text' if mode = 'local')
42938      */
42939     displayField: undefined,
42940     /**
42941      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42942      * mode = 'remote' or 'value' if mode = 'local'). 
42943      * Note: use of a valueField requires the user make a selection
42944      * in order for a value to be mapped.
42945      */
42946     valueField: undefined,
42947     
42948     
42949     /**
42950      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42951      * field's data value (defaults to the underlying DOM element's name)
42952      */
42953     hiddenName: undefined,
42954     /**
42955      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42956      */
42957     listClass: '',
42958     /**
42959      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42960      */
42961     selectedClass: 'x-combo-selected',
42962     /**
42963      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42964      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42965      * which displays a downward arrow icon).
42966      */
42967     triggerClass : 'x-form-arrow-trigger',
42968     /**
42969      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42970      */
42971     shadow:'sides',
42972     /**
42973      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42974      * anchor positions (defaults to 'tl-bl')
42975      */
42976     listAlign: 'tl-bl?',
42977     /**
42978      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42979      */
42980     maxHeight: 300,
42981     /**
42982      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42983      * query specified by the allQuery config option (defaults to 'query')
42984      */
42985     triggerAction: 'query',
42986     /**
42987      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42988      * (defaults to 4, does not apply if editable = false)
42989      */
42990     minChars : 4,
42991     /**
42992      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42993      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42994      */
42995     typeAhead: false,
42996     /**
42997      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42998      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42999      */
43000     queryDelay: 500,
43001     /**
43002      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
43003      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
43004      */
43005     pageSize: 0,
43006     /**
43007      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
43008      * when editable = true (defaults to false)
43009      */
43010     selectOnFocus:false,
43011     /**
43012      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
43013      */
43014     queryParam: 'query',
43015     /**
43016      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
43017      * when mode = 'remote' (defaults to 'Loading...')
43018      */
43019     loadingText: 'Loading...',
43020     /**
43021      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
43022      */
43023     resizable: false,
43024     /**
43025      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
43026      */
43027     handleHeight : 8,
43028     /**
43029      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
43030      * traditional select (defaults to true)
43031      */
43032     editable: true,
43033     /**
43034      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
43035      */
43036     allQuery: '',
43037     /**
43038      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
43039      */
43040     mode: 'remote',
43041     /**
43042      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
43043      * listWidth has a higher value)
43044      */
43045     minListWidth : 70,
43046     /**
43047      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
43048      * allow the user to set arbitrary text into the field (defaults to false)
43049      */
43050     forceSelection:false,
43051     /**
43052      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
43053      * if typeAhead = true (defaults to 250)
43054      */
43055     typeAheadDelay : 250,
43056     /**
43057      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
43058      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
43059      */
43060     valueNotFoundText : undefined,
43061     /**
43062      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
43063      */
43064     blockFocus : false,
43065     
43066     /**
43067      * @cfg {Boolean} disableClear Disable showing of clear button.
43068      */
43069     disableClear : false,
43070     /**
43071      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
43072      */
43073     alwaysQuery : false,
43074     
43075     //private
43076     addicon : false,
43077     editicon: false,
43078     
43079     // element that contains real text value.. (when hidden is used..)
43080      
43081     // private
43082     onRender : function(ct, position)
43083     {
43084         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
43085         
43086         if(this.hiddenName){
43087             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43088                     'before', true);
43089             this.hiddenField.value =
43090                 this.hiddenValue !== undefined ? this.hiddenValue :
43091                 this.value !== undefined ? this.value : '';
43092
43093             // prevent input submission
43094             this.el.dom.removeAttribute('name');
43095              
43096              
43097         }
43098         
43099         if(Roo.isGecko){
43100             this.el.dom.setAttribute('autocomplete', 'off');
43101         }
43102
43103         var cls = 'x-combo-list';
43104
43105         this.list = new Roo.Layer({
43106             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43107         });
43108
43109         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43110         this.list.setWidth(lw);
43111         this.list.swallowEvent('mousewheel');
43112         this.assetHeight = 0;
43113
43114         if(this.title){
43115             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43116             this.assetHeight += this.header.getHeight();
43117         }
43118
43119         this.innerList = this.list.createChild({cls:cls+'-inner'});
43120         this.innerList.on('mouseover', this.onViewOver, this);
43121         this.innerList.on('mousemove', this.onViewMove, this);
43122         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43123         
43124         if(this.allowBlank && !this.pageSize && !this.disableClear){
43125             this.footer = this.list.createChild({cls:cls+'-ft'});
43126             this.pageTb = new Roo.Toolbar(this.footer);
43127            
43128         }
43129         if(this.pageSize){
43130             this.footer = this.list.createChild({cls:cls+'-ft'});
43131             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
43132                     {pageSize: this.pageSize});
43133             
43134         }
43135         
43136         if (this.pageTb && this.allowBlank && !this.disableClear) {
43137             var _this = this;
43138             this.pageTb.add(new Roo.Toolbar.Fill(), {
43139                 cls: 'x-btn-icon x-btn-clear',
43140                 text: '&#160;',
43141                 handler: function()
43142                 {
43143                     _this.collapse();
43144                     _this.clearValue();
43145                     _this.onSelect(false, -1);
43146                 }
43147             });
43148         }
43149         if (this.footer) {
43150             this.assetHeight += this.footer.getHeight();
43151         }
43152         
43153
43154         if(!this.tpl){
43155             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
43156         }
43157
43158         this.view = new Roo.View(this.innerList, this.tpl, {
43159             singleSelect:true,
43160             store: this.store,
43161             selectedClass: this.selectedClass
43162         });
43163
43164         this.view.on('click', this.onViewClick, this);
43165
43166         this.store.on('beforeload', this.onBeforeLoad, this);
43167         this.store.on('load', this.onLoad, this);
43168         this.store.on('loadexception', this.onLoadException, this);
43169
43170         if(this.resizable){
43171             this.resizer = new Roo.Resizable(this.list,  {
43172                pinned:true, handles:'se'
43173             });
43174             this.resizer.on('resize', function(r, w, h){
43175                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
43176                 this.listWidth = w;
43177                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
43178                 this.restrictHeight();
43179             }, this);
43180             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
43181         }
43182         if(!this.editable){
43183             this.editable = true;
43184             this.setEditable(false);
43185         }  
43186         
43187         
43188         if (typeof(this.events.add.listeners) != 'undefined') {
43189             
43190             this.addicon = this.wrap.createChild(
43191                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
43192        
43193             this.addicon.on('click', function(e) {
43194                 this.fireEvent('add', this);
43195             }, this);
43196         }
43197         if (typeof(this.events.edit.listeners) != 'undefined') {
43198             
43199             this.editicon = this.wrap.createChild(
43200                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
43201             if (this.addicon) {
43202                 this.editicon.setStyle('margin-left', '40px');
43203             }
43204             this.editicon.on('click', function(e) {
43205                 
43206                 // we fire even  if inothing is selected..
43207                 this.fireEvent('edit', this, this.lastData );
43208                 
43209             }, this);
43210         }
43211         
43212         
43213         
43214     },
43215
43216     // private
43217     initEvents : function(){
43218         Roo.form.ComboBox.superclass.initEvents.call(this);
43219
43220         this.keyNav = new Roo.KeyNav(this.el, {
43221             "up" : function(e){
43222                 this.inKeyMode = true;
43223                 this.selectPrev();
43224             },
43225
43226             "down" : function(e){
43227                 if(!this.isExpanded()){
43228                     this.onTriggerClick();
43229                 }else{
43230                     this.inKeyMode = true;
43231                     this.selectNext();
43232                 }
43233             },
43234
43235             "enter" : function(e){
43236                 this.onViewClick();
43237                 //return true;
43238             },
43239
43240             "esc" : function(e){
43241                 this.collapse();
43242             },
43243
43244             "tab" : function(e){
43245                 this.onViewClick(false);
43246                 this.fireEvent("specialkey", this, e);
43247                 return true;
43248             },
43249
43250             scope : this,
43251
43252             doRelay : function(foo, bar, hname){
43253                 if(hname == 'down' || this.scope.isExpanded()){
43254                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43255                 }
43256                 return true;
43257             },
43258
43259             forceKeyDown: true
43260         });
43261         this.queryDelay = Math.max(this.queryDelay || 10,
43262                 this.mode == 'local' ? 10 : 250);
43263         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43264         if(this.typeAhead){
43265             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43266         }
43267         if(this.editable !== false){
43268             this.el.on("keyup", this.onKeyUp, this);
43269         }
43270         if(this.forceSelection){
43271             this.on('blur', this.doForce, this);
43272         }
43273     },
43274
43275     onDestroy : function(){
43276         if(this.view){
43277             this.view.setStore(null);
43278             this.view.el.removeAllListeners();
43279             this.view.el.remove();
43280             this.view.purgeListeners();
43281         }
43282         if(this.list){
43283             this.list.destroy();
43284         }
43285         if(this.store){
43286             this.store.un('beforeload', this.onBeforeLoad, this);
43287             this.store.un('load', this.onLoad, this);
43288             this.store.un('loadexception', this.onLoadException, this);
43289         }
43290         Roo.form.ComboBox.superclass.onDestroy.call(this);
43291     },
43292
43293     // private
43294     fireKey : function(e){
43295         if(e.isNavKeyPress() && !this.list.isVisible()){
43296             this.fireEvent("specialkey", this, e);
43297         }
43298     },
43299
43300     // private
43301     onResize: function(w, h){
43302         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43303         
43304         if(typeof w != 'number'){
43305             // we do not handle it!?!?
43306             return;
43307         }
43308         var tw = this.trigger.getWidth();
43309         tw += this.addicon ? this.addicon.getWidth() : 0;
43310         tw += this.editicon ? this.editicon.getWidth() : 0;
43311         var x = w - tw;
43312         this.el.setWidth( this.adjustWidth('input', x));
43313             
43314         this.trigger.setStyle('left', x+'px');
43315         
43316         if(this.list && this.listWidth === undefined){
43317             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43318             this.list.setWidth(lw);
43319             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43320         }
43321         
43322     
43323         
43324     },
43325
43326     /**
43327      * Allow or prevent the user from directly editing the field text.  If false is passed,
43328      * the user will only be able to select from the items defined in the dropdown list.  This method
43329      * is the runtime equivalent of setting the 'editable' config option at config time.
43330      * @param {Boolean} value True to allow the user to directly edit the field text
43331      */
43332     setEditable : function(value){
43333         if(value == this.editable){
43334             return;
43335         }
43336         this.editable = value;
43337         if(!value){
43338             this.el.dom.setAttribute('readOnly', true);
43339             this.el.on('mousedown', this.onTriggerClick,  this);
43340             this.el.addClass('x-combo-noedit');
43341         }else{
43342             this.el.dom.setAttribute('readOnly', false);
43343             this.el.un('mousedown', this.onTriggerClick,  this);
43344             this.el.removeClass('x-combo-noedit');
43345         }
43346     },
43347
43348     // private
43349     onBeforeLoad : function(){
43350         if(!this.hasFocus){
43351             return;
43352         }
43353         this.innerList.update(this.loadingText ?
43354                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43355         this.restrictHeight();
43356         this.selectedIndex = -1;
43357     },
43358
43359     // private
43360     onLoad : function(){
43361         if(!this.hasFocus){
43362             return;
43363         }
43364         if(this.store.getCount() > 0){
43365             this.expand();
43366             this.restrictHeight();
43367             if(this.lastQuery == this.allQuery){
43368                 if(this.editable){
43369                     this.el.dom.select();
43370                 }
43371                 if(!this.selectByValue(this.value, true)){
43372                     this.select(0, true);
43373                 }
43374             }else{
43375                 this.selectNext();
43376                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43377                     this.taTask.delay(this.typeAheadDelay);
43378                 }
43379             }
43380         }else{
43381             this.onEmptyResults();
43382         }
43383         //this.el.focus();
43384     },
43385     // private
43386     onLoadException : function()
43387     {
43388         this.collapse();
43389         Roo.log(this.store.reader.jsonData);
43390         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43391             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43392         }
43393         
43394         
43395     },
43396     // private
43397     onTypeAhead : function(){
43398         if(this.store.getCount() > 0){
43399             var r = this.store.getAt(0);
43400             var newValue = r.data[this.displayField];
43401             var len = newValue.length;
43402             var selStart = this.getRawValue().length;
43403             if(selStart != len){
43404                 this.setRawValue(newValue);
43405                 this.selectText(selStart, newValue.length);
43406             }
43407         }
43408     },
43409
43410     // private
43411     onSelect : function(record, index){
43412         if(this.fireEvent('beforeselect', this, record, index) !== false){
43413             this.setFromData(index > -1 ? record.data : false);
43414             this.collapse();
43415             this.fireEvent('select', this, record, index);
43416         }
43417     },
43418
43419     /**
43420      * Returns the currently selected field value or empty string if no value is set.
43421      * @return {String} value The selected value
43422      */
43423     getValue : function(){
43424         if(this.valueField){
43425             return typeof this.value != 'undefined' ? this.value : '';
43426         }
43427         return Roo.form.ComboBox.superclass.getValue.call(this);
43428     },
43429
43430     /**
43431      * Clears any text/value currently set in the field
43432      */
43433     clearValue : function(){
43434         if(this.hiddenField){
43435             this.hiddenField.value = '';
43436         }
43437         this.value = '';
43438         this.setRawValue('');
43439         this.lastSelectionText = '';
43440         
43441     },
43442
43443     /**
43444      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43445      * will be displayed in the field.  If the value does not match the data value of an existing item,
43446      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43447      * Otherwise the field will be blank (although the value will still be set).
43448      * @param {String} value The value to match
43449      */
43450     setValue : function(v){
43451         var text = v;
43452         if(this.valueField){
43453             var r = this.findRecord(this.valueField, v);
43454             if(r){
43455                 text = r.data[this.displayField];
43456             }else if(this.valueNotFoundText !== undefined){
43457                 text = this.valueNotFoundText;
43458             }
43459         }
43460         this.lastSelectionText = text;
43461         if(this.hiddenField){
43462             this.hiddenField.value = v;
43463         }
43464         Roo.form.ComboBox.superclass.setValue.call(this, text);
43465         this.value = v;
43466     },
43467     /**
43468      * @property {Object} the last set data for the element
43469      */
43470     
43471     lastData : false,
43472     /**
43473      * Sets the value of the field based on a object which is related to the record format for the store.
43474      * @param {Object} value the value to set as. or false on reset?
43475      */
43476     setFromData : function(o){
43477         var dv = ''; // display value
43478         var vv = ''; // value value..
43479         this.lastData = o;
43480         if (this.displayField) {
43481             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43482         } else {
43483             // this is an error condition!!!
43484             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43485         }
43486         
43487         if(this.valueField){
43488             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43489         }
43490         if(this.hiddenField){
43491             this.hiddenField.value = vv;
43492             
43493             this.lastSelectionText = dv;
43494             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43495             this.value = vv;
43496             return;
43497         }
43498         // no hidden field.. - we store the value in 'value', but still display
43499         // display field!!!!
43500         this.lastSelectionText = dv;
43501         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43502         this.value = vv;
43503         
43504         
43505     },
43506     // private
43507     reset : function(){
43508         // overridden so that last data is reset..
43509         this.setValue(this.resetValue);
43510         this.originalValue = this.getValue();
43511         this.clearInvalid();
43512         this.lastData = false;
43513         if (this.view) {
43514             this.view.clearSelections();
43515         }
43516     },
43517     // private
43518     findRecord : function(prop, value){
43519         var record;
43520         if(this.store.getCount() > 0){
43521             this.store.each(function(r){
43522                 if(r.data[prop] == value){
43523                     record = r;
43524                     return false;
43525                 }
43526                 return true;
43527             });
43528         }
43529         return record;
43530     },
43531     
43532     getName: function()
43533     {
43534         // returns hidden if it's set..
43535         if (!this.rendered) {return ''};
43536         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43537         
43538     },
43539     // private
43540     onViewMove : function(e, t){
43541         this.inKeyMode = false;
43542     },
43543
43544     // private
43545     onViewOver : function(e, t){
43546         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43547             return;
43548         }
43549         var item = this.view.findItemFromChild(t);
43550         if(item){
43551             var index = this.view.indexOf(item);
43552             this.select(index, false);
43553         }
43554     },
43555
43556     // private
43557     onViewClick : function(doFocus)
43558     {
43559         var index = this.view.getSelectedIndexes()[0];
43560         var r = this.store.getAt(index);
43561         if(r){
43562             this.onSelect(r, index);
43563         }
43564         if(doFocus !== false && !this.blockFocus){
43565             this.el.focus();
43566         }
43567     },
43568
43569     // private
43570     restrictHeight : function(){
43571         this.innerList.dom.style.height = '';
43572         var inner = this.innerList.dom;
43573         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43574         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43575         this.list.beginUpdate();
43576         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43577         this.list.alignTo(this.el, this.listAlign);
43578         this.list.endUpdate();
43579     },
43580
43581     // private
43582     onEmptyResults : function(){
43583         this.collapse();
43584     },
43585
43586     /**
43587      * Returns true if the dropdown list is expanded, else false.
43588      */
43589     isExpanded : function(){
43590         return this.list.isVisible();
43591     },
43592
43593     /**
43594      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43595      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43596      * @param {String} value The data value of the item to select
43597      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43598      * selected item if it is not currently in view (defaults to true)
43599      * @return {Boolean} True if the value matched an item in the list, else false
43600      */
43601     selectByValue : function(v, scrollIntoView){
43602         if(v !== undefined && v !== null){
43603             var r = this.findRecord(this.valueField || this.displayField, v);
43604             if(r){
43605                 this.select(this.store.indexOf(r), scrollIntoView);
43606                 return true;
43607             }
43608         }
43609         return false;
43610     },
43611
43612     /**
43613      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43614      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43615      * @param {Number} index The zero-based index of the list item to select
43616      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43617      * selected item if it is not currently in view (defaults to true)
43618      */
43619     select : function(index, scrollIntoView){
43620         this.selectedIndex = index;
43621         this.view.select(index);
43622         if(scrollIntoView !== false){
43623             var el = this.view.getNode(index);
43624             if(el){
43625                 this.innerList.scrollChildIntoView(el, false);
43626             }
43627         }
43628     },
43629
43630     // private
43631     selectNext : function(){
43632         var ct = this.store.getCount();
43633         if(ct > 0){
43634             if(this.selectedIndex == -1){
43635                 this.select(0);
43636             }else if(this.selectedIndex < ct-1){
43637                 this.select(this.selectedIndex+1);
43638             }
43639         }
43640     },
43641
43642     // private
43643     selectPrev : function(){
43644         var ct = this.store.getCount();
43645         if(ct > 0){
43646             if(this.selectedIndex == -1){
43647                 this.select(0);
43648             }else if(this.selectedIndex != 0){
43649                 this.select(this.selectedIndex-1);
43650             }
43651         }
43652     },
43653
43654     // private
43655     onKeyUp : function(e){
43656         if(this.editable !== false && !e.isSpecialKey()){
43657             this.lastKey = e.getKey();
43658             this.dqTask.delay(this.queryDelay);
43659         }
43660     },
43661
43662     // private
43663     validateBlur : function(){
43664         return !this.list || !this.list.isVisible();   
43665     },
43666
43667     // private
43668     initQuery : function(){
43669         this.doQuery(this.getRawValue());
43670     },
43671
43672     // private
43673     doForce : function(){
43674         if(this.el.dom.value.length > 0){
43675             this.el.dom.value =
43676                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43677              
43678         }
43679     },
43680
43681     /**
43682      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43683      * query allowing the query action to be canceled if needed.
43684      * @param {String} query The SQL query to execute
43685      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43686      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43687      * saved in the current store (defaults to false)
43688      */
43689     doQuery : function(q, forceAll){
43690         if(q === undefined || q === null){
43691             q = '';
43692         }
43693         var qe = {
43694             query: q,
43695             forceAll: forceAll,
43696             combo: this,
43697             cancel:false
43698         };
43699         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43700             return false;
43701         }
43702         q = qe.query;
43703         forceAll = qe.forceAll;
43704         if(forceAll === true || (q.length >= this.minChars)){
43705             if(this.lastQuery != q || this.alwaysQuery){
43706                 this.lastQuery = q;
43707                 if(this.mode == 'local'){
43708                     this.selectedIndex = -1;
43709                     if(forceAll){
43710                         this.store.clearFilter();
43711                     }else{
43712                         this.store.filter(this.displayField, q);
43713                     }
43714                     this.onLoad();
43715                 }else{
43716                     this.store.baseParams[this.queryParam] = q;
43717                     this.store.load({
43718                         params: this.getParams(q)
43719                     });
43720                     this.expand();
43721                 }
43722             }else{
43723                 this.selectedIndex = -1;
43724                 this.onLoad();   
43725             }
43726         }
43727     },
43728
43729     // private
43730     getParams : function(q){
43731         var p = {};
43732         //p[this.queryParam] = q;
43733         if(this.pageSize){
43734             p.start = 0;
43735             p.limit = this.pageSize;
43736         }
43737         return p;
43738     },
43739
43740     /**
43741      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43742      */
43743     collapse : function(){
43744         if(!this.isExpanded()){
43745             return;
43746         }
43747         this.list.hide();
43748         Roo.get(document).un('mousedown', this.collapseIf, this);
43749         Roo.get(document).un('mousewheel', this.collapseIf, this);
43750         if (!this.editable) {
43751             Roo.get(document).un('keydown', this.listKeyPress, this);
43752         }
43753         this.fireEvent('collapse', this);
43754     },
43755
43756     // private
43757     collapseIf : function(e){
43758         if(!e.within(this.wrap) && !e.within(this.list)){
43759             this.collapse();
43760         }
43761     },
43762
43763     /**
43764      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43765      */
43766     expand : function(){
43767         if(this.isExpanded() || !this.hasFocus){
43768             return;
43769         }
43770         this.list.alignTo(this.el, this.listAlign);
43771         this.list.show();
43772         Roo.get(document).on('mousedown', this.collapseIf, this);
43773         Roo.get(document).on('mousewheel', this.collapseIf, this);
43774         if (!this.editable) {
43775             Roo.get(document).on('keydown', this.listKeyPress, this);
43776         }
43777         
43778         this.fireEvent('expand', this);
43779     },
43780
43781     // private
43782     // Implements the default empty TriggerField.onTriggerClick function
43783     onTriggerClick : function(){
43784         if(this.disabled){
43785             return;
43786         }
43787         if(this.isExpanded()){
43788             this.collapse();
43789             if (!this.blockFocus) {
43790                 this.el.focus();
43791             }
43792             
43793         }else {
43794             this.hasFocus = true;
43795             if(this.triggerAction == 'all') {
43796                 this.doQuery(this.allQuery, true);
43797             } else {
43798                 this.doQuery(this.getRawValue());
43799             }
43800             if (!this.blockFocus) {
43801                 this.el.focus();
43802             }
43803         }
43804     },
43805     listKeyPress : function(e)
43806     {
43807         //Roo.log('listkeypress');
43808         // scroll to first matching element based on key pres..
43809         if (e.isSpecialKey()) {
43810             return false;
43811         }
43812         var k = String.fromCharCode(e.getKey()).toUpperCase();
43813         //Roo.log(k);
43814         var match  = false;
43815         var csel = this.view.getSelectedNodes();
43816         var cselitem = false;
43817         if (csel.length) {
43818             var ix = this.view.indexOf(csel[0]);
43819             cselitem  = this.store.getAt(ix);
43820             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43821                 cselitem = false;
43822             }
43823             
43824         }
43825         
43826         this.store.each(function(v) { 
43827             if (cselitem) {
43828                 // start at existing selection.
43829                 if (cselitem.id == v.id) {
43830                     cselitem = false;
43831                 }
43832                 return;
43833             }
43834                 
43835             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43836                 match = this.store.indexOf(v);
43837                 return false;
43838             }
43839         }, this);
43840         
43841         if (match === false) {
43842             return true; // no more action?
43843         }
43844         // scroll to?
43845         this.view.select(match);
43846         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43847         sn.scrollIntoView(sn.dom.parentNode, false);
43848     } 
43849
43850     /** 
43851     * @cfg {Boolean} grow 
43852     * @hide 
43853     */
43854     /** 
43855     * @cfg {Number} growMin 
43856     * @hide 
43857     */
43858     /** 
43859     * @cfg {Number} growMax 
43860     * @hide 
43861     */
43862     /**
43863      * @hide
43864      * @method autoSize
43865      */
43866 });/*
43867  * Copyright(c) 2010-2012, Roo J Solutions Limited
43868  *
43869  * Licence LGPL
43870  *
43871  */
43872
43873 /**
43874  * @class Roo.form.ComboBoxArray
43875  * @extends Roo.form.TextField
43876  * A facebook style adder... for lists of email / people / countries  etc...
43877  * pick multiple items from a combo box, and shows each one.
43878  *
43879  *  Fred [x]  Brian [x]  [Pick another |v]
43880  *
43881  *
43882  *  For this to work: it needs various extra information
43883  *    - normal combo problay has
43884  *      name, hiddenName
43885  *    + displayField, valueField
43886  *
43887  *    For our purpose...
43888  *
43889  *
43890  *   If we change from 'extends' to wrapping...
43891  *   
43892  *  
43893  *
43894  
43895  
43896  * @constructor
43897  * Create a new ComboBoxArray.
43898  * @param {Object} config Configuration options
43899  */
43900  
43901
43902 Roo.form.ComboBoxArray = function(config)
43903 {
43904     this.addEvents({
43905         /**
43906          * @event beforeremove
43907          * Fires before remove the value from the list
43908              * @param {Roo.form.ComboBoxArray} _self This combo box array
43909              * @param {Roo.form.ComboBoxArray.Item} item removed item
43910              */
43911         'beforeremove' : true,
43912         /**
43913          * @event remove
43914          * Fires when remove the value from the list
43915              * @param {Roo.form.ComboBoxArray} _self This combo box array
43916              * @param {Roo.form.ComboBoxArray.Item} item removed item
43917              */
43918         'remove' : true
43919         
43920         
43921     });
43922     
43923     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43924     
43925     this.items = new Roo.util.MixedCollection(false);
43926     
43927     // construct the child combo...
43928     
43929     
43930     
43931     
43932    
43933     
43934 }
43935
43936  
43937 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43938
43939     /**
43940      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43941      */
43942     
43943     lastData : false,
43944     
43945     // behavies liek a hiddne field
43946     inputType:      'hidden',
43947     /**
43948      * @cfg {Number} width The width of the box that displays the selected element
43949      */ 
43950     width:          300,
43951
43952     
43953     
43954     /**
43955      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43956      */
43957     name : false,
43958     /**
43959      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43960      */
43961     hiddenName : false,
43962       /**
43963      * @cfg {String} seperator    The value seperator normally ',' 
43964      */
43965     seperator : ',',
43966     
43967     // private the array of items that are displayed..
43968     items  : false,
43969     // private - the hidden field el.
43970     hiddenEl : false,
43971     // private - the filed el..
43972     el : false,
43973     
43974     //validateValue : function() { return true; }, // all values are ok!
43975     //onAddClick: function() { },
43976     
43977     onRender : function(ct, position) 
43978     {
43979         
43980         // create the standard hidden element
43981         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43982         
43983         
43984         // give fake names to child combo;
43985         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43986         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43987         
43988         this.combo = Roo.factory(this.combo, Roo.form);
43989         this.combo.onRender(ct, position);
43990         if (typeof(this.combo.width) != 'undefined') {
43991             this.combo.onResize(this.combo.width,0);
43992         }
43993         
43994         this.combo.initEvents();
43995         
43996         // assigned so form know we need to do this..
43997         this.store          = this.combo.store;
43998         this.valueField     = this.combo.valueField;
43999         this.displayField   = this.combo.displayField ;
44000         
44001         
44002         this.combo.wrap.addClass('x-cbarray-grp');
44003         
44004         var cbwrap = this.combo.wrap.createChild(
44005             {tag: 'div', cls: 'x-cbarray-cb'},
44006             this.combo.el.dom
44007         );
44008         
44009              
44010         this.hiddenEl = this.combo.wrap.createChild({
44011             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
44012         });
44013         this.el = this.combo.wrap.createChild({
44014             tag: 'input',  type:'hidden' , name: this.name, value : ''
44015         });
44016          //   this.el.dom.removeAttribute("name");
44017         
44018         
44019         this.outerWrap = this.combo.wrap;
44020         this.wrap = cbwrap;
44021         
44022         this.outerWrap.setWidth(this.width);
44023         this.outerWrap.dom.removeChild(this.el.dom);
44024         
44025         this.wrap.dom.appendChild(this.el.dom);
44026         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
44027         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
44028         
44029         this.combo.trigger.setStyle('position','relative');
44030         this.combo.trigger.setStyle('left', '0px');
44031         this.combo.trigger.setStyle('top', '2px');
44032         
44033         this.combo.el.setStyle('vertical-align', 'text-bottom');
44034         
44035         //this.trigger.setStyle('vertical-align', 'top');
44036         
44037         // this should use the code from combo really... on('add' ....)
44038         if (this.adder) {
44039             
44040         
44041             this.adder = this.outerWrap.createChild(
44042                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
44043             var _t = this;
44044             this.adder.on('click', function(e) {
44045                 _t.fireEvent('adderclick', this, e);
44046             }, _t);
44047         }
44048         //var _t = this;
44049         //this.adder.on('click', this.onAddClick, _t);
44050         
44051         
44052         this.combo.on('select', function(cb, rec, ix) {
44053             this.addItem(rec.data);
44054             
44055             cb.setValue('');
44056             cb.el.dom.value = '';
44057             //cb.lastData = rec.data;
44058             // add to list
44059             
44060         }, this);
44061         
44062         
44063     },
44064     
44065     
44066     getName: function()
44067     {
44068         // returns hidden if it's set..
44069         if (!this.rendered) {return ''};
44070         return  this.hiddenName ? this.hiddenName : this.name;
44071         
44072     },
44073     
44074     
44075     onResize: function(w, h){
44076         
44077         return;
44078         // not sure if this is needed..
44079         //this.combo.onResize(w,h);
44080         
44081         if(typeof w != 'number'){
44082             // we do not handle it!?!?
44083             return;
44084         }
44085         var tw = this.combo.trigger.getWidth();
44086         tw += this.addicon ? this.addicon.getWidth() : 0;
44087         tw += this.editicon ? this.editicon.getWidth() : 0;
44088         var x = w - tw;
44089         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
44090             
44091         this.combo.trigger.setStyle('left', '0px');
44092         
44093         if(this.list && this.listWidth === undefined){
44094             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
44095             this.list.setWidth(lw);
44096             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
44097         }
44098         
44099     
44100         
44101     },
44102     
44103     addItem: function(rec)
44104     {
44105         var valueField = this.combo.valueField;
44106         var displayField = this.combo.displayField;
44107         
44108         if (this.items.indexOfKey(rec[valueField]) > -1) {
44109             //console.log("GOT " + rec.data.id);
44110             return;
44111         }
44112         
44113         var x = new Roo.form.ComboBoxArray.Item({
44114             //id : rec[this.idField],
44115             data : rec,
44116             displayField : displayField ,
44117             tipField : displayField ,
44118             cb : this
44119         });
44120         // use the 
44121         this.items.add(rec[valueField],x);
44122         // add it before the element..
44123         this.updateHiddenEl();
44124         x.render(this.outerWrap, this.wrap.dom);
44125         // add the image handler..
44126     },
44127     
44128     updateHiddenEl : function()
44129     {
44130         this.validate();
44131         if (!this.hiddenEl) {
44132             return;
44133         }
44134         var ar = [];
44135         var idField = this.combo.valueField;
44136         
44137         this.items.each(function(f) {
44138             ar.push(f.data[idField]);
44139         });
44140         this.hiddenEl.dom.value = ar.join(this.seperator);
44141         this.validate();
44142     },
44143     
44144     reset : function()
44145     {
44146         this.items.clear();
44147         
44148         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
44149            el.remove();
44150         });
44151         
44152         this.el.dom.value = '';
44153         if (this.hiddenEl) {
44154             this.hiddenEl.dom.value = '';
44155         }
44156         
44157     },
44158     getValue: function()
44159     {
44160         return this.hiddenEl ? this.hiddenEl.dom.value : '';
44161     },
44162     setValue: function(v) // not a valid action - must use addItems..
44163     {
44164         
44165         this.reset();
44166          
44167         if (this.store.isLocal && (typeof(v) == 'string')) {
44168             // then we can use the store to find the values..
44169             // comma seperated at present.. this needs to allow JSON based encoding..
44170             this.hiddenEl.value  = v;
44171             var v_ar = [];
44172             Roo.each(v.split(this.seperator), function(k) {
44173                 Roo.log("CHECK " + this.valueField + ',' + k);
44174                 var li = this.store.query(this.valueField, k);
44175                 if (!li.length) {
44176                     return;
44177                 }
44178                 var add = {};
44179                 add[this.valueField] = k;
44180                 add[this.displayField] = li.item(0).data[this.displayField];
44181                 
44182                 this.addItem(add);
44183             }, this) 
44184              
44185         }
44186         if (typeof(v) == 'object' ) {
44187             // then let's assume it's an array of objects..
44188             Roo.each(v, function(l) {
44189                 var add = l;
44190                 if (typeof(l) == 'string') {
44191                     add = {};
44192                     add[this.valueField] = l;
44193                     add[this.displayField] = l
44194                 }
44195                 this.addItem(add);
44196             }, this);
44197              
44198         }
44199         
44200         
44201     },
44202     setFromData: function(v)
44203     {
44204         // this recieves an object, if setValues is called.
44205         this.reset();
44206         this.el.dom.value = v[this.displayField];
44207         this.hiddenEl.dom.value = v[this.valueField];
44208         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44209             return;
44210         }
44211         var kv = v[this.valueField];
44212         var dv = v[this.displayField];
44213         kv = typeof(kv) != 'string' ? '' : kv;
44214         dv = typeof(dv) != 'string' ? '' : dv;
44215         
44216         
44217         var keys = kv.split(this.seperator);
44218         var display = dv.split(this.seperator);
44219         for (var i = 0 ; i < keys.length; i++) {
44220             add = {};
44221             add[this.valueField] = keys[i];
44222             add[this.displayField] = display[i];
44223             this.addItem(add);
44224         }
44225       
44226         
44227     },
44228     
44229     /**
44230      * Validates the combox array value
44231      * @return {Boolean} True if the value is valid, else false
44232      */
44233     validate : function(){
44234         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44235             this.clearInvalid();
44236             return true;
44237         }
44238         return false;
44239     },
44240     
44241     validateValue : function(value){
44242         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44243         
44244     },
44245     
44246     /*@
44247      * overide
44248      * 
44249      */
44250     isDirty : function() {
44251         if(this.disabled) {
44252             return false;
44253         }
44254         
44255         try {
44256             var d = Roo.decode(String(this.originalValue));
44257         } catch (e) {
44258             return String(this.getValue()) !== String(this.originalValue);
44259         }
44260         
44261         var originalValue = [];
44262         
44263         for (var i = 0; i < d.length; i++){
44264             originalValue.push(d[i][this.valueField]);
44265         }
44266         
44267         return String(this.getValue()) !== String(originalValue.join(this.seperator));
44268         
44269     }
44270     
44271 });
44272
44273
44274
44275 /**
44276  * @class Roo.form.ComboBoxArray.Item
44277  * @extends Roo.BoxComponent
44278  * A selected item in the list
44279  *  Fred [x]  Brian [x]  [Pick another |v]
44280  * 
44281  * @constructor
44282  * Create a new item.
44283  * @param {Object} config Configuration options
44284  */
44285  
44286 Roo.form.ComboBoxArray.Item = function(config) {
44287     config.id = Roo.id();
44288     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44289 }
44290
44291 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44292     data : {},
44293     cb: false,
44294     displayField : false,
44295     tipField : false,
44296     
44297     
44298     defaultAutoCreate : {
44299         tag: 'div',
44300         cls: 'x-cbarray-item',
44301         cn : [ 
44302             { tag: 'div' },
44303             {
44304                 tag: 'img',
44305                 width:16,
44306                 height : 16,
44307                 src : Roo.BLANK_IMAGE_URL ,
44308                 align: 'center'
44309             }
44310         ]
44311         
44312     },
44313     
44314  
44315     onRender : function(ct, position)
44316     {
44317         Roo.form.Field.superclass.onRender.call(this, ct, position);
44318         
44319         if(!this.el){
44320             var cfg = this.getAutoCreate();
44321             this.el = ct.createChild(cfg, position);
44322         }
44323         
44324         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44325         
44326         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44327             this.cb.renderer(this.data) :
44328             String.format('{0}',this.data[this.displayField]);
44329         
44330             
44331         this.el.child('div').dom.setAttribute('qtip',
44332                         String.format('{0}',this.data[this.tipField])
44333         );
44334         
44335         this.el.child('img').on('click', this.remove, this);
44336         
44337     },
44338    
44339     remove : function()
44340     {
44341         if(this.cb.disabled){
44342             return;
44343         }
44344         
44345         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44346             this.cb.items.remove(this);
44347             this.el.child('img').un('click', this.remove, this);
44348             this.el.remove();
44349             this.cb.updateHiddenEl();
44350
44351             this.cb.fireEvent('remove', this.cb, this);
44352         }
44353         
44354     }
44355 });/*
44356  * RooJS Library 1.1.1
44357  * Copyright(c) 2008-2011  Alan Knowles
44358  *
44359  * License - LGPL
44360  */
44361  
44362
44363 /**
44364  * @class Roo.form.ComboNested
44365  * @extends Roo.form.ComboBox
44366  * A combobox for that allows selection of nested items in a list,
44367  * eg.
44368  *
44369  *  Book
44370  *    -> red
44371  *    -> green
44372  *  Table
44373  *    -> square
44374  *      ->red
44375  *      ->green
44376  *    -> rectangle
44377  *      ->green
44378  *      
44379  * 
44380  * @constructor
44381  * Create a new ComboNested
44382  * @param {Object} config Configuration options
44383  */
44384 Roo.form.ComboNested = function(config){
44385     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44386     // should verify some data...
44387     // like
44388     // hiddenName = required..
44389     // displayField = required
44390     // valudField == required
44391     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44392     var _t = this;
44393     Roo.each(req, function(e) {
44394         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44395             throw "Roo.form.ComboNested : missing value for: " + e;
44396         }
44397     });
44398      
44399     
44400 };
44401
44402 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44403    
44404     /*
44405      * @config {Number} max Number of columns to show
44406      */
44407     
44408     maxColumns : 3,
44409    
44410     list : null, // the outermost div..
44411     innerLists : null, // the
44412     views : null,
44413     stores : null,
44414     // private
44415     loadingChildren : false,
44416     
44417     onRender : function(ct, position)
44418     {
44419         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44420         
44421         if(this.hiddenName){
44422             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44423                     'before', true);
44424             this.hiddenField.value =
44425                 this.hiddenValue !== undefined ? this.hiddenValue :
44426                 this.value !== undefined ? this.value : '';
44427
44428             // prevent input submission
44429             this.el.dom.removeAttribute('name');
44430              
44431              
44432         }
44433         
44434         if(Roo.isGecko){
44435             this.el.dom.setAttribute('autocomplete', 'off');
44436         }
44437
44438         var cls = 'x-combo-list';
44439
44440         this.list = new Roo.Layer({
44441             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44442         });
44443
44444         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44445         this.list.setWidth(lw);
44446         this.list.swallowEvent('mousewheel');
44447         this.assetHeight = 0;
44448
44449         if(this.title){
44450             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44451             this.assetHeight += this.header.getHeight();
44452         }
44453         this.innerLists = [];
44454         this.views = [];
44455         this.stores = [];
44456         for (var i =0 ; i < this.maxColumns; i++) {
44457             this.onRenderList( cls, i);
44458         }
44459         
44460         // always needs footer, as we are going to have an 'OK' button.
44461         this.footer = this.list.createChild({cls:cls+'-ft'});
44462         this.pageTb = new Roo.Toolbar(this.footer);  
44463         var _this = this;
44464         this.pageTb.add(  {
44465             
44466             text: 'Done',
44467             handler: function()
44468             {
44469                 _this.collapse();
44470             }
44471         });
44472         
44473         if ( this.allowBlank && !this.disableClear) {
44474             
44475             this.pageTb.add(new Roo.Toolbar.Fill(), {
44476                 cls: 'x-btn-icon x-btn-clear',
44477                 text: '&#160;',
44478                 handler: function()
44479                 {
44480                     _this.collapse();
44481                     _this.clearValue();
44482                     _this.onSelect(false, -1);
44483                 }
44484             });
44485         }
44486         if (this.footer) {
44487             this.assetHeight += this.footer.getHeight();
44488         }
44489         
44490     },
44491     onRenderList : function (  cls, i)
44492     {
44493         
44494         var lw = Math.floor(
44495                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44496         );
44497         
44498         this.list.setWidth(lw); // default to '1'
44499
44500         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44501         //il.on('mouseover', this.onViewOver, this, { list:  i });
44502         //il.on('mousemove', this.onViewMove, this, { list:  i });
44503         il.setWidth(lw);
44504         il.setStyle({ 'overflow-x' : 'hidden'});
44505
44506         if(!this.tpl){
44507             this.tpl = new Roo.Template({
44508                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44509                 isEmpty: function (value, allValues) {
44510                     //Roo.log(value);
44511                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44512                     return dl ? 'has-children' : 'no-children'
44513                 }
44514             });
44515         }
44516         
44517         var store  = this.store;
44518         if (i > 0) {
44519             store  = new Roo.data.SimpleStore({
44520                 //fields : this.store.reader.meta.fields,
44521                 reader : this.store.reader,
44522                 data : [ ]
44523             });
44524         }
44525         this.stores[i]  = store;
44526                   
44527         var view = this.views[i] = new Roo.View(
44528             il,
44529             this.tpl,
44530             {
44531                 singleSelect:true,
44532                 store: store,
44533                 selectedClass: this.selectedClass
44534             }
44535         );
44536         view.getEl().setWidth(lw);
44537         view.getEl().setStyle({
44538             position: i < 1 ? 'relative' : 'absolute',
44539             top: 0,
44540             left: (i * lw ) + 'px',
44541             display : i > 0 ? 'none' : 'block'
44542         });
44543         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44544         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44545         //view.on('click', this.onViewClick, this, { list : i });
44546
44547         store.on('beforeload', this.onBeforeLoad, this);
44548         store.on('load',  this.onLoad, this, { list  : i});
44549         store.on('loadexception', this.onLoadException, this);
44550
44551         // hide the other vies..
44552         
44553         
44554         
44555     },
44556       
44557     restrictHeight : function()
44558     {
44559         var mh = 0;
44560         Roo.each(this.innerLists, function(il,i) {
44561             var el = this.views[i].getEl();
44562             el.dom.style.height = '';
44563             var inner = el.dom;
44564             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44565             // only adjust heights on other ones..
44566             mh = Math.max(h, mh);
44567             if (i < 1) {
44568                 
44569                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44570                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44571                
44572             }
44573             
44574             
44575         }, this);
44576         
44577         this.list.beginUpdate();
44578         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44579         this.list.alignTo(this.el, this.listAlign);
44580         this.list.endUpdate();
44581         
44582     },
44583      
44584     
44585     // -- store handlers..
44586     // private
44587     onBeforeLoad : function()
44588     {
44589         if(!this.hasFocus){
44590             return;
44591         }
44592         this.innerLists[0].update(this.loadingText ?
44593                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44594         this.restrictHeight();
44595         this.selectedIndex = -1;
44596     },
44597     // private
44598     onLoad : function(a,b,c,d)
44599     {
44600         if (!this.loadingChildren) {
44601             // then we are loading the top level. - hide the children
44602             for (var i = 1;i < this.views.length; i++) {
44603                 this.views[i].getEl().setStyle({ display : 'none' });
44604             }
44605             var lw = Math.floor(
44606                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44607             );
44608         
44609              this.list.setWidth(lw); // default to '1'
44610
44611             
44612         }
44613         if(!this.hasFocus){
44614             return;
44615         }
44616         
44617         if(this.store.getCount() > 0) {
44618             this.expand();
44619             this.restrictHeight();   
44620         } else {
44621             this.onEmptyResults();
44622         }
44623         
44624         if (!this.loadingChildren) {
44625             this.selectActive();
44626         }
44627         /*
44628         this.stores[1].loadData([]);
44629         this.stores[2].loadData([]);
44630         this.views
44631         */    
44632     
44633         //this.el.focus();
44634     },
44635     
44636     
44637     // private
44638     onLoadException : function()
44639     {
44640         this.collapse();
44641         Roo.log(this.store.reader.jsonData);
44642         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44643             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44644         }
44645         
44646         
44647     },
44648     // no cleaning of leading spaces on blur here.
44649     cleanLeadingSpace : function(e) { },
44650     
44651
44652     onSelectChange : function (view, sels, opts )
44653     {
44654         var ix = view.getSelectedIndexes();
44655          
44656         if (opts.list > this.maxColumns - 2) {
44657             if (view.store.getCount()<  1) {
44658                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44659
44660             } else  {
44661                 if (ix.length) {
44662                     // used to clear ?? but if we are loading unselected 
44663                     this.setFromData(view.store.getAt(ix[0]).data);
44664                 }
44665                 
44666             }
44667             
44668             return;
44669         }
44670         
44671         if (!ix.length) {
44672             // this get's fired when trigger opens..
44673            // this.setFromData({});
44674             var str = this.stores[opts.list+1];
44675             str.data.clear(); // removeall wihtout the fire events..
44676             return;
44677         }
44678         
44679         var rec = view.store.getAt(ix[0]);
44680          
44681         this.setFromData(rec.data);
44682         this.fireEvent('select', this, rec, ix[0]);
44683         
44684         var lw = Math.floor(
44685              (
44686                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44687              ) / this.maxColumns
44688         );
44689         this.loadingChildren = true;
44690         this.stores[opts.list+1].loadDataFromChildren( rec );
44691         this.loadingChildren = false;
44692         var dl = this.stores[opts.list+1]. getTotalCount();
44693         
44694         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44695         
44696         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44697         for (var i = opts.list+2; i < this.views.length;i++) {
44698             this.views[i].getEl().setStyle({ display : 'none' });
44699         }
44700         
44701         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44702         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44703         
44704         if (this.isLoading) {
44705            // this.selectActive(opts.list);
44706         }
44707          
44708     },
44709     
44710     
44711     
44712     
44713     onDoubleClick : function()
44714     {
44715         this.collapse(); //??
44716     },
44717     
44718      
44719     
44720     
44721     
44722     // private
44723     recordToStack : function(store, prop, value, stack)
44724     {
44725         var cstore = new Roo.data.SimpleStore({
44726             //fields : this.store.reader.meta.fields, // we need array reader.. for
44727             reader : this.store.reader,
44728             data : [ ]
44729         });
44730         var _this = this;
44731         var record  = false;
44732         var srec = false;
44733         if(store.getCount() < 1){
44734             return false;
44735         }
44736         store.each(function(r){
44737             if(r.data[prop] == value){
44738                 record = r;
44739             srec = r;
44740                 return false;
44741             }
44742             if (r.data.cn && r.data.cn.length) {
44743                 cstore.loadDataFromChildren( r);
44744                 var cret = _this.recordToStack(cstore, prop, value, stack);
44745                 if (cret !== false) {
44746                     record = cret;
44747                     srec = r;
44748                     return false;
44749                 }
44750             }
44751              
44752             return true;
44753         });
44754         if (record == false) {
44755             return false
44756         }
44757         stack.unshift(srec);
44758         return record;
44759     },
44760     
44761     /*
44762      * find the stack of stores that match our value.
44763      *
44764      * 
44765      */
44766     
44767     selectActive : function ()
44768     {
44769         // if store is not loaded, then we will need to wait for that to happen first.
44770         var stack = [];
44771         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44772         for (var i = 0; i < stack.length; i++ ) {
44773             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44774         }
44775         
44776     }
44777         
44778          
44779     
44780     
44781     
44782     
44783 });/*
44784  * Based on:
44785  * Ext JS Library 1.1.1
44786  * Copyright(c) 2006-2007, Ext JS, LLC.
44787  *
44788  * Originally Released Under LGPL - original licence link has changed is not relivant.
44789  *
44790  * Fork - LGPL
44791  * <script type="text/javascript">
44792  */
44793 /**
44794  * @class Roo.form.Checkbox
44795  * @extends Roo.form.Field
44796  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44797  * @constructor
44798  * Creates a new Checkbox
44799  * @param {Object} config Configuration options
44800  */
44801 Roo.form.Checkbox = function(config){
44802     Roo.form.Checkbox.superclass.constructor.call(this, config);
44803     this.addEvents({
44804         /**
44805          * @event check
44806          * Fires when the checkbox is checked or unchecked.
44807              * @param {Roo.form.Checkbox} this This checkbox
44808              * @param {Boolean} checked The new checked value
44809              */
44810         check : true
44811     });
44812 };
44813
44814 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44815     /**
44816      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44817      */
44818     focusClass : undefined,
44819     /**
44820      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44821      */
44822     fieldClass: "x-form-field",
44823     /**
44824      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44825      */
44826     checked: false,
44827     /**
44828      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44829      * {tag: "input", type: "checkbox", autocomplete: "off"})
44830      */
44831     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44832     /**
44833      * @cfg {String} boxLabel The text that appears beside the checkbox
44834      */
44835     boxLabel : "",
44836     /**
44837      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44838      */  
44839     inputValue : '1',
44840     /**
44841      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44842      */
44843      valueOff: '0', // value when not checked..
44844
44845     actionMode : 'viewEl', 
44846     //
44847     // private
44848     itemCls : 'x-menu-check-item x-form-item',
44849     groupClass : 'x-menu-group-item',
44850     inputType : 'hidden',
44851     
44852     
44853     inSetChecked: false, // check that we are not calling self...
44854     
44855     inputElement: false, // real input element?
44856     basedOn: false, // ????
44857     
44858     isFormField: true, // not sure where this is needed!!!!
44859
44860     onResize : function(){
44861         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44862         if(!this.boxLabel){
44863             this.el.alignTo(this.wrap, 'c-c');
44864         }
44865     },
44866
44867     initEvents : function(){
44868         Roo.form.Checkbox.superclass.initEvents.call(this);
44869         this.el.on("click", this.onClick,  this);
44870         this.el.on("change", this.onClick,  this);
44871     },
44872
44873
44874     getResizeEl : function(){
44875         return this.wrap;
44876     },
44877
44878     getPositionEl : function(){
44879         return this.wrap;
44880     },
44881
44882     // private
44883     onRender : function(ct, position){
44884         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44885         /*
44886         if(this.inputValue !== undefined){
44887             this.el.dom.value = this.inputValue;
44888         }
44889         */
44890         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44891         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44892         var viewEl = this.wrap.createChild({ 
44893             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44894         this.viewEl = viewEl;   
44895         this.wrap.on('click', this.onClick,  this); 
44896         
44897         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44898         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44899         
44900         
44901         
44902         if(this.boxLabel){
44903             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44904         //    viewEl.on('click', this.onClick,  this); 
44905         }
44906         //if(this.checked){
44907             this.setChecked(this.checked);
44908         //}else{
44909             //this.checked = this.el.dom;
44910         //}
44911
44912     },
44913
44914     // private
44915     initValue : Roo.emptyFn,
44916
44917     /**
44918      * Returns the checked state of the checkbox.
44919      * @return {Boolean} True if checked, else false
44920      */
44921     getValue : function(){
44922         if(this.el){
44923             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44924         }
44925         return this.valueOff;
44926         
44927     },
44928
44929         // private
44930     onClick : function(){ 
44931         if (this.disabled) {
44932             return;
44933         }
44934         this.setChecked(!this.checked);
44935
44936         //if(this.el.dom.checked != this.checked){
44937         //    this.setValue(this.el.dom.checked);
44938        // }
44939     },
44940
44941     /**
44942      * Sets the checked state of the checkbox.
44943      * On is always based on a string comparison between inputValue and the param.
44944      * @param {Boolean/String} value - the value to set 
44945      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44946      */
44947     setValue : function(v,suppressEvent){
44948         
44949         
44950         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44951         //if(this.el && this.el.dom){
44952         //    this.el.dom.checked = this.checked;
44953         //    this.el.dom.defaultChecked = this.checked;
44954         //}
44955         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44956         //this.fireEvent("check", this, this.checked);
44957     },
44958     // private..
44959     setChecked : function(state,suppressEvent)
44960     {
44961         if (this.inSetChecked) {
44962             this.checked = state;
44963             return;
44964         }
44965         
44966     
44967         if(this.wrap){
44968             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44969         }
44970         this.checked = state;
44971         if(suppressEvent !== true){
44972             this.fireEvent('check', this, state);
44973         }
44974         this.inSetChecked = true;
44975         this.el.dom.value = state ? this.inputValue : this.valueOff;
44976         this.inSetChecked = false;
44977         
44978     },
44979     // handle setting of hidden value by some other method!!?!?
44980     setFromHidden: function()
44981     {
44982         if(!this.el){
44983             return;
44984         }
44985         //console.log("SET FROM HIDDEN");
44986         //alert('setFrom hidden');
44987         this.setValue(this.el.dom.value);
44988     },
44989     
44990     onDestroy : function()
44991     {
44992         if(this.viewEl){
44993             Roo.get(this.viewEl).remove();
44994         }
44995          
44996         Roo.form.Checkbox.superclass.onDestroy.call(this);
44997     },
44998     
44999     setBoxLabel : function(str)
45000     {
45001         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
45002     }
45003
45004 });/*
45005  * Based on:
45006  * Ext JS Library 1.1.1
45007  * Copyright(c) 2006-2007, Ext JS, LLC.
45008  *
45009  * Originally Released Under LGPL - original licence link has changed is not relivant.
45010  *
45011  * Fork - LGPL
45012  * <script type="text/javascript">
45013  */
45014  
45015 /**
45016  * @class Roo.form.Radio
45017  * @extends Roo.form.Checkbox
45018  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
45019  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
45020  * @constructor
45021  * Creates a new Radio
45022  * @param {Object} config Configuration options
45023  */
45024 Roo.form.Radio = function(){
45025     Roo.form.Radio.superclass.constructor.apply(this, arguments);
45026 };
45027 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
45028     inputType: 'radio',
45029
45030     /**
45031      * If this radio is part of a group, it will return the selected value
45032      * @return {String}
45033      */
45034     getGroupValue : function(){
45035         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
45036     },
45037     
45038     
45039     onRender : function(ct, position){
45040         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45041         
45042         if(this.inputValue !== undefined){
45043             this.el.dom.value = this.inputValue;
45044         }
45045          
45046         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
45047         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
45048         //var viewEl = this.wrap.createChild({ 
45049         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
45050         //this.viewEl = viewEl;   
45051         //this.wrap.on('click', this.onClick,  this); 
45052         
45053         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45054         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
45055         
45056         
45057         
45058         if(this.boxLabel){
45059             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
45060         //    viewEl.on('click', this.onClick,  this); 
45061         }
45062          if(this.checked){
45063             this.el.dom.checked =   'checked' ;
45064         }
45065          
45066     } 
45067     
45068     
45069 });Roo.rtf = {}; // namespace
45070 Roo.rtf.Hex = function(hex)
45071 {
45072     this.hexstr = hex;
45073 };
45074 Roo.rtf.Paragraph = function(opts)
45075 {
45076     this.content = []; ///??? is that used?
45077 };Roo.rtf.Span = function(opts)
45078 {
45079     this.value = opts.value;
45080 };
45081
45082 Roo.rtf.Group = function(parent)
45083 {
45084     // we dont want to acutally store parent - it will make debug a nightmare..
45085     this.content = [];
45086     this.cn  = [];
45087      
45088        
45089     
45090 };
45091
45092 Roo.rtf.Group.prototype = {
45093     ignorable : false,
45094     content: false,
45095     cn: false,
45096     addContent : function(node) {
45097         // could set styles...
45098         this.content.push(node);
45099     },
45100     addChild : function(cn)
45101     {
45102         this.cn.push(cn);
45103     },
45104     // only for images really...
45105     toDataURL : function()
45106     {
45107         var mimetype = false;
45108         switch(true) {
45109             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
45110                 mimetype = "image/png";
45111                 break;
45112              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
45113                 mimetype = "image/jpeg";
45114                 break;
45115             default :
45116                 return 'about:blank'; // ?? error?
45117         }
45118         
45119         
45120         var hexstring = this.content[this.content.length-1].value;
45121         
45122         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
45123             return String.fromCharCode(parseInt(a, 16));
45124         }).join(""));
45125     }
45126     
45127 };
45128 // this looks like it's normally the {rtf{ .... }}
45129 Roo.rtf.Document = function()
45130 {
45131     // we dont want to acutally store parent - it will make debug a nightmare..
45132     this.rtlch  = [];
45133     this.content = [];
45134     this.cn = [];
45135     
45136 };
45137 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
45138     addChild : function(cn)
45139     {
45140         this.cn.push(cn);
45141         switch(cn.type) {
45142             case 'rtlch': // most content seems to be inside this??
45143             case 'listtext':
45144             case 'shpinst':
45145                 this.rtlch.push(cn);
45146                 return;
45147             default:
45148                 this[cn.type] = cn;
45149         }
45150         
45151     },
45152     
45153     getElementsByType : function(type)
45154     {
45155         var ret =  [];
45156         this._getElementsByType(type, ret, this.cn, 'rtf');
45157         return ret;
45158     },
45159     _getElementsByType : function (type, ret, search_array, path)
45160     {
45161         search_array.forEach(function(n,i) {
45162             if (n.type == type) {
45163                 n.path = path + '/' + n.type + ':' + i;
45164                 ret.push(n);
45165             }
45166             if (n.cn.length > 0) {
45167                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
45168             }
45169         },this);
45170     }
45171     
45172 });
45173  
45174 Roo.rtf.Ctrl = function(opts)
45175 {
45176     this.value = opts.value;
45177     this.param = opts.param;
45178 };
45179 /**
45180  *
45181  *
45182  * based on this https://github.com/iarna/rtf-parser
45183  * it's really only designed to extract pict from pasted RTF 
45184  *
45185  * usage:
45186  *
45187  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45188  *  
45189  *
45190  */
45191
45192  
45193
45194
45195
45196 Roo.rtf.Parser = function(text) {
45197     //super({objectMode: true})
45198     this.text = '';
45199     this.parserState = this.parseText;
45200     
45201     // these are for interpeter...
45202     this.doc = {};
45203     ///this.parserState = this.parseTop
45204     this.groupStack = [];
45205     this.hexStore = [];
45206     this.doc = false;
45207     
45208     this.groups = []; // where we put the return.
45209     
45210     for (var ii = 0; ii < text.length; ++ii) {
45211         ++this.cpos;
45212         
45213         if (text[ii] === '\n') {
45214             ++this.row;
45215             this.col = 1;
45216         } else {
45217             ++this.col;
45218         }
45219         this.parserState(text[ii]);
45220     }
45221     
45222     
45223     
45224 };
45225 Roo.rtf.Parser.prototype = {
45226     text : '', // string being parsed..
45227     controlWord : '',
45228     controlWordParam :  '',
45229     hexChar : '',
45230     doc : false,
45231     group: false,
45232     groupStack : false,
45233     hexStore : false,
45234     
45235     
45236     cpos : 0, 
45237     row : 1, // reportin?
45238     col : 1, //
45239
45240      
45241     push : function (el)
45242     {
45243         var m = 'cmd'+ el.type;
45244         if (typeof(this[m]) == 'undefined') {
45245             Roo.log('invalid cmd:' + el.type);
45246             return;
45247         }
45248         this[m](el);
45249         //Roo.log(el);
45250     },
45251     flushHexStore : function()
45252     {
45253         if (this.hexStore.length < 1) {
45254             return;
45255         }
45256         var hexstr = this.hexStore.map(
45257             function(cmd) {
45258                 return cmd.value;
45259         }).join('');
45260         
45261         this.group.addContent( new Roo.rtf.Hex( hexstr ));
45262               
45263             
45264         this.hexStore.splice(0)
45265         
45266     },
45267     
45268     cmdgroupstart : function()
45269     {
45270         this.flushHexStore();
45271         if (this.group) {
45272             this.groupStack.push(this.group);
45273         }
45274          // parent..
45275         if (this.doc === false) {
45276             this.group = this.doc = new Roo.rtf.Document();
45277             return;
45278             
45279         }
45280         this.group = new Roo.rtf.Group(this.group);
45281     },
45282     cmdignorable : function()
45283     {
45284         this.flushHexStore();
45285         this.group.ignorable = true;
45286     },
45287     cmdendparagraph : function()
45288     {
45289         this.flushHexStore();
45290         this.group.addContent(new Roo.rtf.Paragraph());
45291     },
45292     cmdgroupend : function ()
45293     {
45294         this.flushHexStore();
45295         var endingGroup = this.group;
45296         
45297         
45298         this.group = this.groupStack.pop();
45299         if (this.group) {
45300             this.group.addChild(endingGroup);
45301         }
45302         
45303         
45304         
45305         var doc = this.group || this.doc;
45306         //if (endingGroup instanceof FontTable) {
45307         //  doc.fonts = endingGroup.table
45308         //} else if (endingGroup instanceof ColorTable) {
45309         //  doc.colors = endingGroup.table
45310         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45311         if (endingGroup.ignorable === false) {
45312             //code
45313             this.groups.push(endingGroup);
45314            // Roo.log( endingGroup );
45315         }
45316             //Roo.each(endingGroup.content, function(item)) {
45317             //    doc.addContent(item);
45318             //}
45319             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45320         //}
45321     },
45322     cmdtext : function (cmd)
45323     {
45324         this.flushHexStore();
45325         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45326             //this.group = this.doc
45327             return;  // we really don't care about stray text...
45328         }
45329         this.group.addContent(new Roo.rtf.Span(cmd));
45330     },
45331     cmdcontrolword : function (cmd)
45332     {
45333         this.flushHexStore();
45334         if (!this.group.type) {
45335             this.group.type = cmd.value;
45336             return;
45337         }
45338         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45339         // we actually don't care about ctrl words...
45340         return ;
45341         /*
45342         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45343         if (this[method]) {
45344             this[method](cmd.param)
45345         } else {
45346             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45347         }
45348         */
45349     },
45350     cmdhexchar : function(cmd) {
45351         this.hexStore.push(cmd);
45352     },
45353     cmderror : function(cmd) {
45354         throw new Exception (cmd.value);
45355     },
45356     
45357     /*
45358       _flush (done) {
45359         if (this.text !== '\u0000') this.emitText()
45360         done()
45361       }
45362       */
45363       
45364       
45365     parseText : function(c)
45366     {
45367         if (c === '\\') {
45368             this.parserState = this.parseEscapes;
45369         } else if (c === '{') {
45370             this.emitStartGroup();
45371         } else if (c === '}') {
45372             this.emitEndGroup();
45373         } else if (c === '\x0A' || c === '\x0D') {
45374             // cr/lf are noise chars
45375         } else {
45376             this.text += c;
45377         }
45378     },
45379     
45380     parseEscapes: function (c)
45381     {
45382         if (c === '\\' || c === '{' || c === '}') {
45383             this.text += c;
45384             this.parserState = this.parseText;
45385         } else {
45386             this.parserState = this.parseControlSymbol;
45387             this.parseControlSymbol(c);
45388         }
45389     },
45390     parseControlSymbol: function(c)
45391     {
45392         if (c === '~') {
45393             this.text += '\u00a0'; // nbsp
45394             this.parserState = this.parseText
45395         } else if (c === '-') {
45396              this.text += '\u00ad'; // soft hyphen
45397         } else if (c === '_') {
45398             this.text += '\u2011'; // non-breaking hyphen
45399         } else if (c === '*') {
45400             this.emitIgnorable();
45401             this.parserState = this.parseText;
45402         } else if (c === "'") {
45403             this.parserState = this.parseHexChar;
45404         } else if (c === '|') { // formula cacter
45405             this.emitFormula();
45406             this.parserState = this.parseText;
45407         } else if (c === ':') { // subentry in an index entry
45408             this.emitIndexSubEntry();
45409             this.parserState = this.parseText;
45410         } else if (c === '\x0a') {
45411             this.emitEndParagraph();
45412             this.parserState = this.parseText;
45413         } else if (c === '\x0d') {
45414             this.emitEndParagraph();
45415             this.parserState = this.parseText;
45416         } else {
45417             this.parserState = this.parseControlWord;
45418             this.parseControlWord(c);
45419         }
45420     },
45421     parseHexChar: function (c)
45422     {
45423         if (/^[A-Fa-f0-9]$/.test(c)) {
45424             this.hexChar += c;
45425             if (this.hexChar.length >= 2) {
45426               this.emitHexChar();
45427               this.parserState = this.parseText;
45428             }
45429             return;
45430         }
45431         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45432         this.parserState = this.parseText;
45433         
45434     },
45435     parseControlWord : function(c)
45436     {
45437         if (c === ' ') {
45438             this.emitControlWord();
45439             this.parserState = this.parseText;
45440         } else if (/^[-\d]$/.test(c)) {
45441             this.parserState = this.parseControlWordParam;
45442             this.controlWordParam += c;
45443         } else if (/^[A-Za-z]$/.test(c)) {
45444           this.controlWord += c;
45445         } else {
45446           this.emitControlWord();
45447           this.parserState = this.parseText;
45448           this.parseText(c);
45449         }
45450     },
45451     parseControlWordParam : function (c) {
45452         if (/^\d$/.test(c)) {
45453           this.controlWordParam += c;
45454         } else if (c === ' ') {
45455           this.emitControlWord();
45456           this.parserState = this.parseText;
45457         } else {
45458           this.emitControlWord();
45459           this.parserState = this.parseText;
45460           this.parseText(c);
45461         }
45462     },
45463     
45464     
45465     
45466     
45467     emitText : function () {
45468         if (this.text === '') {
45469             return;
45470         }
45471         this.push({
45472             type: 'text',
45473             value: this.text,
45474             pos: this.cpos,
45475             row: this.row,
45476             col: this.col
45477         });
45478         this.text = ''
45479     },
45480     emitControlWord : function ()
45481     {
45482         this.emitText();
45483         if (this.controlWord === '') {
45484             this.emitError('empty control word');
45485         } else {
45486             this.push({
45487                   type: 'controlword',
45488                   value: this.controlWord,
45489                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45490                   pos: this.cpos,
45491                   row: this.row,
45492                   col: this.col
45493             });
45494         }
45495         this.controlWord = '';
45496         this.controlWordParam = '';
45497     },
45498     emitStartGroup : function ()
45499     {
45500         this.emitText();
45501         this.push({
45502             type: 'groupstart',
45503             pos: this.cpos,
45504             row: this.row,
45505             col: this.col
45506         });
45507     },
45508     emitEndGroup : function ()
45509     {
45510         this.emitText();
45511         this.push({
45512             type: 'groupend',
45513             pos: this.cpos,
45514             row: this.row,
45515             col: this.col
45516         });
45517     },
45518     emitIgnorable : function ()
45519     {
45520         this.emitText();
45521         this.push({
45522             type: 'ignorable',
45523             pos: this.cpos,
45524             row: this.row,
45525             col: this.col
45526         });
45527     },
45528     emitHexChar : function ()
45529     {
45530         this.emitText();
45531         this.push({
45532             type: 'hexchar',
45533             value: this.hexChar,
45534             pos: this.cpos,
45535             row: this.row,
45536             col: this.col
45537         });
45538         this.hexChar = ''
45539     },
45540     emitError : function (message)
45541     {
45542       this.emitText();
45543       this.push({
45544             type: 'error',
45545             value: message,
45546             row: this.row,
45547             col: this.col,
45548             char: this.cpos //,
45549             //stack: new Error().stack
45550         });
45551     },
45552     emitEndParagraph : function () {
45553         this.emitText();
45554         this.push({
45555             type: 'endparagraph',
45556             pos: this.cpos,
45557             row: this.row,
45558             col: this.col
45559         });
45560     }
45561      
45562 } ;
45563 Roo.htmleditor = {};
45564  
45565 /**
45566  * @class Roo.htmleditor.Filter
45567  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45568  * @cfg {DomElement} node The node to iterate and filter
45569  * @cfg {boolean|String|Array} tag Tags to replace 
45570  * @constructor
45571  * Create a new Filter.
45572  * @param {Object} config Configuration options
45573  */
45574
45575
45576
45577 Roo.htmleditor.Filter = function(cfg) {
45578     Roo.apply(this.cfg);
45579     // this does not actually call walk as it's really just a abstract class
45580 }
45581
45582
45583 Roo.htmleditor.Filter.prototype = {
45584     
45585     node: false,
45586     
45587     tag: false,
45588
45589     // overrride to do replace comments.
45590     replaceComment : false,
45591     
45592     // overrride to do replace or do stuff with tags..
45593     replaceTag : false,
45594     
45595     walk : function(dom)
45596     {
45597         Roo.each( Array.from(dom.childNodes), function( e ) {
45598             switch(true) {
45599                 
45600                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
45601                     this.replaceComment(e);
45602                     return;
45603                 
45604                 case e.nodeType != 1: //not a node.
45605                     return;
45606                 
45607                 case this.tag === true: // everything
45608                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45609                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45610                     if (this.replaceTag && false === this.replaceTag(e)) {
45611                         return;
45612                     }
45613                     if (e.hasChildNodes()) {
45614                         this.walk(e);
45615                     }
45616                     return;
45617                 
45618                 default:    // tags .. that do not match.
45619                     if (e.hasChildNodes()) {
45620                         this.walk(e);
45621                     }
45622             }
45623             
45624         }, this);
45625         
45626     }
45627 }; 
45628
45629 /**
45630  * @class Roo.htmleditor.FilterAttributes
45631  * clean attributes and  styles including http:// etc.. in attribute
45632  * @constructor
45633 * Run a new Attribute Filter
45634 * @param {Object} config Configuration options
45635  */
45636 Roo.htmleditor.FilterAttributes = function(cfg)
45637 {
45638     Roo.apply(this, cfg);
45639     this.attrib_black = this.attrib_black || [];
45640     this.attrib_white = this.attrib_white || [];
45641
45642     this.attrib_clean = this.attrib_clean || [];
45643     this.style_white = this.style_white || [];
45644     this.style_black = this.style_black || [];
45645     this.walk(cfg.node);
45646 }
45647
45648 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45649 {
45650     tag: true, // all tags
45651     
45652     attrib_black : false, // array
45653     attrib_clean : false,
45654     attrib_white : false,
45655
45656     style_white : false,
45657     style_black : false,
45658      
45659      
45660     replaceTag : function(node)
45661     {
45662         if (!node.attributes || !node.attributes.length) {
45663             return true;
45664         }
45665         
45666         for (var i = node.attributes.length-1; i > -1 ; i--) {
45667             var a = node.attributes[i];
45668             //console.log(a);
45669             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45670                 node.removeAttribute(a.name);
45671                 continue;
45672             }
45673             
45674             
45675             
45676             if (a.name.toLowerCase().substr(0,2)=='on')  {
45677                 node.removeAttribute(a.name);
45678                 continue;
45679             }
45680             
45681             
45682             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45683                 node.removeAttribute(a.name);
45684                 continue;
45685             }
45686             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45687                 this.cleanAttr(node,a.name,a.value); // fixme..
45688                 continue;
45689             }
45690             if (a.name == 'style') {
45691                 this.cleanStyle(node,a.name,a.value);
45692                 continue;
45693             }
45694             /// clean up MS crap..
45695             // tecnically this should be a list of valid class'es..
45696             
45697             
45698             if (a.name == 'class') {
45699                 if (a.value.match(/^Mso/)) {
45700                     node.removeAttribute('class');
45701                 }
45702                 
45703                 if (a.value.match(/^body$/)) {
45704                     node.removeAttribute('class');
45705                 }
45706                 continue;
45707             }
45708             
45709             
45710             // style cleanup!?
45711             // class cleanup?
45712             
45713         }
45714         return true; // clean children
45715     },
45716         
45717     cleanAttr: function(node, n,v)
45718     {
45719         
45720         if (v.match(/^\./) || v.match(/^\//)) {
45721             return;
45722         }
45723         if (v.match(/^(http|https):\/\//)
45724             || v.match(/^mailto:/) 
45725             || v.match(/^ftp:/)
45726             || v.match(/^data:/)
45727             ) {
45728             return;
45729         }
45730         if (v.match(/^#/)) {
45731             return;
45732         }
45733         if (v.match(/^\{/)) { // allow template editing.
45734             return;
45735         }
45736 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45737         node.removeAttribute(n);
45738         
45739     },
45740     cleanStyle : function(node,  n,v)
45741     {
45742         if (v.match(/expression/)) { //XSS?? should we even bother..
45743             node.removeAttribute(n);
45744             return;
45745         }
45746         
45747         var parts = v.split(/;/);
45748         var clean = [];
45749         
45750         Roo.each(parts, function(p) {
45751             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45752             if (!p.length) {
45753                 return true;
45754             }
45755             var l = p.split(':').shift().replace(/\s+/g,'');
45756             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45757             
45758             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45759                 return true;
45760             }
45761             //Roo.log()
45762             // only allow 'c whitelisted system attributes'
45763             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45764                 return true;
45765             }
45766             
45767             
45768             clean.push(p);
45769             return true;
45770         },this);
45771         if (clean.length) { 
45772             node.setAttribute(n, clean.join(';'));
45773         } else {
45774             node.removeAttribute(n);
45775         }
45776         
45777     }
45778         
45779         
45780         
45781     
45782 });/**
45783  * @class Roo.htmleditor.FilterBlack
45784  * remove blacklisted elements.
45785  * @constructor
45786  * Run a new Blacklisted Filter
45787  * @param {Object} config Configuration options
45788  */
45789
45790 Roo.htmleditor.FilterBlack = function(cfg)
45791 {
45792     Roo.apply(this, cfg);
45793     this.walk(cfg.node);
45794 }
45795
45796 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45797 {
45798     tag : true, // all elements.
45799    
45800     replaceTag : function(n)
45801     {
45802         n.parentNode.removeChild(n);
45803     }
45804 });
45805 /**
45806  * @class Roo.htmleditor.FilterComment
45807  * remove comments.
45808  * @constructor
45809 * Run a new Comments Filter
45810 * @param {Object} config Configuration options
45811  */
45812 Roo.htmleditor.FilterComment = function(cfg)
45813 {
45814     this.walk(cfg.node);
45815 }
45816
45817 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45818 {
45819   
45820     replaceComment : function(n)
45821     {
45822         n.parentNode.removeChild(n);
45823     }
45824 });/**
45825  * @class Roo.htmleditor.FilterKeepChildren
45826  * remove tags but keep children
45827  * @constructor
45828  * Run a new Keep Children Filter
45829  * @param {Object} config Configuration options
45830  */
45831
45832 Roo.htmleditor.FilterKeepChildren = function(cfg)
45833 {
45834     Roo.apply(this, cfg);
45835     if (this.tag === false) {
45836         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45837     }
45838     this.walk(cfg.node);
45839 }
45840
45841 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45842 {
45843     
45844   
45845     replaceTag : function(node)
45846     {
45847         // walk children...
45848         //Roo.log(node);
45849         var ar = Array.from(node.childNodes);
45850         //remove first..
45851         for (var i = 0; i < ar.length; i++) {
45852             if (ar[i].nodeType == 1) {
45853                 if (
45854                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45855                     || // array and it matches
45856                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45857                 ) {
45858                     this.replaceTag(ar[i]); // child is blacklisted as well...
45859                     continue;
45860                 }
45861             }
45862         }  
45863         ar = Array.from(node.childNodes);
45864         for (var i = 0; i < ar.length; i++) {
45865          
45866             node.removeChild(ar[i]);
45867             // what if we need to walk these???
45868             node.parentNode.insertBefore(ar[i], node);
45869             if (this.tag !== false) {
45870                 this.walk(ar[i]);
45871                 
45872             }
45873         }
45874         node.parentNode.removeChild(node);
45875         return false; // don't walk children
45876         
45877         
45878     }
45879 });/**
45880  * @class Roo.htmleditor.FilterParagraph
45881  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45882  * like on 'push' to remove the <p> tags and replace them with line breaks.
45883  * @constructor
45884  * Run a new Paragraph Filter
45885  * @param {Object} config Configuration options
45886  */
45887
45888 Roo.htmleditor.FilterParagraph = function(cfg)
45889 {
45890     // no need to apply config.
45891     this.walk(cfg.node);
45892 }
45893
45894 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45895 {
45896     
45897      
45898     tag : 'P',
45899     
45900      
45901     replaceTag : function(node)
45902     {
45903         
45904         if (node.childNodes.length == 1 &&
45905             node.childNodes[0].nodeType == 3 &&
45906             node.childNodes[0].textContent.trim().length < 1
45907             ) {
45908             // remove and replace with '<BR>';
45909             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45910             return false; // no need to walk..
45911         }
45912         var ar = Array.from(node.childNodes);
45913         for (var i = 0; i < ar.length; i++) {
45914             node.removeChild(ar[i]);
45915             // what if we need to walk these???
45916             node.parentNode.insertBefore(ar[i], node);
45917         }
45918         // now what about this?
45919         // <p> &nbsp; </p>
45920         
45921         // double BR.
45922         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45923         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45924         node.parentNode.removeChild(node);
45925         
45926         return false;
45927
45928     }
45929     
45930 });/**
45931  * @class Roo.htmleditor.FilterSpan
45932  * filter span's with no attributes out..
45933  * @constructor
45934  * Run a new Span Filter
45935  * @param {Object} config Configuration options
45936  */
45937
45938 Roo.htmleditor.FilterSpan = function(cfg)
45939 {
45940     // no need to apply config.
45941     this.walk(cfg.node);
45942 }
45943
45944 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45945 {
45946      
45947     tag : 'SPAN',
45948      
45949  
45950     replaceTag : function(node)
45951     {
45952         if (node.attributes && node.attributes.length > 0) {
45953             return true; // walk if there are any.
45954         }
45955         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45956         return false;
45957      
45958     }
45959     
45960 });/**
45961  * @class Roo.htmleditor.FilterTableWidth
45962   try and remove table width data - as that frequently messes up other stuff.
45963  * 
45964  *      was cleanTableWidths.
45965  *
45966  * Quite often pasting from word etc.. results in tables with column and widths.
45967  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45968  *
45969  * @constructor
45970  * Run a new Table Filter
45971  * @param {Object} config Configuration options
45972  */
45973
45974 Roo.htmleditor.FilterTableWidth = function(cfg)
45975 {
45976     // no need to apply config.
45977     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45978     this.walk(cfg.node);
45979 }
45980
45981 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45982 {
45983      
45984      
45985     
45986     replaceTag: function(node) {
45987         
45988         
45989       
45990         if (node.hasAttribute('width')) {
45991             node.removeAttribute('width');
45992         }
45993         
45994          
45995         if (node.hasAttribute("style")) {
45996             // pretty basic...
45997             
45998             var styles = node.getAttribute("style").split(";");
45999             var nstyle = [];
46000             Roo.each(styles, function(s) {
46001                 if (!s.match(/:/)) {
46002                     return;
46003                 }
46004                 var kv = s.split(":");
46005                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
46006                     return;
46007                 }
46008                 // what ever is left... we allow.
46009                 nstyle.push(s);
46010             });
46011             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46012             if (!nstyle.length) {
46013                 node.removeAttribute('style');
46014             }
46015         }
46016         
46017         return true; // continue doing children..
46018     }
46019 });/**
46020  * @class Roo.htmleditor.FilterWord
46021  * try and clean up all the mess that Word generates.
46022  * 
46023  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
46024  
46025  * @constructor
46026  * Run a new Span Filter
46027  * @param {Object} config Configuration options
46028  */
46029
46030 Roo.htmleditor.FilterWord = function(cfg)
46031 {
46032     // no need to apply config.
46033     this.walk(cfg.node);
46034 }
46035
46036 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
46037 {
46038     tag: true,
46039      
46040     
46041     /**
46042      * Clean up MS wordisms...
46043      */
46044     replaceTag : function(node)
46045     {
46046          
46047         // no idea what this does - span with text, replaceds with just text.
46048         if(
46049                 node.nodeName == 'SPAN' &&
46050                 !node.hasAttributes() &&
46051                 node.childNodes.length == 1 &&
46052                 node.firstChild.nodeName == "#text"  
46053         ) {
46054             var textNode = node.firstChild;
46055             node.removeChild(textNode);
46056             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
46057                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
46058             }
46059             node.parentNode.insertBefore(textNode, node);
46060             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
46061                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
46062             }
46063             
46064             node.parentNode.removeChild(node);
46065             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
46066         }
46067         
46068    
46069         
46070         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
46071             node.parentNode.removeChild(node);
46072             return false; // dont do chidlren
46073         }
46074         //Roo.log(node.tagName);
46075         // remove - but keep children..
46076         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
46077             //Roo.log('-- removed');
46078             while (node.childNodes.length) {
46079                 var cn = node.childNodes[0];
46080                 node.removeChild(cn);
46081                 node.parentNode.insertBefore(cn, node);
46082                 // move node to parent - and clean it..
46083                 this.replaceTag(cn);
46084             }
46085             node.parentNode.removeChild(node);
46086             /// no need to iterate chidlren = it's got none..
46087             //this.iterateChildren(node, this.cleanWord);
46088             return false; // no need to iterate children.
46089         }
46090         // clean styles
46091         if (node.className.length) {
46092             
46093             var cn = node.className.split(/\W+/);
46094             var cna = [];
46095             Roo.each(cn, function(cls) {
46096                 if (cls.match(/Mso[a-zA-Z]+/)) {
46097                     return;
46098                 }
46099                 cna.push(cls);
46100             });
46101             node.className = cna.length ? cna.join(' ') : '';
46102             if (!cna.length) {
46103                 node.removeAttribute("class");
46104             }
46105         }
46106         
46107         if (node.hasAttribute("lang")) {
46108             node.removeAttribute("lang");
46109         }
46110         
46111         if (node.hasAttribute("style")) {
46112             
46113             var styles = node.getAttribute("style").split(";");
46114             var nstyle = [];
46115             Roo.each(styles, function(s) {
46116                 if (!s.match(/:/)) {
46117                     return;
46118                 }
46119                 var kv = s.split(":");
46120                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
46121                     return;
46122                 }
46123                 // what ever is left... we allow.
46124                 nstyle.push(s);
46125             });
46126             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46127             if (!nstyle.length) {
46128                 node.removeAttribute('style');
46129             }
46130         }
46131         return true; // do children
46132         
46133         
46134         
46135     }
46136 });
46137 /**
46138  * @class Roo.htmleditor.FilterStyleToTag
46139  * part of the word stuff... - certain 'styles' should be converted to tags.
46140  * eg.
46141  *   font-weight: bold -> bold
46142  *   ?? super / subscrit etc..
46143  * 
46144  * @constructor
46145 * Run a new style to tag filter.
46146 * @param {Object} config Configuration options
46147  */
46148 Roo.htmleditor.FilterStyleToTag = function(cfg)
46149 {
46150     
46151     this.tags = {
46152         B  : [ 'fontWeight' , 'bold'],
46153         I :  [ 'fontStyle' , 'italic'],
46154         //pre :  [ 'font-style' , 'italic'],
46155         // h1.. h6 ?? font-size?
46156         SUP : [ 'verticalAlign' , 'super' ],
46157         SUB : [ 'verticalAlign' , 'sub' ]
46158         
46159         
46160     };
46161     
46162     Roo.apply(this, cfg);
46163      
46164     
46165     this.walk(cfg.node);
46166     
46167     
46168     
46169 }
46170
46171
46172 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
46173 {
46174     tag: true, // all tags
46175     
46176     tags : false,
46177     
46178     
46179     replaceTag : function(node)
46180     {
46181         
46182         
46183         if (node.getAttribute("style") === null) {
46184             return true;
46185         }
46186         var inject = [];
46187         for (var k in this.tags) {
46188             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46189                 inject.push(k);
46190                 node.style.removeProperty(this.tags[k][0]);
46191             }
46192         }
46193         if (!inject.length) {
46194             return true; 
46195         }
46196         var cn = Array.from(node.childNodes);
46197         var nn = node;
46198         Roo.each(inject, function(t) {
46199             var nc = node.ownerDocument.createElement(t);
46200             nn.appendChild(nc);
46201             nn = nc;
46202         });
46203         for(var i = 0;i < cn.length;cn++) {
46204             node.removeChild(cn[i]);
46205             nn.appendChild(cn[i]);
46206         }
46207         return true /// iterate thru
46208     }
46209     
46210 })/**
46211  * @class Roo.htmleditor.FilterLongBr
46212  * BR/BR/BR - keep a maximum of 2...
46213  * @constructor
46214  * Run a new Long BR Filter
46215  * @param {Object} config Configuration options
46216  */
46217
46218 Roo.htmleditor.FilterLongBr = function(cfg)
46219 {
46220     // no need to apply config.
46221     this.walk(cfg.node);
46222 }
46223
46224 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46225 {
46226     
46227      
46228     tag : 'BR',
46229     
46230      
46231     replaceTag : function(node)
46232     {
46233         
46234         var ps = node.nextSibling;
46235         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46236             ps = ps.nextSibling;
46237         }
46238         
46239         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
46240             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46241             return false;
46242         }
46243         
46244         if (!ps || ps.nodeType != 1) {
46245             return false;
46246         }
46247         
46248         if (!ps || ps.tagName != 'BR') {
46249            
46250             return false;
46251         }
46252         
46253         
46254         
46255         
46256         
46257         if (!node.previousSibling) {
46258             return false;
46259         }
46260         var ps = node.previousSibling;
46261         
46262         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46263             ps = ps.previousSibling;
46264         }
46265         if (!ps || ps.nodeType != 1) {
46266             return false;
46267         }
46268         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46269         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46270             return false;
46271         }
46272         
46273         node.parentNode.removeChild(node); // remove me...
46274         
46275         return false; // no need to do children
46276
46277     }
46278     
46279 }); 
46280
46281 /**
46282  * @class Roo.htmleditor.FilterBlock
46283  * removes id / data-block and contenteditable that are associated with blocks
46284  * usage should be done on a cloned copy of the dom
46285  * @constructor
46286 * Run a new Attribute Filter { node : xxxx }}
46287 * @param {Object} config Configuration options
46288  */
46289 Roo.htmleditor.FilterBlock = function(cfg)
46290 {
46291     Roo.apply(this, cfg);
46292     var qa = cfg.node.querySelectorAll;
46293     this.removeAttributes('data-block');
46294     this.removeAttributes('contenteditable');
46295     this.removeAttributes('id');
46296     
46297 }
46298
46299 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
46300 {
46301     node: true, // all tags
46302      
46303      
46304     removeAttributes : function(attr)
46305     {
46306         var ar = this.node.querySelectorAll('*[' + attr + ']');
46307         for (var i =0;i<ar.length;i++) {
46308             ar[i].removeAttribute(attr);
46309         }
46310     }
46311         
46312         
46313         
46314     
46315 });
46316 /***
46317  * This is based loosely on tinymce 
46318  * @class Roo.htmleditor.TidySerializer
46319  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46320  * @constructor
46321  * @method Serializer
46322  * @param {Object} settings Name/value settings object.
46323  */
46324
46325
46326 Roo.htmleditor.TidySerializer = function(settings)
46327 {
46328     Roo.apply(this, settings);
46329     
46330     this.writer = new Roo.htmleditor.TidyWriter(settings);
46331     
46332     
46333
46334 };
46335 Roo.htmleditor.TidySerializer.prototype = {
46336     
46337     /**
46338      * @param {boolean} inner do the inner of the node.
46339      */
46340     inner : false,
46341     
46342     writer : false,
46343     
46344     /**
46345     * Serializes the specified node into a string.
46346     *
46347     * @example
46348     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
46349     * @method serialize
46350     * @param {DomElement} node Node instance to serialize.
46351     * @return {String} String with HTML based on DOM tree.
46352     */
46353     serialize : function(node) {
46354         
46355         // = settings.validate;
46356         var writer = this.writer;
46357         var self  = this;
46358         this.handlers = {
46359             // #text
46360             3: function(node) {
46361                 
46362                 writer.text(node.nodeValue, node);
46363             },
46364             // #comment
46365             8: function(node) {
46366                 writer.comment(node.nodeValue);
46367             },
46368             // Processing instruction
46369             7: function(node) {
46370                 writer.pi(node.name, node.nodeValue);
46371             },
46372             // Doctype
46373             10: function(node) {
46374                 writer.doctype(node.nodeValue);
46375             },
46376             // CDATA
46377             4: function(node) {
46378                 writer.cdata(node.nodeValue);
46379             },
46380             // Document fragment
46381             11: function(node) {
46382                 node = node.firstChild;
46383                 if (!node) {
46384                     return;
46385                 }
46386                 while(node) {
46387                     self.walk(node);
46388                     node = node.nextSibling
46389                 }
46390             }
46391         };
46392         writer.reset();
46393         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
46394         return writer.getContent();
46395     },
46396
46397     walk: function(node)
46398     {
46399         var attrName, attrValue, sortedAttrs, i, l, elementRule,
46400             handler = this.handlers[node.nodeType];
46401             
46402         if (handler) {
46403             handler(node);
46404             return;
46405         }
46406     
46407         var name = node.nodeName;
46408         var isEmpty = node.childNodes.length < 1;
46409       
46410         var writer = this.writer;
46411         var attrs = node.attributes;
46412         // Sort attributes
46413         
46414         writer.start(node.nodeName, attrs, isEmpty, node);
46415         if (isEmpty) {
46416             return;
46417         }
46418         node = node.firstChild;
46419         if (!node) {
46420             writer.end(name);
46421             return;
46422         }
46423         while (node) {
46424             this.walk(node);
46425             node = node.nextSibling;
46426         }
46427         writer.end(name);
46428         
46429     
46430     }
46431     // Serialize element and treat all non elements as fragments
46432    
46433 }; 
46434
46435 /***
46436  * This is based loosely on tinymce 
46437  * @class Roo.htmleditor.TidyWriter
46438  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46439  *
46440  * Known issues?
46441  * - not tested much with 'PRE' formated elements.
46442  * 
46443  *
46444  *
46445  */
46446
46447 Roo.htmleditor.TidyWriter = function(settings)
46448 {
46449     
46450     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
46451     Roo.apply(this, settings);
46452     this.html = [];
46453     this.state = [];
46454      
46455     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
46456   
46457 }
46458 Roo.htmleditor.TidyWriter.prototype = {
46459
46460  
46461     state : false,
46462     
46463     indent :  '  ',
46464     
46465     // part of state...
46466     indentstr : '',
46467     in_pre: false,
46468     in_inline : false,
46469     last_inline : false,
46470     encode : false,
46471      
46472     
46473             /**
46474     * Writes the a start element such as <p id="a">.
46475     *
46476     * @method start
46477     * @param {String} name Name of the element.
46478     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
46479     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
46480     */
46481     start: function(name, attrs, empty, node)
46482     {
46483         var i, l, attr, value;
46484         
46485         // there are some situations where adding line break && indentation will not work. will not work.
46486         // <span / b / i ... formating?
46487         
46488         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46489         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
46490         
46491         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
46492         
46493         var add_lb = name == 'BR' ? false : in_inline;
46494         
46495         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
46496             i_inline = false;
46497         }
46498
46499         var indentstr =  this.indentstr;
46500         
46501         // e_inline = elements that can be inline, but still allow \n before and after?
46502         // only 'BR' ??? any others?
46503         
46504         // ADD LINE BEFORE tage
46505         if (!this.in_pre) {
46506             if (in_inline) {
46507                 //code
46508                 if (name == 'BR') {
46509                     this.addLine();
46510                 } else if (this.lastElementEndsWS()) {
46511                     this.addLine();
46512                 } else{
46513                     // otherwise - no new line. (and dont indent.)
46514                     indentstr = '';
46515                 }
46516                 
46517             } else {
46518                 this.addLine();
46519             }
46520         } else {
46521             indentstr = '';
46522         }
46523         
46524         this.html.push(indentstr + '<', name.toLowerCase());
46525         
46526         if (attrs) {
46527             for (i = 0, l = attrs.length; i < l; i++) {
46528                 attr = attrs[i];
46529                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
46530             }
46531         }
46532      
46533         if (empty) {
46534             if (is_short) {
46535                 this.html[this.html.length] = '/>';
46536             } else {
46537                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
46538             }
46539             var e_inline = name == 'BR' ? false : this.in_inline;
46540             
46541             if (!e_inline && !this.in_pre) {
46542                 this.addLine();
46543             }
46544             return;
46545         
46546         }
46547         // not empty..
46548         this.html[this.html.length] = '>';
46549         
46550         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
46551         /*
46552         if (!in_inline && !in_pre) {
46553             var cn = node.firstChild;
46554             while(cn) {
46555                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
46556                     in_inline = true
46557                     break;
46558                 }
46559                 cn = cn.nextSibling;
46560             }
46561              
46562         }
46563         */
46564         
46565         
46566         this.pushState({
46567             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
46568             in_pre : in_pre,
46569             in_inline :  in_inline
46570         });
46571         // add a line after if we are not in a
46572         
46573         if (!in_inline && !in_pre) {
46574             this.addLine();
46575         }
46576         
46577             
46578          
46579         
46580     },
46581     
46582     lastElementEndsWS : function()
46583     {
46584         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
46585         if (value === false) {
46586             return true;
46587         }
46588         return value.match(/\s+$/);
46589         
46590     },
46591     
46592     /**
46593      * Writes the a end element such as </p>.
46594      *
46595      * @method end
46596      * @param {String} name Name of the element.
46597      */
46598     end: function(name) {
46599         var value;
46600         this.popState();
46601         var indentstr = '';
46602         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46603         
46604         if (!this.in_pre && !in_inline) {
46605             this.addLine();
46606             indentstr  = this.indentstr;
46607         }
46608         this.html.push(indentstr + '</', name.toLowerCase(), '>');
46609         this.last_inline = in_inline;
46610         
46611         // pop the indent state..
46612     },
46613     /**
46614      * Writes a text node.
46615      *
46616      * In pre - we should not mess with the contents.
46617      * 
46618      *
46619      * @method text
46620      * @param {String} text String to write out.
46621      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
46622      */
46623     text: function(text, node)
46624     {
46625         // if not in whitespace critical
46626         if (text.length < 1) {
46627             return;
46628         }
46629         if (this.in_pre) {
46630             this.html[this.html.length] =  text;
46631             return;   
46632         }
46633         
46634         if (this.in_inline) {
46635             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
46636             if (text != ' ') {
46637                 text = text.replace(/\s+/,' ');  // all white space to single white space
46638                 
46639                     
46640                 // if next tag is '<BR>', then we can trim right..
46641                 if (node.nextSibling &&
46642                     node.nextSibling.nodeType == 1 &&
46643                     node.nextSibling.nodeName == 'BR' )
46644                 {
46645                     text = text.replace(/\s+$/g,'');
46646                 }
46647                 // if previous tag was a BR, we can also trim..
46648                 if (node.previousSibling &&
46649                     node.previousSibling.nodeType == 1 &&
46650                     node.previousSibling.nodeName == 'BR' )
46651                 {
46652                     text = this.indentstr +  text.replace(/^\s+/g,'');
46653                 }
46654                 if (text.match(/\n/)) {
46655                     text = text.replace(
46656                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
46657                     );
46658                     // remoeve the last whitespace / line break.
46659                     text = text.replace(/\n\s+$/,'');
46660                 }
46661                 // repace long lines
46662                 
46663             }
46664              
46665             this.html[this.html.length] =  text;
46666             return;   
46667         }
46668         // see if previous element was a inline element.
46669         var indentstr = this.indentstr;
46670    
46671         text = text.replace(/\s+/g," "); // all whitespace into single white space.
46672         
46673         // should trim left?
46674         if (node.previousSibling &&
46675             node.previousSibling.nodeType == 1 &&
46676             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
46677         {
46678             indentstr = '';
46679             
46680         } else {
46681             this.addLine();
46682             text = text.replace(/^\s+/,''); // trim left
46683           
46684         }
46685         // should trim right?
46686         if (node.nextSibling &&
46687             node.nextSibling.nodeType == 1 &&
46688             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
46689         {
46690           // noop
46691             
46692         }  else {
46693             text = text.replace(/\s+$/,''); // trim right
46694         }
46695          
46696               
46697         
46698         
46699         
46700         if (text.length < 1) {
46701             return;
46702         }
46703         if (!text.match(/\n/)) {
46704             this.html.push(indentstr + text);
46705             return;
46706         }
46707         
46708         text = this.indentstr + text.replace(
46709             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
46710         );
46711         // remoeve the last whitespace / line break.
46712         text = text.replace(/\s+$/,''); 
46713         
46714         this.html.push(text);
46715         
46716         // split and indent..
46717         
46718         
46719     },
46720     /**
46721      * Writes a cdata node such as <![CDATA[data]]>.
46722      *
46723      * @method cdata
46724      * @param {String} text String to write out inside the cdata.
46725      */
46726     cdata: function(text) {
46727         this.html.push('<![CDATA[', text, ']]>');
46728     },
46729     /**
46730     * Writes a comment node such as <!-- Comment -->.
46731     *
46732     * @method cdata
46733     * @param {String} text String to write out inside the comment.
46734     */
46735    comment: function(text) {
46736        this.html.push('<!--', text, '-->');
46737    },
46738     /**
46739      * Writes a PI node such as <?xml attr="value" ?>.
46740      *
46741      * @method pi
46742      * @param {String} name Name of the pi.
46743      * @param {String} text String to write out inside the pi.
46744      */
46745     pi: function(name, text) {
46746         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
46747         this.indent != '' && this.html.push('\n');
46748     },
46749     /**
46750      * Writes a doctype node such as <!DOCTYPE data>.
46751      *
46752      * @method doctype
46753      * @param {String} text String to write out inside the doctype.
46754      */
46755     doctype: function(text) {
46756         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
46757     },
46758     /**
46759      * Resets the internal buffer if one wants to reuse the writer.
46760      *
46761      * @method reset
46762      */
46763     reset: function() {
46764         this.html.length = 0;
46765         this.state = [];
46766         this.pushState({
46767             indentstr : '',
46768             in_pre : false, 
46769             in_inline : false
46770         })
46771     },
46772     /**
46773      * Returns the contents that got serialized.
46774      *
46775      * @method getContent
46776      * @return {String} HTML contents that got written down.
46777      */
46778     getContent: function() {
46779         return this.html.join('').replace(/\n$/, '');
46780     },
46781     
46782     pushState : function(cfg)
46783     {
46784         this.state.push(cfg);
46785         Roo.apply(this, cfg);
46786     },
46787     
46788     popState : function()
46789     {
46790         if (this.state.length < 1) {
46791             return; // nothing to push
46792         }
46793         var cfg = {
46794             in_pre: false,
46795             indentstr : ''
46796         };
46797         this.state.pop();
46798         if (this.state.length > 0) {
46799             cfg = this.state[this.state.length-1]; 
46800         }
46801         Roo.apply(this, cfg);
46802     },
46803     
46804     addLine: function()
46805     {
46806         if (this.html.length < 1) {
46807             return;
46808         }
46809         
46810         
46811         var value = this.html[this.html.length - 1];
46812         if (value.length > 0 && '\n' !== value) {
46813             this.html.push('\n');
46814         }
46815     }
46816     
46817     
46818 //'pre script noscript style textarea video audio iframe object code'
46819 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
46820 // inline 
46821 };
46822
46823 Roo.htmleditor.TidyWriter.inline_elements = [
46824         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
46825         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
46826 ];
46827 Roo.htmleditor.TidyWriter.shortend_elements = [
46828     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
46829     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
46830 ];
46831
46832 Roo.htmleditor.TidyWriter.whitespace_elements = [
46833     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
46834 ];/***
46835  * This is based loosely on tinymce 
46836  * @class Roo.htmleditor.TidyEntities
46837  * @static
46838  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46839  *
46840  * Not 100% sure this is actually used or needed.
46841  */
46842
46843 Roo.htmleditor.TidyEntities = {
46844     
46845     /**
46846      * initialize data..
46847      */
46848     init : function (){
46849      
46850         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
46851        
46852     },
46853
46854
46855     buildEntitiesLookup: function(items, radix) {
46856         var i, chr, entity, lookup = {};
46857         if (!items) {
46858             return {};
46859         }
46860         items = typeof(items) == 'string' ? items.split(',') : items;
46861         radix = radix || 10;
46862         // Build entities lookup table
46863         for (i = 0; i < items.length; i += 2) {
46864             chr = String.fromCharCode(parseInt(items[i], radix));
46865             // Only add non base entities
46866             if (!this.baseEntities[chr]) {
46867                 entity = '&' + items[i + 1] + ';';
46868                 lookup[chr] = entity;
46869                 lookup[entity] = chr;
46870             }
46871         }
46872         return lookup;
46873         
46874     },
46875     
46876     asciiMap : {
46877             128: '€',
46878             130: '‚',
46879             131: 'ƒ',
46880             132: '„',
46881             133: '…',
46882             134: '†',
46883             135: '‡',
46884             136: 'ˆ',
46885             137: '‰',
46886             138: 'Š',
46887             139: '‹',
46888             140: 'Œ',
46889             142: 'Ž',
46890             145: '‘',
46891             146: '’',
46892             147: '“',
46893             148: '”',
46894             149: '•',
46895             150: '–',
46896             151: '—',
46897             152: '˜',
46898             153: '™',
46899             154: 'š',
46900             155: '›',
46901             156: 'œ',
46902             158: 'ž',
46903             159: 'Ÿ'
46904     },
46905     // Raw entities
46906     baseEntities : {
46907         '"': '&quot;',
46908         // Needs to be escaped since the YUI compressor would otherwise break the code
46909         '\'': '&#39;',
46910         '<': '&lt;',
46911         '>': '&gt;',
46912         '&': '&amp;',
46913         '`': '&#96;'
46914     },
46915     // Reverse lookup table for raw entities
46916     reverseEntities : {
46917         '&lt;': '<',
46918         '&gt;': '>',
46919         '&amp;': '&',
46920         '&quot;': '"',
46921         '&apos;': '\''
46922     },
46923     
46924     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
46925     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
46926     rawCharsRegExp : /[<>&\"\']/g,
46927     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
46928     namedEntities  : false,
46929     namedEntitiesData : [ 
46930         '50',
46931         'nbsp',
46932         '51',
46933         'iexcl',
46934         '52',
46935         'cent',
46936         '53',
46937         'pound',
46938         '54',
46939         'curren',
46940         '55',
46941         'yen',
46942         '56',
46943         'brvbar',
46944         '57',
46945         'sect',
46946         '58',
46947         'uml',
46948         '59',
46949         'copy',
46950         '5a',
46951         'ordf',
46952         '5b',
46953         'laquo',
46954         '5c',
46955         'not',
46956         '5d',
46957         'shy',
46958         '5e',
46959         'reg',
46960         '5f',
46961         'macr',
46962         '5g',
46963         'deg',
46964         '5h',
46965         'plusmn',
46966         '5i',
46967         'sup2',
46968         '5j',
46969         'sup3',
46970         '5k',
46971         'acute',
46972         '5l',
46973         'micro',
46974         '5m',
46975         'para',
46976         '5n',
46977         'middot',
46978         '5o',
46979         'cedil',
46980         '5p',
46981         'sup1',
46982         '5q',
46983         'ordm',
46984         '5r',
46985         'raquo',
46986         '5s',
46987         'frac14',
46988         '5t',
46989         'frac12',
46990         '5u',
46991         'frac34',
46992         '5v',
46993         'iquest',
46994         '60',
46995         'Agrave',
46996         '61',
46997         'Aacute',
46998         '62',
46999         'Acirc',
47000         '63',
47001         'Atilde',
47002         '64',
47003         'Auml',
47004         '65',
47005         'Aring',
47006         '66',
47007         'AElig',
47008         '67',
47009         'Ccedil',
47010         '68',
47011         'Egrave',
47012         '69',
47013         'Eacute',
47014         '6a',
47015         'Ecirc',
47016         '6b',
47017         'Euml',
47018         '6c',
47019         'Igrave',
47020         '6d',
47021         'Iacute',
47022         '6e',
47023         'Icirc',
47024         '6f',
47025         'Iuml',
47026         '6g',
47027         'ETH',
47028         '6h',
47029         'Ntilde',
47030         '6i',
47031         'Ograve',
47032         '6j',
47033         'Oacute',
47034         '6k',
47035         'Ocirc',
47036         '6l',
47037         'Otilde',
47038         '6m',
47039         'Ouml',
47040         '6n',
47041         'times',
47042         '6o',
47043         'Oslash',
47044         '6p',
47045         'Ugrave',
47046         '6q',
47047         'Uacute',
47048         '6r',
47049         'Ucirc',
47050         '6s',
47051         'Uuml',
47052         '6t',
47053         'Yacute',
47054         '6u',
47055         'THORN',
47056         '6v',
47057         'szlig',
47058         '70',
47059         'agrave',
47060         '71',
47061         'aacute',
47062         '72',
47063         'acirc',
47064         '73',
47065         'atilde',
47066         '74',
47067         'auml',
47068         '75',
47069         'aring',
47070         '76',
47071         'aelig',
47072         '77',
47073         'ccedil',
47074         '78',
47075         'egrave',
47076         '79',
47077         'eacute',
47078         '7a',
47079         'ecirc',
47080         '7b',
47081         'euml',
47082         '7c',
47083         'igrave',
47084         '7d',
47085         'iacute',
47086         '7e',
47087         'icirc',
47088         '7f',
47089         'iuml',
47090         '7g',
47091         'eth',
47092         '7h',
47093         'ntilde',
47094         '7i',
47095         'ograve',
47096         '7j',
47097         'oacute',
47098         '7k',
47099         'ocirc',
47100         '7l',
47101         'otilde',
47102         '7m',
47103         'ouml',
47104         '7n',
47105         'divide',
47106         '7o',
47107         'oslash',
47108         '7p',
47109         'ugrave',
47110         '7q',
47111         'uacute',
47112         '7r',
47113         'ucirc',
47114         '7s',
47115         'uuml',
47116         '7t',
47117         'yacute',
47118         '7u',
47119         'thorn',
47120         '7v',
47121         'yuml',
47122         'ci',
47123         'fnof',
47124         'sh',
47125         'Alpha',
47126         'si',
47127         'Beta',
47128         'sj',
47129         'Gamma',
47130         'sk',
47131         'Delta',
47132         'sl',
47133         'Epsilon',
47134         'sm',
47135         'Zeta',
47136         'sn',
47137         'Eta',
47138         'so',
47139         'Theta',
47140         'sp',
47141         'Iota',
47142         'sq',
47143         'Kappa',
47144         'sr',
47145         'Lambda',
47146         'ss',
47147         'Mu',
47148         'st',
47149         'Nu',
47150         'su',
47151         'Xi',
47152         'sv',
47153         'Omicron',
47154         't0',
47155         'Pi',
47156         't1',
47157         'Rho',
47158         't3',
47159         'Sigma',
47160         't4',
47161         'Tau',
47162         't5',
47163         'Upsilon',
47164         't6',
47165         'Phi',
47166         't7',
47167         'Chi',
47168         't8',
47169         'Psi',
47170         't9',
47171         'Omega',
47172         'th',
47173         'alpha',
47174         'ti',
47175         'beta',
47176         'tj',
47177         'gamma',
47178         'tk',
47179         'delta',
47180         'tl',
47181         'epsilon',
47182         'tm',
47183         'zeta',
47184         'tn',
47185         'eta',
47186         'to',
47187         'theta',
47188         'tp',
47189         'iota',
47190         'tq',
47191         'kappa',
47192         'tr',
47193         'lambda',
47194         'ts',
47195         'mu',
47196         'tt',
47197         'nu',
47198         'tu',
47199         'xi',
47200         'tv',
47201         'omicron',
47202         'u0',
47203         'pi',
47204         'u1',
47205         'rho',
47206         'u2',
47207         'sigmaf',
47208         'u3',
47209         'sigma',
47210         'u4',
47211         'tau',
47212         'u5',
47213         'upsilon',
47214         'u6',
47215         'phi',
47216         'u7',
47217         'chi',
47218         'u8',
47219         'psi',
47220         'u9',
47221         'omega',
47222         'uh',
47223         'thetasym',
47224         'ui',
47225         'upsih',
47226         'um',
47227         'piv',
47228         '812',
47229         'bull',
47230         '816',
47231         'hellip',
47232         '81i',
47233         'prime',
47234         '81j',
47235         'Prime',
47236         '81u',
47237         'oline',
47238         '824',
47239         'frasl',
47240         '88o',
47241         'weierp',
47242         '88h',
47243         'image',
47244         '88s',
47245         'real',
47246         '892',
47247         'trade',
47248         '89l',
47249         'alefsym',
47250         '8cg',
47251         'larr',
47252         '8ch',
47253         'uarr',
47254         '8ci',
47255         'rarr',
47256         '8cj',
47257         'darr',
47258         '8ck',
47259         'harr',
47260         '8dl',
47261         'crarr',
47262         '8eg',
47263         'lArr',
47264         '8eh',
47265         'uArr',
47266         '8ei',
47267         'rArr',
47268         '8ej',
47269         'dArr',
47270         '8ek',
47271         'hArr',
47272         '8g0',
47273         'forall',
47274         '8g2',
47275         'part',
47276         '8g3',
47277         'exist',
47278         '8g5',
47279         'empty',
47280         '8g7',
47281         'nabla',
47282         '8g8',
47283         'isin',
47284         '8g9',
47285         'notin',
47286         '8gb',
47287         'ni',
47288         '8gf',
47289         'prod',
47290         '8gh',
47291         'sum',
47292         '8gi',
47293         'minus',
47294         '8gn',
47295         'lowast',
47296         '8gq',
47297         'radic',
47298         '8gt',
47299         'prop',
47300         '8gu',
47301         'infin',
47302         '8h0',
47303         'ang',
47304         '8h7',
47305         'and',
47306         '8h8',
47307         'or',
47308         '8h9',
47309         'cap',
47310         '8ha',
47311         'cup',
47312         '8hb',
47313         'int',
47314         '8hk',
47315         'there4',
47316         '8hs',
47317         'sim',
47318         '8i5',
47319         'cong',
47320         '8i8',
47321         'asymp',
47322         '8j0',
47323         'ne',
47324         '8j1',
47325         'equiv',
47326         '8j4',
47327         'le',
47328         '8j5',
47329         'ge',
47330         '8k2',
47331         'sub',
47332         '8k3',
47333         'sup',
47334         '8k4',
47335         'nsub',
47336         '8k6',
47337         'sube',
47338         '8k7',
47339         'supe',
47340         '8kl',
47341         'oplus',
47342         '8kn',
47343         'otimes',
47344         '8l5',
47345         'perp',
47346         '8m5',
47347         'sdot',
47348         '8o8',
47349         'lceil',
47350         '8o9',
47351         'rceil',
47352         '8oa',
47353         'lfloor',
47354         '8ob',
47355         'rfloor',
47356         '8p9',
47357         'lang',
47358         '8pa',
47359         'rang',
47360         '9ea',
47361         'loz',
47362         '9j0',
47363         'spades',
47364         '9j3',
47365         'clubs',
47366         '9j5',
47367         'hearts',
47368         '9j6',
47369         'diams',
47370         'ai',
47371         'OElig',
47372         'aj',
47373         'oelig',
47374         'b0',
47375         'Scaron',
47376         'b1',
47377         'scaron',
47378         'bo',
47379         'Yuml',
47380         'm6',
47381         'circ',
47382         'ms',
47383         'tilde',
47384         '802',
47385         'ensp',
47386         '803',
47387         'emsp',
47388         '809',
47389         'thinsp',
47390         '80c',
47391         'zwnj',
47392         '80d',
47393         'zwj',
47394         '80e',
47395         'lrm',
47396         '80f',
47397         'rlm',
47398         '80j',
47399         'ndash',
47400         '80k',
47401         'mdash',
47402         '80o',
47403         'lsquo',
47404         '80p',
47405         'rsquo',
47406         '80q',
47407         'sbquo',
47408         '80s',
47409         'ldquo',
47410         '80t',
47411         'rdquo',
47412         '80u',
47413         'bdquo',
47414         '810',
47415         'dagger',
47416         '811',
47417         'Dagger',
47418         '81g',
47419         'permil',
47420         '81p',
47421         'lsaquo',
47422         '81q',
47423         'rsaquo',
47424         '85c',
47425         'euro'
47426     ],
47427
47428          
47429     /**
47430      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
47431      *
47432      * @method encodeRaw
47433      * @param {String} text Text to encode.
47434      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47435      * @return {String} Entity encoded text.
47436      */
47437     encodeRaw: function(text, attr)
47438     {
47439         var t = this;
47440         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47441             return t.baseEntities[chr] || chr;
47442         });
47443     },
47444     /**
47445      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
47446      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
47447      * and is exposed as the DOMUtils.encode function.
47448      *
47449      * @method encodeAllRaw
47450      * @param {String} text Text to encode.
47451      * @return {String} Entity encoded text.
47452      */
47453     encodeAllRaw: function(text) {
47454         var t = this;
47455         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
47456             return t.baseEntities[chr] || chr;
47457         });
47458     },
47459     /**
47460      * Encodes the specified string using numeric entities. The core entities will be
47461      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
47462      *
47463      * @method encodeNumeric
47464      * @param {String} text Text to encode.
47465      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47466      * @return {String} Entity encoded text.
47467      */
47468     encodeNumeric: function(text, attr) {
47469         var t = this;
47470         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47471             // Multi byte sequence convert it to a single entity
47472             if (chr.length > 1) {
47473                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
47474             }
47475             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
47476         });
47477     },
47478     /**
47479      * Encodes the specified string using named entities. The core entities will be encoded
47480      * as named ones but all non lower ascii characters will be encoded into named entities.
47481      *
47482      * @method encodeNamed
47483      * @param {String} text Text to encode.
47484      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47485      * @param {Object} entities Optional parameter with entities to use.
47486      * @return {String} Entity encoded text.
47487      */
47488     encodeNamed: function(text, attr, entities) {
47489         var t = this;
47490         entities = entities || this.namedEntities;
47491         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47492             return t.baseEntities[chr] || entities[chr] || chr;
47493         });
47494     },
47495     /**
47496      * Returns an encode function based on the name(s) and it's optional entities.
47497      *
47498      * @method getEncodeFunc
47499      * @param {String} name Comma separated list of encoders for example named,numeric.
47500      * @param {String} entities Optional parameter with entities to use instead of the built in set.
47501      * @return {function} Encode function to be used.
47502      */
47503     getEncodeFunc: function(name, entities) {
47504         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
47505         var t = this;
47506         function encodeNamedAndNumeric(text, attr) {
47507             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
47508                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
47509             });
47510         }
47511
47512         function encodeCustomNamed(text, attr) {
47513             return t.encodeNamed(text, attr, entities);
47514         }
47515         // Replace + with , to be compatible with previous TinyMCE versions
47516         name = this.makeMap(name.replace(/\+/g, ','));
47517         // Named and numeric encoder
47518         if (name.named && name.numeric) {
47519             return this.encodeNamedAndNumeric;
47520         }
47521         // Named encoder
47522         if (name.named) {
47523             // Custom names
47524             if (entities) {
47525                 return encodeCustomNamed;
47526             }
47527             return this.encodeNamed;
47528         }
47529         // Numeric
47530         if (name.numeric) {
47531             return this.encodeNumeric;
47532         }
47533         // Raw encoder
47534         return this.encodeRaw;
47535     },
47536     /**
47537      * Decodes the specified string, this will replace entities with raw UTF characters.
47538      *
47539      * @method decode
47540      * @param {String} text Text to entity decode.
47541      * @return {String} Entity decoded string.
47542      */
47543     decode: function(text)
47544     {
47545         var  t = this;
47546         return text.replace(this.entityRegExp, function(all, numeric) {
47547             if (numeric) {
47548                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
47549                 // Support upper UTF
47550                 if (numeric > 65535) {
47551                     numeric -= 65536;
47552                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
47553                 }
47554                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
47555             }
47556             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
47557         });
47558     },
47559     nativeDecode : function (text) {
47560         return text;
47561     },
47562     makeMap : function (items, delim, map) {
47563                 var i;
47564                 items = items || [];
47565                 delim = delim || ',';
47566                 if (typeof items == "string") {
47567                         items = items.split(delim);
47568                 }
47569                 map = map || {};
47570                 i = items.length;
47571                 while (i--) {
47572                         map[items[i]] = {};
47573                 }
47574                 return map;
47575         }
47576 };
47577     
47578     
47579     
47580 Roo.htmleditor.TidyEntities.init();
47581 /**
47582  * @class Roo.htmleditor.KeyEnter
47583  * Handle Enter press..
47584  * @cfg {Roo.HtmlEditorCore} core the editor.
47585  * @constructor
47586  * Create a new Filter.
47587  * @param {Object} config Configuration options
47588  */
47589
47590
47591
47592
47593
47594 Roo.htmleditor.KeyEnter = function(cfg) {
47595     Roo.apply(this, cfg);
47596     // this does not actually call walk as it's really just a abstract class
47597  
47598     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
47599 }
47600
47601 //Roo.htmleditor.KeyEnter.i = 0;
47602
47603
47604 Roo.htmleditor.KeyEnter.prototype = {
47605     
47606     core : false,
47607     
47608     keypress : function(e)
47609     {
47610         if (e.charCode != 13 && e.charCode != 10) {
47611             Roo.log([e.charCode,e]);
47612             return true;
47613         }
47614         e.preventDefault();
47615         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
47616         var doc = this.core.doc;
47617           //add a new line
47618        
47619     
47620         var sel = this.core.getSelection();
47621         var range = sel.getRangeAt(0);
47622         var n = range.commonAncestorContainer;
47623         var pc = range.closest([ 'ol', 'ul']);
47624         var pli = range.closest('li');
47625         if (!pc || e.ctrlKey) {
47626             sel.insertNode('br', 'after'); 
47627          
47628             this.core.undoManager.addEvent();
47629             this.core.fireEditorEvent(e);
47630             return false;
47631         }
47632         
47633         // deal with <li> insetion
47634         if (pli.innerText.trim() == '' &&
47635             pli.previousSibling &&
47636             pli.previousSibling.nodeName == 'LI' &&
47637             pli.previousSibling.innerText.trim() ==  '') {
47638             pli.parentNode.removeChild(pli.previousSibling);
47639             sel.cursorAfter(pc);
47640             this.core.undoManager.addEvent();
47641             this.core.fireEditorEvent(e);
47642             return false;
47643         }
47644     
47645         var li = doc.createElement('LI');
47646         li.innerHTML = '&nbsp;';
47647         if (!pli || !pli.firstSibling) {
47648             pc.appendChild(li);
47649         } else {
47650             pli.parentNode.insertBefore(li, pli.firstSibling);
47651         }
47652         sel.cursorText (li.firstChild);
47653       
47654         this.core.undoManager.addEvent();
47655         this.core.fireEditorEvent(e);
47656
47657         return false;
47658         
47659     
47660         
47661         
47662          
47663     }
47664 };
47665      
47666 /**
47667  * @class Roo.htmleditor.Block
47668  * Base class for html editor blocks - do not use it directly .. extend it..
47669  * @cfg {DomElement} node The node to apply stuff to.
47670  * @cfg {String} friendly_name the name that appears in the context bar about this block
47671  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
47672  
47673  * @constructor
47674  * Create a new Filter.
47675  * @param {Object} config Configuration options
47676  */
47677
47678 Roo.htmleditor.Block  = function(cfg)
47679 {
47680     // do nothing .. should not be called really.
47681 }
47682 /**
47683  * factory method to get the block from an element (using cache if necessary)
47684  * @static
47685  * @param {HtmlElement} the dom element
47686  */
47687 Roo.htmleditor.Block.factory = function(node)
47688 {
47689     var cc = Roo.htmleditor.Block.cache;
47690     var id = Roo.get(node).id;
47691     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
47692         Roo.htmleditor.Block.cache[id].readElement(node);
47693         return Roo.htmleditor.Block.cache[id];
47694     }
47695     var db  = node.getAttribute('data-block');
47696     if (!db) {
47697         db = node.nodeName.toLowerCase().toUpperCaseFirst();
47698     }
47699     var cls = Roo.htmleditor['Block' + db];
47700     if (typeof(cls) == 'undefined') {
47701         //Roo.log(node.getAttribute('data-block'));
47702         Roo.log("OOps missing block : " + 'Block' + db);
47703         return false;
47704     }
47705     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
47706     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
47707 };
47708
47709 /**
47710  * initalize all Elements from content that are 'blockable'
47711  * @static
47712  * @param the body element
47713  */
47714 Roo.htmleditor.Block.initAll = function(body, type)
47715 {
47716     if (typeof(type) == 'undefined') {
47717         var ia = Roo.htmleditor.Block.initAll;
47718         ia(body,'table');
47719         ia(body,'td');
47720         ia(body,'figure');
47721         return;
47722     }
47723     Roo.each(Roo.get(body).query(type), function(e) {
47724         Roo.htmleditor.Block.factory(e);    
47725     },this);
47726 };
47727 // question goes here... do we need to clear out this cache sometimes?
47728 // or show we make it relivant to the htmleditor.
47729 Roo.htmleditor.Block.cache = {};
47730
47731 Roo.htmleditor.Block.prototype = {
47732     
47733     node : false,
47734     
47735      // used by context menu
47736     friendly_name : 'Based Block',
47737     
47738     // text for button to delete this element
47739     deleteTitle : false,
47740     
47741     context : false,
47742     /**
47743      * Update a node with values from this object
47744      * @param {DomElement} node
47745      */
47746     updateElement : function(node)
47747     {
47748         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
47749     },
47750      /**
47751      * convert to plain HTML for calling insertAtCursor..
47752      */
47753     toHTML : function()
47754     {
47755         return Roo.DomHelper.markup(this.toObject());
47756     },
47757     /**
47758      * used by readEleemnt to extract data from a node
47759      * may need improving as it's pretty basic
47760      
47761      * @param {DomElement} node
47762      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
47763      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
47764      * @param {String} style the style property - eg. text-align
47765      */
47766     getVal : function(node, tag, attr, style)
47767     {
47768         var n = node;
47769         if (tag !== true && n.tagName != tag.toUpperCase()) {
47770             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
47771             // but kiss for now.
47772             n = node.getElementsByTagName(tag).item(0);
47773         }
47774         if (!n) {
47775             return '';
47776         }
47777         if (attr === false) {
47778             return n;
47779         }
47780         if (attr == 'html') {
47781             return n.innerHTML;
47782         }
47783         if (attr == 'style') {
47784             return n.style[style]; 
47785         }
47786         
47787         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
47788             
47789     },
47790     /**
47791      * create a DomHelper friendly object - for use with 
47792      * Roo.DomHelper.markup / overwrite / etc..
47793      * (override this)
47794      */
47795     toObject : function()
47796     {
47797         return {};
47798     },
47799       /**
47800      * Read a node that has a 'data-block' property - and extract the values from it.
47801      * @param {DomElement} node - the node
47802      */
47803     readElement : function(node)
47804     {
47805         
47806     } 
47807     
47808     
47809 };
47810
47811  
47812
47813 /**
47814  * @class Roo.htmleditor.BlockFigure
47815  * Block that has an image and a figcaption
47816  * @cfg {String} image_src the url for the image
47817  * @cfg {String} align (left|right) alignment for the block default left
47818  * @cfg {String} caption the text to appear below  (and in the alt tag)
47819  * @cfg {String} caption_display (block|none) display or not the caption
47820  * @cfg {String|number} image_width the width of the image number or %?
47821  * @cfg {String|number} image_height the height of the image number or %?
47822  * 
47823  * @constructor
47824  * Create a new Filter.
47825  * @param {Object} config Configuration options
47826  */
47827
47828 Roo.htmleditor.BlockFigure = function(cfg)
47829 {
47830     if (cfg.node) {
47831         this.readElement(cfg.node);
47832         this.updateElement(cfg.node);
47833     }
47834     Roo.apply(this, cfg);
47835 }
47836 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
47837  
47838     
47839     // setable values.
47840     image_src: '',
47841     align: 'center',
47842     caption : '',
47843     caption_display : 'block',
47844     width : '100%',
47845     cls : '',
47846     href: '',
47847     video_url : '',
47848     
47849     // margin: '2%', not used
47850     
47851     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
47852
47853     
47854     // used by context menu
47855     friendly_name : 'Image with caption',
47856     deleteTitle : "Delete Image and Caption",
47857     
47858     contextMenu : function(toolbar)
47859     {
47860         
47861         var block = function() {
47862             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
47863         };
47864         
47865         
47866         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
47867         
47868         var syncValue = toolbar.editorcore.syncValue;
47869         
47870         var fields = {};
47871         
47872         return [
47873              {
47874                 xtype : 'TextItem',
47875                 text : "Source: ",
47876                 xns : rooui.Toolbar  //Boostrap?
47877             },
47878             {
47879                 xtype : 'Button',
47880                 text: 'Change Image URL',
47881                  
47882                 listeners : {
47883                     click: function (btn, state)
47884                     {
47885                         var b = block();
47886                         
47887                         Roo.MessageBox.show({
47888                             title : "Image Source URL",
47889                             msg : "Enter the url for the image",
47890                             buttons: Roo.MessageBox.OKCANCEL,
47891                             fn: function(btn, val){
47892                                 if (btn != 'ok') {
47893                                     return;
47894                                 }
47895                                 b.image_src = val;
47896                                 b.updateElement();
47897                                 syncValue();
47898                                 toolbar.editorcore.onEditorEvent();
47899                             },
47900                             minWidth:250,
47901                             prompt:true,
47902                             //multiline: multiline,
47903                             modal : true,
47904                             value : b.image_src
47905                         });
47906                     }
47907                 },
47908                 xns : rooui.Toolbar
47909             },
47910          
47911             {
47912                 xtype : 'Button',
47913                 text: 'Change Link URL',
47914                  
47915                 listeners : {
47916                     click: function (btn, state)
47917                     {
47918                         var b = block();
47919                         
47920                         Roo.MessageBox.show({
47921                             title : "Link URL",
47922                             msg : "Enter the url for the link - leave blank to have no link",
47923                             buttons: Roo.MessageBox.OKCANCEL,
47924                             fn: function(btn, val){
47925                                 if (btn != 'ok') {
47926                                     return;
47927                                 }
47928                                 b.href = val;
47929                                 b.updateElement();
47930                                 syncValue();
47931                                 toolbar.editorcore.onEditorEvent();
47932                             },
47933                             minWidth:250,
47934                             prompt:true,
47935                             //multiline: multiline,
47936                             modal : true,
47937                             value : b.href
47938                         });
47939                     }
47940                 },
47941                 xns : rooui.Toolbar
47942             },
47943             {
47944                 xtype : 'Button',
47945                 text: 'Show Video URL',
47946                  
47947                 listeners : {
47948                     click: function (btn, state)
47949                     {
47950                         Roo.MessageBox.alert("Video URL",
47951                             block().video_url == '' ? 'This image is not linked ot a video' :
47952                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
47953                     }
47954                 },
47955                 xns : rooui.Toolbar
47956             },
47957             
47958             
47959             {
47960                 xtype : 'TextItem',
47961                 text : "Width: ",
47962                 xns : rooui.Toolbar  //Boostrap?
47963             },
47964             {
47965                 xtype : 'ComboBox',
47966                 allowBlank : false,
47967                 displayField : 'val',
47968                 editable : true,
47969                 listWidth : 100,
47970                 triggerAction : 'all',
47971                 typeAhead : true,
47972                 valueField : 'val',
47973                 width : 70,
47974                 name : 'width',
47975                 listeners : {
47976                     select : function (combo, r, index)
47977                     {
47978                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
47979                         var b = block();
47980                         b.width = r.get('val');
47981                         b.updateElement();
47982                         syncValue();
47983                         toolbar.editorcore.onEditorEvent();
47984                     }
47985                 },
47986                 xns : rooui.form,
47987                 store : {
47988                     xtype : 'SimpleStore',
47989                     data : [
47990                         ['auto'],
47991                         ['50%'],
47992                         ['100%']
47993                     ],
47994                     fields : [ 'val'],
47995                     xns : Roo.data
47996                 }
47997             },
47998             {
47999                 xtype : 'TextItem',
48000                 text : "Align: ",
48001                 xns : rooui.Toolbar  //Boostrap?
48002             },
48003             {
48004                 xtype : 'ComboBox',
48005                 allowBlank : false,
48006                 displayField : 'val',
48007                 editable : true,
48008                 listWidth : 100,
48009                 triggerAction : 'all',
48010                 typeAhead : true,
48011                 valueField : 'val',
48012                 width : 70,
48013                 name : 'align',
48014                 listeners : {
48015                     select : function (combo, r, index)
48016                     {
48017                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48018                         var b = block();
48019                         b.align = r.get('val');
48020                         b.updateElement();
48021                         syncValue();
48022                         toolbar.editorcore.onEditorEvent();
48023                     }
48024                 },
48025                 xns : rooui.form,
48026                 store : {
48027                     xtype : 'SimpleStore',
48028                     data : [
48029                         ['left'],
48030                         ['right'],
48031                         ['center']
48032                     ],
48033                     fields : [ 'val'],
48034                     xns : Roo.data
48035                 }
48036             },
48037             
48038             
48039             {
48040                 xtype : 'Button',
48041                 text: 'Hide Caption',
48042                 name : 'caption_display',
48043                 pressed : false,
48044                 enableToggle : true,
48045                 setValue : function(v) {
48046                     this.toggle(v == 'block' ? false : true);
48047                 },
48048                 listeners : {
48049                     toggle: function (btn, state)
48050                     {
48051                         var b  = block();
48052                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
48053                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
48054                         b.updateElement();
48055                         syncValue();
48056                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48057                         toolbar.editorcore.onEditorEvent();
48058                     }
48059                 },
48060                 xns : rooui.Toolbar
48061             }
48062         ];
48063         
48064     },
48065     /**
48066      * create a DomHelper friendly object - for use with
48067      * Roo.DomHelper.markup / overwrite / etc..
48068      */
48069     toObject : function()
48070     {
48071         var d = document.createElement('div');
48072         d.innerHTML = this.caption;
48073         
48074         var m = this.width == '50%' && this.align == 'center' ? '0 auto' : 0; 
48075         
48076         var iw = this.align == 'center' ? this.width : '100%';
48077         var img =   {
48078             tag : 'img',
48079             contenteditable : 'false',
48080             src : this.image_src,
48081             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
48082             style: {
48083                 width : iw,
48084                 maxWidth : iw + ' !important', // this is not getting rendered?
48085                 margin : m 
48086                 
48087                 
48088             }
48089         };
48090         /*
48091         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
48092                     '<a href="{2}">' + 
48093                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
48094                     '</a>' + 
48095                 '</div>',
48096         */
48097                 
48098         if (this.href.length > 0) {
48099             img = {
48100                 tag : 'a',
48101                 href: this.href,
48102                 contenteditable : 'true',
48103                 cn : [
48104                     img
48105                 ]
48106             };
48107         }
48108         
48109         
48110         if (this.video_url.length > 0) {
48111             img = {
48112                 tag : 'div',
48113                 cls : this.cls,
48114                 frameborder : 0,
48115                 allowfullscreen : true,
48116                 width : 420,  // these are for video tricks - that we replace the outer
48117                 height : 315,
48118                 src : this.video_url,
48119                 cn : [
48120                     img
48121                 ]
48122             };
48123         }
48124         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
48125         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
48126         
48127         return  {
48128             tag: 'figure',
48129             'data-block' : 'Figure',
48130             
48131             contenteditable : 'false',
48132             
48133             style : {
48134                 display: 'block',
48135                 float :  this.align ,
48136                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
48137                 width : this.align == 'center' ? '100%' : this.width,
48138                 margin:  '0px',
48139                 padding: '10px',
48140                 textAlign : this.align   // seems to work for email..
48141                 
48142             },
48143            
48144             
48145             align : this.align,
48146             cn : [
48147                 img,
48148               
48149                 {
48150                     tag: 'figcaption',
48151                     'data-display' : this.caption_display,
48152                     style : {
48153                         textAlign : 'left',
48154                         fontSize : '16px',
48155                         lineHeight : '24px',
48156                         display : this.caption_display,
48157                         maxWidth : this.width + ' !important',
48158                         margin: m,
48159                         width: this.width
48160                     
48161                          
48162                     },
48163                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
48164                     cn : [
48165                         {
48166                             tag: 'div',
48167                             style  : {
48168                                 marginTop : '16px',
48169                                 textAlign : 'left'
48170                             },
48171                             align: 'left',
48172                             cn : [
48173                                 {
48174                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
48175                                     tag : 'i',
48176                                     contenteditable : true,
48177                                     html : captionhtml
48178                                 }
48179                                 
48180                             ]
48181                         }
48182                         
48183                     ]
48184                     
48185                 }
48186             ]
48187         };
48188          
48189     },
48190     
48191     readElement : function(node)
48192     {
48193         // this should not really come from the link...
48194         this.video_url = this.getVal(node, 'div', 'src');
48195         this.cls = this.getVal(node, 'div', 'class');
48196         this.href = this.getVal(node, 'a', 'href');
48197         
48198         
48199         this.image_src = this.getVal(node, 'img', 'src');
48200          
48201         this.align = this.getVal(node, 'figure', 'align');
48202         var figcaption = this.getVal(node, 'figcaption', false);
48203         this.caption = this.getVal(figcaption, 'i', 'html');
48204
48205         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
48206         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
48207         this.width = this.getVal(node, 'figcaption', 'style', 'width');
48208         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
48209         
48210     },
48211     removeNode : function()
48212     {
48213         return this.node;
48214     }
48215     
48216   
48217    
48218      
48219     
48220     
48221     
48222     
48223 })
48224
48225  
48226
48227 /**
48228  * @class Roo.htmleditor.BlockTable
48229  * Block that manages a table
48230  * 
48231  * @constructor
48232  * Create a new Filter.
48233  * @param {Object} config Configuration options
48234  */
48235
48236 Roo.htmleditor.BlockTable = function(cfg)
48237 {
48238     if (cfg.node) {
48239         this.readElement(cfg.node);
48240         this.updateElement(cfg.node);
48241     }
48242     Roo.apply(this, cfg);
48243     if (!cfg.node) {
48244         this.rows = [];
48245         for(var r = 0; r < this.no_row; r++) {
48246             this.rows[r] = [];
48247             for(var c = 0; c < this.no_col; c++) {
48248                 this.rows[r][c] = this.emptyCell();
48249             }
48250         }
48251     }
48252     
48253     
48254 }
48255 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
48256  
48257     rows : false,
48258     no_col : 1,
48259     no_row : 1,
48260     
48261     
48262     width: '100%',
48263     
48264     // used by context menu
48265     friendly_name : 'Table',
48266     deleteTitle : 'Delete Table',
48267     // context menu is drawn once..
48268     
48269     contextMenu : function(toolbar)
48270     {
48271         
48272         var block = function() {
48273             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48274         };
48275         
48276         
48277         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48278         
48279         var syncValue = toolbar.editorcore.syncValue;
48280         
48281         var fields = {};
48282         
48283         return [
48284             {
48285                 xtype : 'TextItem',
48286                 text : "Width: ",
48287                 xns : rooui.Toolbar  //Boostrap?
48288             },
48289             {
48290                 xtype : 'ComboBox',
48291                 allowBlank : false,
48292                 displayField : 'val',
48293                 editable : true,
48294                 listWidth : 100,
48295                 triggerAction : 'all',
48296                 typeAhead : true,
48297                 valueField : 'val',
48298                 width : 100,
48299                 name : 'width',
48300                 listeners : {
48301                     select : function (combo, r, index)
48302                     {
48303                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48304                         var b = block();
48305                         b.width = r.get('val');
48306                         b.updateElement();
48307                         syncValue();
48308                         toolbar.editorcore.onEditorEvent();
48309                     }
48310                 },
48311                 xns : rooui.form,
48312                 store : {
48313                     xtype : 'SimpleStore',
48314                     data : [
48315                         ['100%'],
48316                         ['auto']
48317                     ],
48318                     fields : [ 'val'],
48319                     xns : Roo.data
48320                 }
48321             },
48322             // -------- Cols
48323             
48324             {
48325                 xtype : 'TextItem',
48326                 text : "Columns: ",
48327                 xns : rooui.Toolbar  //Boostrap?
48328             },
48329          
48330             {
48331                 xtype : 'Button',
48332                 text: '-',
48333                 listeners : {
48334                     click : function (_self, e)
48335                     {
48336                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48337                         block().removeColumn();
48338                         syncValue();
48339                         toolbar.editorcore.onEditorEvent();
48340                     }
48341                 },
48342                 xns : rooui.Toolbar
48343             },
48344             {
48345                 xtype : 'Button',
48346                 text: '+',
48347                 listeners : {
48348                     click : function (_self, e)
48349                     {
48350                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48351                         block().addColumn();
48352                         syncValue();
48353                         toolbar.editorcore.onEditorEvent();
48354                     }
48355                 },
48356                 xns : rooui.Toolbar
48357             },
48358             // -------- ROWS
48359             {
48360                 xtype : 'TextItem',
48361                 text : "Rows: ",
48362                 xns : rooui.Toolbar  //Boostrap?
48363             },
48364          
48365             {
48366                 xtype : 'Button',
48367                 text: '-',
48368                 listeners : {
48369                     click : function (_self, e)
48370                     {
48371                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48372                         block().removeRow();
48373                         syncValue();
48374                         toolbar.editorcore.onEditorEvent();
48375                     }
48376                 },
48377                 xns : rooui.Toolbar
48378             },
48379             {
48380                 xtype : 'Button',
48381                 text: '+',
48382                 listeners : {
48383                     click : function (_self, e)
48384                     {
48385                         block().addRow();
48386                         syncValue();
48387                         toolbar.editorcore.onEditorEvent();
48388                     }
48389                 },
48390                 xns : rooui.Toolbar
48391             },
48392             // -------- ROWS
48393             {
48394                 xtype : 'Button',
48395                 text: 'Reset Column Widths',
48396                 listeners : {
48397                     
48398                     click : function (_self, e)
48399                     {
48400                         block().resetWidths();
48401                         syncValue();
48402                         toolbar.editorcore.onEditorEvent();
48403                     }
48404                 },
48405                 xns : rooui.Toolbar
48406             } 
48407             
48408             
48409             
48410         ];
48411         
48412     },
48413     
48414     
48415   /**
48416      * create a DomHelper friendly object - for use with
48417      * Roo.DomHelper.markup / overwrite / etc..
48418      * ?? should it be called with option to hide all editing features?
48419      */
48420     toObject : function()
48421     {
48422         
48423         var ret = {
48424             tag : 'table',
48425             contenteditable : 'false', // this stops cell selection from picking the table.
48426             'data-block' : 'Table',
48427             style : {
48428                 width:  this.width,
48429                 border : 'solid 1px #000', // ??? hard coded?
48430                 'border-collapse' : 'collapse' 
48431             },
48432             cn : [
48433                 { tag : 'tbody' , cn : [] }
48434             ]
48435         };
48436         
48437         // do we have a head = not really 
48438         var ncols = 0;
48439         Roo.each(this.rows, function( row ) {
48440             var tr = {
48441                 tag: 'tr',
48442                 style : {
48443                     margin: '6px',
48444                     border : 'solid 1px #000',
48445                     textAlign : 'left' 
48446                 },
48447                 cn : [ ]
48448             };
48449             
48450             ret.cn[0].cn.push(tr);
48451             // does the row have any properties? ?? height?
48452             var nc = 0;
48453             Roo.each(row, function( cell ) {
48454                 
48455                 var td = {
48456                     tag : 'td',
48457                     contenteditable :  'true',
48458                     'data-block' : 'Td',
48459                     html : cell.html,
48460                     style : cell.style
48461                 };
48462                 if (cell.colspan > 1) {
48463                     td.colspan = cell.colspan ;
48464                     nc += cell.colspan;
48465                 } else {
48466                     nc++;
48467                 }
48468                 if (cell.rowspan > 1) {
48469                     td.rowspan = cell.rowspan ;
48470                 }
48471                 
48472                 
48473                 // widths ?
48474                 tr.cn.push(td);
48475                     
48476                 
48477             }, this);
48478             ncols = Math.max(nc, ncols);
48479             
48480             
48481         }, this);
48482         // add the header row..
48483         
48484         ncols++;
48485          
48486         
48487         return ret;
48488          
48489     },
48490     
48491     readElement : function(node)
48492     {
48493         node  = node ? node : this.node ;
48494         this.width = this.getVal(node, true, 'style', 'width') || '100%';
48495         
48496         this.rows = [];
48497         this.no_row = 0;
48498         var trs = Array.from(node.rows);
48499         trs.forEach(function(tr) {
48500             var row =  [];
48501             this.rows.push(row);
48502             
48503             this.no_row++;
48504             var no_column = 0;
48505             Array.from(tr.cells).forEach(function(td) {
48506                 
48507                 var add = {
48508                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
48509                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
48510                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
48511                     html : td.innerHTML
48512                 };
48513                 no_column += add.colspan;
48514                      
48515                 
48516                 row.push(add);
48517                 
48518                 
48519             },this);
48520             this.no_col = Math.max(this.no_col, no_column);
48521             
48522             
48523         },this);
48524         
48525         
48526     },
48527     normalizeRows: function()
48528     {
48529         var ret= [];
48530         var rid = -1;
48531         this.rows.forEach(function(row) {
48532             rid++;
48533             ret[rid] = [];
48534             row = this.normalizeRow(row);
48535             var cid = 0;
48536             row.forEach(function(c) {
48537                 while (typeof(ret[rid][cid]) != 'undefined') {
48538                     cid++;
48539                 }
48540                 if (typeof(ret[rid]) == 'undefined') {
48541                     ret[rid] = [];
48542                 }
48543                 ret[rid][cid] = c;
48544                 c.row = rid;
48545                 c.col = cid;
48546                 if (c.rowspan < 2) {
48547                     return;
48548                 }
48549                 
48550                 for(var i = 1 ;i < c.rowspan; i++) {
48551                     if (typeof(ret[rid+i]) == 'undefined') {
48552                         ret[rid+i] = [];
48553                     }
48554                     ret[rid+i][cid] = c;
48555                 }
48556             });
48557         }, this);
48558         return ret;
48559     
48560     },
48561     
48562     normalizeRow: function(row)
48563     {
48564         var ret= [];
48565         row.forEach(function(c) {
48566             if (c.colspan < 2) {
48567                 ret.push(c);
48568                 return;
48569             }
48570             for(var i =0 ;i < c.colspan; i++) {
48571                 ret.push(c);
48572             }
48573         });
48574         return ret;
48575     
48576     },
48577     
48578     deleteColumn : function(sel)
48579     {
48580         if (!sel || sel.type != 'col') {
48581             return;
48582         }
48583         if (this.no_col < 2) {
48584             return;
48585         }
48586         
48587         this.rows.forEach(function(row) {
48588             var cols = this.normalizeRow(row);
48589             var col = cols[sel.col];
48590             if (col.colspan > 1) {
48591                 col.colspan --;
48592             } else {
48593                 row.remove(col);
48594             }
48595             
48596         }, this);
48597         this.no_col--;
48598         
48599     },
48600     removeColumn : function()
48601     {
48602         this.deleteColumn({
48603             type: 'col',
48604             col : this.no_col-1
48605         });
48606         this.updateElement();
48607     },
48608     
48609      
48610     addColumn : function()
48611     {
48612         
48613         this.rows.forEach(function(row) {
48614             row.push(this.emptyCell());
48615            
48616         }, this);
48617         this.updateElement();
48618     },
48619     
48620     deleteRow : function(sel)
48621     {
48622         if (!sel || sel.type != 'row') {
48623             return;
48624         }
48625         
48626         if (this.no_row < 2) {
48627             return;
48628         }
48629         
48630         var rows = this.normalizeRows();
48631         
48632         
48633         rows[sel.row].forEach(function(col) {
48634             if (col.rowspan > 1) {
48635                 col.rowspan--;
48636             } else {
48637                 col.remove = 1; // flage it as removed.
48638             }
48639             
48640         }, this);
48641         var newrows = [];
48642         this.rows.forEach(function(row) {
48643             newrow = [];
48644             row.forEach(function(c) {
48645                 if (typeof(c.remove) == 'undefined') {
48646                     newrow.push(c);
48647                 }
48648                 
48649             });
48650             if (newrow.length > 0) {
48651                 newrows.push(row);
48652             }
48653         });
48654         this.rows =  newrows;
48655         
48656         
48657         
48658         this.no_row--;
48659         this.updateElement();
48660         
48661     },
48662     removeRow : function()
48663     {
48664         this.deleteRow({
48665             type: 'row',
48666             row : this.no_row-1
48667         });
48668         
48669     },
48670     
48671      
48672     addRow : function()
48673     {
48674         
48675         var row = [];
48676         for (var i = 0; i < this.no_col; i++ ) {
48677             
48678             row.push(this.emptyCell());
48679            
48680         }
48681         this.rows.push(row);
48682         this.updateElement();
48683         
48684     },
48685      
48686     // the default cell object... at present...
48687     emptyCell : function() {
48688         return (new Roo.htmleditor.BlockTd({})).toObject();
48689         
48690      
48691     },
48692     
48693     removeNode : function()
48694     {
48695         return this.node;
48696     },
48697     
48698     
48699     
48700     resetWidths : function()
48701     {
48702         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
48703             var nn = Roo.htmleditor.Block.factory(n);
48704             nn.width = '';
48705             nn.updateElement(n);
48706         });
48707     }
48708     
48709     
48710     
48711     
48712 })
48713
48714 /**
48715  *
48716  * editing a TD?
48717  *
48718  * since selections really work on the table cell, then editing really should work from there
48719  *
48720  * The original plan was to support merging etc... - but that may not be needed yet..
48721  *
48722  * So this simple version will support:
48723  *   add/remove cols
48724  *   adjust the width +/-
48725  *   reset the width...
48726  *   
48727  *
48728  */
48729
48730
48731  
48732
48733 /**
48734  * @class Roo.htmleditor.BlockTable
48735  * Block that manages a table
48736  * 
48737  * @constructor
48738  * Create a new Filter.
48739  * @param {Object} config Configuration options
48740  */
48741
48742 Roo.htmleditor.BlockTd = function(cfg)
48743 {
48744     if (cfg.node) {
48745         this.readElement(cfg.node);
48746         this.updateElement(cfg.node);
48747     }
48748     Roo.apply(this, cfg);
48749      
48750     
48751     
48752 }
48753 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
48754  
48755     node : false,
48756     
48757     width: '',
48758     textAlign : 'left',
48759     valign : 'top',
48760     
48761     colspan : 1,
48762     rowspan : 1,
48763     
48764     
48765     // used by context menu
48766     friendly_name : 'Table Cell',
48767     deleteTitle : false, // use our customer delete
48768     
48769     // context menu is drawn once..
48770     
48771     contextMenu : function(toolbar)
48772     {
48773         
48774         var cell = function() {
48775             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48776         };
48777         
48778         var table = function() {
48779             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
48780         };
48781         
48782         var lr = false;
48783         var saveSel = function()
48784         {
48785             lr = toolbar.editorcore.getSelection().getRangeAt(0);
48786         }
48787         var restoreSel = function()
48788         {
48789             if (lr) {
48790                 (function() {
48791                     toolbar.editorcore.focus();
48792                     var cr = toolbar.editorcore.getSelection();
48793                     cr.removeAllRanges();
48794                     cr.addRange(lr);
48795                     toolbar.editorcore.onEditorEvent();
48796                 }).defer(10, this);
48797                 
48798                 
48799             }
48800         }
48801         
48802         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48803         
48804         var syncValue = toolbar.editorcore.syncValue;
48805         
48806         var fields = {};
48807         
48808         return [
48809             {
48810                 xtype : 'Button',
48811                 text : 'Edit Table',
48812                 listeners : {
48813                     click : function() {
48814                         var t = toolbar.tb.selectedNode.closest('table');
48815                         toolbar.editorcore.selectNode(t);
48816                         toolbar.editorcore.onEditorEvent();                        
48817                     }
48818                 }
48819                 
48820             },
48821               
48822            
48823              
48824             {
48825                 xtype : 'TextItem',
48826                 text : "Column Width: ",
48827                  xns : rooui.Toolbar 
48828                
48829             },
48830             {
48831                 xtype : 'Button',
48832                 text: '-',
48833                 listeners : {
48834                     click : function (_self, e)
48835                     {
48836                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48837                         cell().shrinkColumn();
48838                         syncValue();
48839                          toolbar.editorcore.onEditorEvent();
48840                     }
48841                 },
48842                 xns : rooui.Toolbar
48843             },
48844             {
48845                 xtype : 'Button',
48846                 text: '+',
48847                 listeners : {
48848                     click : function (_self, e)
48849                     {
48850                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48851                         cell().growColumn();
48852                         syncValue();
48853                         toolbar.editorcore.onEditorEvent();
48854                     }
48855                 },
48856                 xns : rooui.Toolbar
48857             },
48858             
48859             {
48860                 xtype : 'TextItem',
48861                 text : "Vertical Align: ",
48862                 xns : rooui.Toolbar  //Boostrap?
48863             },
48864             {
48865                 xtype : 'ComboBox',
48866                 allowBlank : false,
48867                 displayField : 'val',
48868                 editable : true,
48869                 listWidth : 100,
48870                 triggerAction : 'all',
48871                 typeAhead : true,
48872                 valueField : 'val',
48873                 width : 100,
48874                 name : 'valign',
48875                 listeners : {
48876                     select : function (combo, r, index)
48877                     {
48878                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48879                         var b = cell();
48880                         b.valign = r.get('val');
48881                         b.updateElement();
48882                         syncValue();
48883                         toolbar.editorcore.onEditorEvent();
48884                     }
48885                 },
48886                 xns : rooui.form,
48887                 store : {
48888                     xtype : 'SimpleStore',
48889                     data : [
48890                         ['top'],
48891                         ['middle'],
48892                         ['bottom'] // there are afew more... 
48893                     ],
48894                     fields : [ 'val'],
48895                     xns : Roo.data
48896                 }
48897             },
48898             
48899             {
48900                 xtype : 'TextItem',
48901                 text : "Merge Cells: ",
48902                  xns : rooui.Toolbar 
48903                
48904             },
48905             
48906             
48907             {
48908                 xtype : 'Button',
48909                 text: 'Right',
48910                 listeners : {
48911                     click : function (_self, e)
48912                     {
48913                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48914                         cell().mergeRight();
48915                         //block().growColumn();
48916                         syncValue();
48917                         toolbar.editorcore.onEditorEvent();
48918                     }
48919                 },
48920                 xns : rooui.Toolbar
48921             },
48922              
48923             {
48924                 xtype : 'Button',
48925                 text: 'Below',
48926                 listeners : {
48927                     click : function (_self, e)
48928                     {
48929                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48930                         cell().mergeBelow();
48931                         //block().growColumn();
48932                         syncValue();
48933                         toolbar.editorcore.onEditorEvent();
48934                     }
48935                 },
48936                 xns : rooui.Toolbar
48937             },
48938             {
48939                 xtype : 'TextItem',
48940                 text : "| ",
48941                  xns : rooui.Toolbar 
48942                
48943             },
48944             
48945             {
48946                 xtype : 'Button',
48947                 text: 'Split',
48948                 listeners : {
48949                     click : function (_self, e)
48950                     {
48951                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48952                         cell().split();
48953                         syncValue();
48954                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48955                         toolbar.editorcore.onEditorEvent();
48956                                              
48957                     }
48958                 },
48959                 xns : rooui.Toolbar
48960             },
48961             {
48962                 xtype : 'Fill',
48963                 xns : rooui.Toolbar 
48964                
48965             },
48966         
48967           
48968             {
48969                 xtype : 'Button',
48970                 text: 'Delete',
48971                  
48972                 xns : rooui.Toolbar,
48973                 menu : {
48974                     xtype : 'Menu',
48975                     xns : rooui.menu,
48976                     items : [
48977                         {
48978                             xtype : 'Item',
48979                             html: 'Column',
48980                             listeners : {
48981                                 click : function (_self, e)
48982                                 {
48983                                     var t = table();
48984                                     
48985                                     cell().deleteColumn();
48986                                     syncValue();
48987                                     toolbar.editorcore.selectNode(t.node);
48988                                     toolbar.editorcore.onEditorEvent();   
48989                                 }
48990                             },
48991                             xns : rooui.menu
48992                         },
48993                         {
48994                             xtype : 'Item',
48995                             html: 'Row',
48996                             listeners : {
48997                                 click : function (_self, e)
48998                                 {
48999                                     var t = table();
49000                                     cell().deleteRow();
49001                                     syncValue();
49002                                     
49003                                     toolbar.editorcore.selectNode(t.node);
49004                                     toolbar.editorcore.onEditorEvent();   
49005                                                          
49006                                 }
49007                             },
49008                             xns : rooui.menu
49009                         },
49010                        {
49011                             xtype : 'Separator',
49012                             xns : rooui.menu
49013                         },
49014                         {
49015                             xtype : 'Item',
49016                             html: 'Table',
49017                             listeners : {
49018                                 click : function (_self, e)
49019                                 {
49020                                     var t = table();
49021                                     var nn = t.node.nextSibling || t.node.previousSibling;
49022                                     t.node.parentNode.removeChild(t.node);
49023                                     if (nn) { 
49024                                         toolbar.editorcore.selectNode(nn, true);
49025                                     }
49026                                     toolbar.editorcore.onEditorEvent();   
49027                                                          
49028                                 }
49029                             },
49030                             xns : rooui.menu
49031                         }
49032                     ]
49033                 }
49034             }
49035             
49036             // align... << fixme
49037             
49038         ];
49039         
49040     },
49041     
49042     
49043   /**
49044      * create a DomHelper friendly object - for use with
49045      * Roo.DomHelper.markup / overwrite / etc..
49046      * ?? should it be called with option to hide all editing features?
49047      */
49048  /**
49049      * create a DomHelper friendly object - for use with
49050      * Roo.DomHelper.markup / overwrite / etc..
49051      * ?? should it be called with option to hide all editing features?
49052      */
49053     toObject : function()
49054     {
49055         
49056         var ret = {
49057             tag : 'td',
49058             contenteditable : 'true', // this stops cell selection from picking the table.
49059             'data-block' : 'Td',
49060             valign : this.valign,
49061             style : {  
49062                 'text-align' :  this.textAlign,
49063                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
49064                 'border-collapse' : 'collapse',
49065                 padding : '6px', // 8 for desktop / 4 for mobile
49066                 'vertical-align': this.valign
49067             },
49068             html : this.html
49069         };
49070         if (this.width != '') {
49071             ret.width = this.width;
49072             ret.style.width = this.width;
49073         }
49074         
49075         
49076         if (this.colspan > 1) {
49077             ret.colspan = this.colspan ;
49078         } 
49079         if (this.rowspan > 1) {
49080             ret.rowspan = this.rowspan ;
49081         }
49082         
49083            
49084         
49085         return ret;
49086          
49087     },
49088     
49089     readElement : function(node)
49090     {
49091         node  = node ? node : this.node ;
49092         this.width = node.style.width;
49093         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
49094         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
49095         this.html = node.innerHTML;
49096         
49097         
49098     },
49099      
49100     // the default cell object... at present...
49101     emptyCell : function() {
49102         return {
49103             colspan :  1,
49104             rowspan :  1,
49105             textAlign : 'left',
49106             html : "&nbsp;" // is this going to be editable now?
49107         };
49108      
49109     },
49110     
49111     removeNode : function()
49112     {
49113         return this.node.closest('table');
49114          
49115     },
49116     
49117     cellData : false,
49118     
49119     colWidths : false,
49120     
49121     toTableArray  : function()
49122     {
49123         var ret = [];
49124         var tab = this.node.closest('tr').closest('table');
49125         Array.from(tab.rows).forEach(function(r, ri){
49126             ret[ri] = [];
49127         });
49128         var rn = 0;
49129         this.colWidths = [];
49130         var all_auto = true;
49131         Array.from(tab.rows).forEach(function(r, ri){
49132             
49133             var cn = 0;
49134             Array.from(r.cells).forEach(function(ce, ci){
49135                 var c =  {
49136                     cell : ce,
49137                     row : rn,
49138                     col: cn,
49139                     colspan : ce.colSpan,
49140                     rowspan : ce.rowSpan
49141                 };
49142                 if (ce.isEqualNode(this.node)) {
49143                     this.cellData = c;
49144                 }
49145                 // if we have been filled up by a row?
49146                 if (typeof(ret[rn][cn]) != 'undefined') {
49147                     while(typeof(ret[rn][cn]) != 'undefined') {
49148                         cn++;
49149                     }
49150                     c.col = cn;
49151                 }
49152                 
49153                 if (typeof(this.colWidths[cn]) == 'undefined') {
49154                     this.colWidths[cn] =   ce.style.width;
49155                     if (this.colWidths[cn] != '') {
49156                         all_auto = false;
49157                     }
49158                 }
49159                 
49160                 
49161                 if (c.colspan < 2 && c.rowspan < 2 ) {
49162                     ret[rn][cn] = c;
49163                     cn++;
49164                     return;
49165                 }
49166                 for(var j = 0; j < c.rowspan; j++) {
49167                     if (typeof(ret[rn+j]) == 'undefined') {
49168                         continue; // we have a problem..
49169                     }
49170                     ret[rn+j][cn] = c;
49171                     for(var i = 0; i < c.colspan; i++) {
49172                         ret[rn+j][cn+i] = c;
49173                     }
49174                 }
49175                 
49176                 cn += c.colspan;
49177             }, this);
49178             rn++;
49179         }, this);
49180         
49181         // initalize widths.?
49182         // either all widths or no widths..
49183         if (all_auto) {
49184             this.colWidths[0] = false; // no widths flag.
49185         }
49186         
49187         
49188         return ret;
49189         
49190     },
49191     
49192     
49193     
49194     
49195     mergeRight: function()
49196     {
49197          
49198         // get the contents of the next cell along..
49199         var tr = this.node.closest('tr');
49200         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
49201         if (i >= tr.childNodes.length - 1) {
49202             return; // no cells on right to merge with.
49203         }
49204         var table = this.toTableArray();
49205         
49206         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
49207             return; // nothing right?
49208         }
49209         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
49210         // right cell - must be same rowspan and on the same row.
49211         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
49212             return; // right hand side is not same rowspan.
49213         }
49214         
49215         
49216         
49217         this.node.innerHTML += ' ' + rc.cell.innerHTML;
49218         tr.removeChild(rc.cell);
49219         this.colspan += rc.colspan;
49220         this.node.setAttribute('colspan', this.colspan);
49221
49222     },
49223     
49224     
49225     mergeBelow : function()
49226     {
49227         var table = this.toTableArray();
49228         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
49229             return; // no row below
49230         }
49231         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
49232             return; // nothing right?
49233         }
49234         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
49235         
49236         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
49237             return; // right hand side is not same rowspan.
49238         }
49239         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
49240         rc.cell.parentNode.removeChild(rc.cell);
49241         this.rowspan += rc.rowspan;
49242         this.node.setAttribute('rowspan', this.rowspan);
49243     },
49244     
49245     split: function()
49246     {
49247         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
49248             return;
49249         }
49250         var table = this.toTableArray();
49251         var cd = this.cellData;
49252         this.rowspan = 1;
49253         this.colspan = 1;
49254         
49255         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
49256             
49257             
49258             
49259             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
49260                 if (r == cd.row && c == cd.col) {
49261                     this.node.removeAttribute('rowspan');
49262                     this.node.removeAttribute('colspan');
49263                     continue;
49264                 }
49265                  
49266                 var ntd = this.node.cloneNode(); // which col/row should be 0..
49267                 ntd.removeAttribute('id'); //
49268                 //ntd.style.width  = '';
49269                 ntd.innerHTML = '';
49270                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
49271             }
49272             
49273         }
49274         this.redrawAllCells(table);
49275         
49276          
49277         
49278     },
49279     
49280     
49281     
49282     redrawAllCells: function(table)
49283     {
49284         
49285          
49286         var tab = this.node.closest('tr').closest('table');
49287         var ctr = tab.rows[0].parentNode;
49288         Array.from(tab.rows).forEach(function(r, ri){
49289             
49290             Array.from(r.cells).forEach(function(ce, ci){
49291                 ce.parentNode.removeChild(ce);
49292             });
49293             r.parentNode.removeChild(r);
49294         });
49295         for(var r = 0 ; r < table.length; r++) {
49296             var re = tab.rows[r];
49297             
49298             var re = tab.ownerDocument.createElement('tr');
49299             ctr.appendChild(re);
49300             for(var c = 0 ; c < table[r].length; c++) {
49301                 if (table[r][c].cell === false) {
49302                     continue;
49303                 }
49304                 
49305                 re.appendChild(table[r][c].cell);
49306                  
49307                 table[r][c].cell = false;
49308             }
49309         }
49310         
49311     },
49312     updateWidths : function(table)
49313     {
49314         for(var r = 0 ; r < table.length; r++) {
49315            
49316             for(var c = 0 ; c < table[r].length; c++) {
49317                 if (table[r][c].cell === false) {
49318                     continue;
49319                 }
49320                 
49321                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
49322                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49323                     el.width = Math.floor(this.colWidths[c])  +'%';
49324                     el.updateElement(el.node);
49325                 }
49326                 table[r][c].cell = false; // done
49327             }
49328         }
49329     },
49330     normalizeWidths : function(table)
49331     {
49332     
49333         if (this.colWidths[0] === false) {
49334             var nw = 100.0 / this.colWidths.length;
49335             this.colWidths.forEach(function(w,i) {
49336                 this.colWidths[i] = nw;
49337             },this);
49338             return;
49339         }
49340     
49341         var t = 0, missing = [];
49342         
49343         this.colWidths.forEach(function(w,i) {
49344             //if you mix % and
49345             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
49346             var add =  this.colWidths[i];
49347             if (add > 0) {
49348                 t+=add;
49349                 return;
49350             }
49351             missing.push(i);
49352             
49353             
49354         },this);
49355         var nc = this.colWidths.length;
49356         if (missing.length) {
49357             var mult = (nc - missing.length) / (1.0 * nc);
49358             var t = mult * t;
49359             var ew = (100 -t) / (1.0 * missing.length);
49360             this.colWidths.forEach(function(w,i) {
49361                 if (w > 0) {
49362                     this.colWidths[i] = w * mult;
49363                     return;
49364                 }
49365                 
49366                 this.colWidths[i] = ew;
49367             }, this);
49368             // have to make up numbers..
49369              
49370         }
49371         // now we should have all the widths..
49372         
49373     
49374     },
49375     
49376     shrinkColumn : function()
49377     {
49378         var table = this.toTableArray();
49379         this.normalizeWidths(table);
49380         var col = this.cellData.col;
49381         var nw = this.colWidths[col] * 0.8;
49382         if (nw < 5) {
49383             return;
49384         }
49385         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
49386         this.colWidths.forEach(function(w,i) {
49387             if (i == col) {
49388                  this.colWidths[i] = nw;
49389                 return;
49390             }
49391             this.colWidths[i] += otherAdd
49392         }, this);
49393         this.updateWidths(table);
49394          
49395     },
49396     growColumn : function()
49397     {
49398         var table = this.toTableArray();
49399         this.normalizeWidths(table);
49400         var col = this.cellData.col;
49401         var nw = this.colWidths[col] * 1.2;
49402         if (nw > 90) {
49403             return;
49404         }
49405         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
49406         this.colWidths.forEach(function(w,i) {
49407             if (i == col) {
49408                 this.colWidths[i] = nw;
49409                 return;
49410             }
49411             this.colWidths[i] -= otherSub
49412         }, this);
49413         this.updateWidths(table);
49414          
49415     },
49416     deleteRow : function()
49417     {
49418         // delete this rows 'tr'
49419         // if any of the cells in this row have a rowspan > 1 && row!= this row..
49420         // then reduce the rowspan.
49421         var table = this.toTableArray();
49422         // this.cellData.row;
49423         for (var i =0;i< table[this.cellData.row].length ; i++) {
49424             var c = table[this.cellData.row][i];
49425             if (c.row != this.cellData.row) {
49426                 
49427                 c.rowspan--;
49428                 c.cell.setAttribute('rowspan', c.rowspan);
49429                 continue;
49430             }
49431             if (c.rowspan > 1) {
49432                 c.rowspan--;
49433                 c.cell.setAttribute('rowspan', c.rowspan);
49434             }
49435         }
49436         table.splice(this.cellData.row,1);
49437         this.redrawAllCells(table);
49438         
49439     },
49440     deleteColumn : function()
49441     {
49442         var table = this.toTableArray();
49443         
49444         for (var i =0;i< table.length ; i++) {
49445             var c = table[i][this.cellData.col];
49446             if (c.col != this.cellData.col) {
49447                 table[i][this.cellData.col].colspan--;
49448             } else if (c.colspan > 1) {
49449                 c.colspan--;
49450                 c.cell.setAttribute('colspan', c.colspan);
49451             }
49452             table[i].splice(this.cellData.col,1);
49453         }
49454         
49455         this.redrawAllCells(table);
49456     }
49457     
49458     
49459     
49460     
49461 })
49462
49463 //<script type="text/javascript">
49464
49465 /*
49466  * Based  Ext JS Library 1.1.1
49467  * Copyright(c) 2006-2007, Ext JS, LLC.
49468  * LGPL
49469  *
49470  */
49471  
49472 /**
49473  * @class Roo.HtmlEditorCore
49474  * @extends Roo.Component
49475  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
49476  *
49477  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
49478  */
49479
49480 Roo.HtmlEditorCore = function(config){
49481     
49482     
49483     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
49484     
49485     
49486     this.addEvents({
49487         /**
49488          * @event initialize
49489          * Fires when the editor is fully initialized (including the iframe)
49490          * @param {Roo.HtmlEditorCore} this
49491          */
49492         initialize: true,
49493         /**
49494          * @event activate
49495          * Fires when the editor is first receives the focus. Any insertion must wait
49496          * until after this event.
49497          * @param {Roo.HtmlEditorCore} this
49498          */
49499         activate: true,
49500          /**
49501          * @event beforesync
49502          * Fires before the textarea is updated with content from the editor iframe. Return false
49503          * to cancel the sync.
49504          * @param {Roo.HtmlEditorCore} this
49505          * @param {String} html
49506          */
49507         beforesync: true,
49508          /**
49509          * @event beforepush
49510          * Fires before the iframe editor is updated with content from the textarea. Return false
49511          * to cancel the push.
49512          * @param {Roo.HtmlEditorCore} this
49513          * @param {String} html
49514          */
49515         beforepush: true,
49516          /**
49517          * @event sync
49518          * Fires when the textarea is updated with content from the editor iframe.
49519          * @param {Roo.HtmlEditorCore} this
49520          * @param {String} html
49521          */
49522         sync: true,
49523          /**
49524          * @event push
49525          * Fires when the iframe editor is updated with content from the textarea.
49526          * @param {Roo.HtmlEditorCore} this
49527          * @param {String} html
49528          */
49529         push: true,
49530         
49531         /**
49532          * @event editorevent
49533          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
49534          * @param {Roo.HtmlEditorCore} this
49535          */
49536         editorevent: true 
49537          
49538         
49539     });
49540     
49541     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
49542     
49543     // defaults : white / black...
49544     this.applyBlacklists();
49545     
49546     
49547     
49548 };
49549
49550
49551 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
49552
49553
49554      /**
49555      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
49556      */
49557     
49558     owner : false,
49559     
49560      /**
49561      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
49562      *                        Roo.resizable.
49563      */
49564     resizable : false,
49565      /**
49566      * @cfg {Number} height (in pixels)
49567      */   
49568     height: 300,
49569    /**
49570      * @cfg {Number} width (in pixels)
49571      */   
49572     width: 500,
49573      /**
49574      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
49575      *         if you are doing an email editor, this probably needs disabling, it's designed
49576      */
49577     autoClean: true,
49578     
49579     /**
49580      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
49581      */
49582     enableBlocks : true,
49583     /**
49584      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
49585      * 
49586      */
49587     stylesheets: false,
49588      /**
49589      * @cfg {String} language default en - language of text (usefull for rtl languages)
49590      * 
49591      */
49592     language: 'en',
49593     
49594     /**
49595      * @cfg {boolean} allowComments - default false - allow comments in HTML source
49596      *          - by default they are stripped - if you are editing email you may need this.
49597      */
49598     allowComments: false,
49599     // id of frame..
49600     frameId: false,
49601     
49602     // private properties
49603     validationEvent : false,
49604     deferHeight: true,
49605     initialized : false,
49606     activated : false,
49607     sourceEditMode : false,
49608     onFocus : Roo.emptyFn,
49609     iframePad:3,
49610     hideMode:'offsets',
49611     
49612     clearUp: true,
49613     
49614     // blacklist + whitelisted elements..
49615     black: false,
49616     white: false,
49617      
49618     bodyCls : '',
49619
49620     
49621     undoManager : false,
49622     /**
49623      * Protected method that will not generally be called directly. It
49624      * is called when the editor initializes the iframe with HTML contents. Override this method if you
49625      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
49626      */
49627     getDocMarkup : function(){
49628         // body styles..
49629         var st = '';
49630         
49631         // inherit styels from page...?? 
49632         if (this.stylesheets === false) {
49633             
49634             Roo.get(document.head).select('style').each(function(node) {
49635                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
49636             });
49637             
49638             Roo.get(document.head).select('link').each(function(node) { 
49639                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
49640             });
49641             
49642         } else if (!this.stylesheets.length) {
49643                 // simple..
49644                 st = '<style type="text/css">' +
49645                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
49646                    '</style>';
49647         } else {
49648             for (var i in this.stylesheets) {
49649                 if (typeof(this.stylesheets[i]) != 'string') {
49650                     continue;
49651                 }
49652                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
49653             }
49654             
49655         }
49656         
49657         st +=  '<style type="text/css">' +
49658             'IMG { cursor: pointer } ' +
49659         '</style>';
49660         
49661         st += '<meta name="google" content="notranslate">';
49662         
49663         var cls = 'notranslate roo-htmleditor-body';
49664         
49665         if(this.bodyCls.length){
49666             cls += ' ' + this.bodyCls;
49667         }
49668         
49669         return '<html  class="notranslate" translate="no"><head>' + st  +
49670             //<style type="text/css">' +
49671             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
49672             //'</style>' +
49673             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
49674     },
49675
49676     // private
49677     onRender : function(ct, position)
49678     {
49679         var _t = this;
49680         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
49681         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
49682         
49683         
49684         this.el.dom.style.border = '0 none';
49685         this.el.dom.setAttribute('tabIndex', -1);
49686         this.el.addClass('x-hidden hide');
49687         
49688         
49689         
49690         if(Roo.isIE){ // fix IE 1px bogus margin
49691             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
49692         }
49693        
49694         
49695         this.frameId = Roo.id();
49696         
49697          
49698         
49699         var iframe = this.owner.wrap.createChild({
49700             tag: 'iframe',
49701             cls: 'form-control', // bootstrap..
49702             id: this.frameId,
49703             name: this.frameId,
49704             frameBorder : 'no',
49705             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
49706         }, this.el
49707         );
49708         
49709         
49710         this.iframe = iframe.dom;
49711
49712         this.assignDocWin();
49713         
49714         this.doc.designMode = 'on';
49715        
49716         this.doc.open();
49717         this.doc.write(this.getDocMarkup());
49718         this.doc.close();
49719
49720         
49721         var task = { // must defer to wait for browser to be ready
49722             run : function(){
49723                 //console.log("run task?" + this.doc.readyState);
49724                 this.assignDocWin();
49725                 if(this.doc.body || this.doc.readyState == 'complete'){
49726                     try {
49727                         this.doc.designMode="on";
49728                         
49729                     } catch (e) {
49730                         return;
49731                     }
49732                     Roo.TaskMgr.stop(task);
49733                     this.initEditor.defer(10, this);
49734                 }
49735             },
49736             interval : 10,
49737             duration: 10000,
49738             scope: this
49739         };
49740         Roo.TaskMgr.start(task);
49741
49742     },
49743
49744     // private
49745     onResize : function(w, h)
49746     {
49747          Roo.log('resize: ' +w + ',' + h );
49748         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
49749         if(!this.iframe){
49750             return;
49751         }
49752         if(typeof w == 'number'){
49753             
49754             this.iframe.style.width = w + 'px';
49755         }
49756         if(typeof h == 'number'){
49757             
49758             this.iframe.style.height = h + 'px';
49759             if(this.doc){
49760                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
49761             }
49762         }
49763         
49764     },
49765
49766     /**
49767      * Toggles the editor between standard and source edit mode.
49768      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
49769      */
49770     toggleSourceEdit : function(sourceEditMode){
49771         
49772         this.sourceEditMode = sourceEditMode === true;
49773         
49774         if(this.sourceEditMode){
49775  
49776             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
49777             
49778         }else{
49779             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
49780             //this.iframe.className = '';
49781             this.deferFocus();
49782         }
49783         //this.setSize(this.owner.wrap.getSize());
49784         //this.fireEvent('editmodechange', this, this.sourceEditMode);
49785     },
49786
49787     
49788   
49789
49790     /**
49791      * Protected method that will not generally be called directly. If you need/want
49792      * custom HTML cleanup, this is the method you should override.
49793      * @param {String} html The HTML to be cleaned
49794      * return {String} The cleaned HTML
49795      */
49796     cleanHtml : function(html)
49797     {
49798         html = String(html);
49799         if(html.length > 5){
49800             if(Roo.isSafari){ // strip safari nonsense
49801                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
49802             }
49803         }
49804         if(html == '&nbsp;'){
49805             html = '';
49806         }
49807         return html;
49808     },
49809
49810     /**
49811      * HTML Editor -> Textarea
49812      * Protected method that will not generally be called directly. Syncs the contents
49813      * of the editor iframe with the textarea.
49814      */
49815     syncValue : function()
49816     {
49817         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
49818         if(this.initialized){
49819             
49820             this.undoManager.addEvent();
49821
49822             
49823             var bd = (this.doc.body || this.doc.documentElement);
49824            
49825             
49826             var sel = this.win.getSelection();
49827             
49828             var div = document.createElement('div');
49829             div.innerHTML = bd.innerHTML;
49830             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
49831             if (gtx.length > 0) {
49832                 var rm = gtx.item(0).parentNode;
49833                 rm.parentNode.removeChild(rm);
49834             }
49835             
49836            
49837             if (this.enableBlocks) {
49838                 new Roo.htmleditor.FilterBlock({ node : div });
49839             }
49840             //?? tidy?
49841             var tidy = new Roo.htmleditor.TidySerializer({
49842                 inner:  true
49843             });
49844             var html  = tidy.serialize(div);
49845             
49846             
49847             if(Roo.isSafari){
49848                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
49849                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
49850                 if(m && m[1]){
49851                     html = '<div style="'+m[0]+'">' + html + '</div>';
49852                 }
49853             }
49854             html = this.cleanHtml(html);
49855             // fix up the special chars.. normaly like back quotes in word...
49856             // however we do not want to do this with chinese..
49857             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
49858                 
49859                 var cc = match.charCodeAt();
49860
49861                 // Get the character value, handling surrogate pairs
49862                 if (match.length == 2) {
49863                     // It's a surrogate pair, calculate the Unicode code point
49864                     var high = match.charCodeAt(0) - 0xD800;
49865                     var low  = match.charCodeAt(1) - 0xDC00;
49866                     cc = (high * 0x400) + low + 0x10000;
49867                 }  else if (
49868                     (cc >= 0x4E00 && cc < 0xA000 ) ||
49869                     (cc >= 0x3400 && cc < 0x4E00 ) ||
49870                     (cc >= 0xf900 && cc < 0xfb00 )
49871                 ) {
49872                         return match;
49873                 }  
49874          
49875                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
49876                 return "&#" + cc + ";";
49877                 
49878                 
49879             });
49880             
49881             
49882              
49883             if(this.owner.fireEvent('beforesync', this, html) !== false){
49884                 this.el.dom.value = html;
49885                 this.owner.fireEvent('sync', this, html);
49886             }
49887         }
49888     },
49889
49890     /**
49891      * TEXTAREA -> EDITABLE
49892      * Protected method that will not generally be called directly. Pushes the value of the textarea
49893      * into the iframe editor.
49894      */
49895     pushValue : function()
49896     {
49897         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
49898         if(this.initialized){
49899             var v = this.el.dom.value.trim();
49900             
49901             
49902             if(this.owner.fireEvent('beforepush', this, v) !== false){
49903                 var d = (this.doc.body || this.doc.documentElement);
49904                 d.innerHTML = v;
49905                  
49906                 this.el.dom.value = d.innerHTML;
49907                 this.owner.fireEvent('push', this, v);
49908             }
49909             if (this.autoClean) {
49910                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
49911                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
49912             }
49913             if (this.enableBlocks) {
49914                 Roo.htmleditor.Block.initAll(this.doc.body);
49915             }
49916             
49917             this.updateLanguage();
49918             
49919             var lc = this.doc.body.lastChild;
49920             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
49921                 // add an extra line at the end.
49922                 this.doc.body.appendChild(this.doc.createElement('br'));
49923             }
49924             
49925             
49926         }
49927     },
49928
49929     // private
49930     deferFocus : function(){
49931         this.focus.defer(10, this);
49932     },
49933
49934     // doc'ed in Field
49935     focus : function(){
49936         if(this.win && !this.sourceEditMode){
49937             this.win.focus();
49938         }else{
49939             this.el.focus();
49940         }
49941     },
49942     
49943     assignDocWin: function()
49944     {
49945         var iframe = this.iframe;
49946         
49947          if(Roo.isIE){
49948             this.doc = iframe.contentWindow.document;
49949             this.win = iframe.contentWindow;
49950         } else {
49951 //            if (!Roo.get(this.frameId)) {
49952 //                return;
49953 //            }
49954 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
49955 //            this.win = Roo.get(this.frameId).dom.contentWindow;
49956             
49957             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
49958                 return;
49959             }
49960             
49961             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
49962             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
49963         }
49964     },
49965     
49966     // private
49967     initEditor : function(){
49968         //console.log("INIT EDITOR");
49969         this.assignDocWin();
49970         
49971         
49972         
49973         this.doc.designMode="on";
49974         this.doc.open();
49975         this.doc.write(this.getDocMarkup());
49976         this.doc.close();
49977         
49978         var dbody = (this.doc.body || this.doc.documentElement);
49979         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
49980         // this copies styles from the containing element into thsi one..
49981         // not sure why we need all of this..
49982         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
49983         
49984         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
49985         //ss['background-attachment'] = 'fixed'; // w3c
49986         dbody.bgProperties = 'fixed'; // ie
49987         dbody.setAttribute("translate", "no");
49988         
49989         //Roo.DomHelper.applyStyles(dbody, ss);
49990         Roo.EventManager.on(this.doc, {
49991              
49992             'mouseup': this.onEditorEvent,
49993             'dblclick': this.onEditorEvent,
49994             'click': this.onEditorEvent,
49995             'keyup': this.onEditorEvent,
49996             
49997             buffer:100,
49998             scope: this
49999         });
50000         Roo.EventManager.on(this.doc, {
50001             'paste': this.onPasteEvent,
50002             scope : this
50003         });
50004         if(Roo.isGecko){
50005             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
50006         }
50007         //??? needed???
50008         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
50009             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
50010         }
50011         this.initialized = true;
50012
50013         
50014         // initialize special key events - enter
50015         new Roo.htmleditor.KeyEnter({core : this});
50016         
50017          
50018         
50019         this.owner.fireEvent('initialize', this);
50020         this.pushValue();
50021     },
50022     // this is to prevent a href clicks resulting in a redirect?
50023    
50024     onPasteEvent : function(e,v)
50025     {
50026         // I think we better assume paste is going to be a dirty load of rubish from word..
50027         
50028         // even pasting into a 'email version' of this widget will have to clean up that mess.
50029         var cd = (e.browserEvent.clipboardData || window.clipboardData);
50030         
50031         // check what type of paste - if it's an image, then handle it differently.
50032         if (cd.files && cd.files.length > 0) {
50033             // pasting images?
50034             var urlAPI = (window.createObjectURL && window) || 
50035                 (window.URL && URL.revokeObjectURL && URL) || 
50036                 (window.webkitURL && webkitURL);
50037     
50038             var url = urlAPI.createObjectURL( cd.files[0]);
50039             this.insertAtCursor('<img src=" + url + ">');
50040             return false;
50041         }
50042         if (cd.types.indexOf('text/html') < 0 ) {
50043             return false;
50044         }
50045         var images = [];
50046         var html = cd.getData('text/html'); // clipboard event
50047         if (cd.types.indexOf('text/rtf') > -1) {
50048             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
50049             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
50050         }
50051         Roo.log(images);
50052         //Roo.log(imgs);
50053         // fixme..
50054         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
50055                        .map(function(g) { return g.toDataURL(); })
50056                        .filter(function(g) { return g != 'about:blank'; });
50057         
50058         
50059         html = this.cleanWordChars(html);
50060         
50061         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
50062         
50063         
50064         var sn = this.getParentElement();
50065         // check if d contains a table, and prevent nesting??
50066         //Roo.log(d.getElementsByTagName('table'));
50067         //Roo.log(sn);
50068         //Roo.log(sn.closest('table'));
50069         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
50070             e.preventDefault();
50071             this.insertAtCursor("You can not nest tables");
50072             //Roo.log("prevent?"); // fixme - 
50073             return false;
50074         }
50075         
50076         if (images.length > 0) {
50077             Roo.each(d.getElementsByTagName('img'), function(img, i) {
50078                 img.setAttribute('src', images[i]);
50079             });
50080         }
50081         if (this.autoClean) {
50082             new Roo.htmleditor.FilterStyleToTag({ node : d });
50083             new Roo.htmleditor.FilterAttributes({
50084                 node : d,
50085                 attrib_white : ['href', 'src', 'name', 'align'],
50086                 attrib_clean : ['href', 'src' ] 
50087             });
50088             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
50089             // should be fonts..
50090             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
50091             new Roo.htmleditor.FilterParagraph({ node : d });
50092             new Roo.htmleditor.FilterSpan({ node : d });
50093             new Roo.htmleditor.FilterLongBr({ node : d });
50094         }
50095         if (this.enableBlocks) {
50096                 
50097             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
50098                 if (img.closest('figure')) { // assume!! that it's aready
50099                     return;
50100                 }
50101                 var fig  = new Roo.htmleditor.BlockFigure({
50102                     image_src  : img.src
50103                 });
50104                 fig.updateElement(img); // replace it..
50105                 
50106             });
50107         }
50108         
50109         
50110         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
50111         if (this.enableBlocks) {
50112             Roo.htmleditor.Block.initAll(this.doc.body);
50113         }
50114         
50115         
50116         e.preventDefault();
50117         return false;
50118         // default behaveiour should be our local cleanup paste? (optional?)
50119         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
50120         //this.owner.fireEvent('paste', e, v);
50121     },
50122     // private
50123     onDestroy : function(){
50124         
50125         
50126         
50127         if(this.rendered){
50128             
50129             //for (var i =0; i < this.toolbars.length;i++) {
50130             //    // fixme - ask toolbars for heights?
50131             //    this.toolbars[i].onDestroy();
50132            // }
50133             
50134             //this.wrap.dom.innerHTML = '';
50135             //this.wrap.remove();
50136         }
50137     },
50138
50139     // private
50140     onFirstFocus : function(){
50141         
50142         this.assignDocWin();
50143         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
50144         
50145         this.activated = true;
50146          
50147     
50148         if(Roo.isGecko){ // prevent silly gecko errors
50149             this.win.focus();
50150             var s = this.win.getSelection();
50151             if(!s.focusNode || s.focusNode.nodeType != 3){
50152                 var r = s.getRangeAt(0);
50153                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
50154                 r.collapse(true);
50155                 this.deferFocus();
50156             }
50157             try{
50158                 this.execCmd('useCSS', true);
50159                 this.execCmd('styleWithCSS', false);
50160             }catch(e){}
50161         }
50162         this.owner.fireEvent('activate', this);
50163     },
50164
50165     // private
50166     adjustFont: function(btn){
50167         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
50168         //if(Roo.isSafari){ // safari
50169         //    adjust *= 2;
50170        // }
50171         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
50172         if(Roo.isSafari){ // safari
50173             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
50174             v =  (v < 10) ? 10 : v;
50175             v =  (v > 48) ? 48 : v;
50176             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
50177             
50178         }
50179         
50180         
50181         v = Math.max(1, v+adjust);
50182         
50183         this.execCmd('FontSize', v  );
50184     },
50185
50186     onEditorEvent : function(e)
50187     {
50188          
50189         
50190         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
50191             return; // we do not handle this.. (undo manager does..)
50192         }
50193         // in theory this detects if the last element is not a br, then we try and do that.
50194         // its so clicking in space at bottom triggers adding a br and moving the cursor.
50195         if (e &&
50196             e.target.nodeName == 'BODY' &&
50197             e.type == "mouseup" &&
50198             this.doc.body.lastChild
50199            ) {
50200             var lc = this.doc.body.lastChild;
50201             // gtx-trans is google translate plugin adding crap.
50202             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
50203                 lc = lc.previousSibling;
50204             }
50205             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
50206             // if last element is <BR> - then dont do anything.
50207             
50208                 var ns = this.doc.createElement('br');
50209                 this.doc.body.appendChild(ns);
50210                 range = this.doc.createRange();
50211                 range.setStartAfter(ns);
50212                 range.collapse(true);
50213                 var sel = this.win.getSelection();
50214                 sel.removeAllRanges();
50215                 sel.addRange(range);
50216             }
50217         }
50218         
50219         
50220         
50221         this.fireEditorEvent(e);
50222       //  this.updateToolbar();
50223         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
50224     },
50225     
50226     fireEditorEvent: function(e)
50227     {
50228         this.owner.fireEvent('editorevent', this, e);
50229     },
50230
50231     insertTag : function(tg)
50232     {
50233         // could be a bit smarter... -> wrap the current selected tRoo..
50234         if (tg.toLowerCase() == 'span' ||
50235             tg.toLowerCase() == 'code' ||
50236             tg.toLowerCase() == 'sup' ||
50237             tg.toLowerCase() == 'sub' 
50238             ) {
50239             
50240             range = this.createRange(this.getSelection());
50241             var wrappingNode = this.doc.createElement(tg.toLowerCase());
50242             wrappingNode.appendChild(range.extractContents());
50243             range.insertNode(wrappingNode);
50244
50245             return;
50246             
50247             
50248             
50249         }
50250         this.execCmd("formatblock",   tg);
50251         this.undoManager.addEvent(); 
50252     },
50253     
50254     insertText : function(txt)
50255     {
50256         
50257         
50258         var range = this.createRange();
50259         range.deleteContents();
50260                //alert(Sender.getAttribute('label'));
50261                
50262         range.insertNode(this.doc.createTextNode(txt));
50263         this.undoManager.addEvent();
50264     } ,
50265     
50266      
50267
50268     /**
50269      * Executes a Midas editor command on the editor document and performs necessary focus and
50270      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
50271      * @param {String} cmd The Midas command
50272      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50273      */
50274     relayCmd : function(cmd, value)
50275     {
50276         
50277         switch (cmd) {
50278             case 'justifyleft':
50279             case 'justifyright':
50280             case 'justifycenter':
50281                 // if we are in a cell, then we will adjust the
50282                 var n = this.getParentElement();
50283                 var td = n.closest('td');
50284                 if (td) {
50285                     var bl = Roo.htmleditor.Block.factory(td);
50286                     bl.textAlign = cmd.replace('justify','');
50287                     bl.updateElement();
50288                     this.owner.fireEvent('editorevent', this);
50289                     return;
50290                 }
50291                 this.execCmd('styleWithCSS', true); // 
50292                 break;
50293             case 'bold':
50294             case 'italic':
50295                 // if there is no selection, then we insert, and set the curson inside it..
50296                 this.execCmd('styleWithCSS', false); 
50297                 break;
50298                 
50299         
50300             default:
50301                 break;
50302         }
50303         
50304         
50305         this.win.focus();
50306         this.execCmd(cmd, value);
50307         this.owner.fireEvent('editorevent', this);
50308         //this.updateToolbar();
50309         this.owner.deferFocus();
50310     },
50311
50312     /**
50313      * Executes a Midas editor command directly on the editor document.
50314      * For visual commands, you should use {@link #relayCmd} instead.
50315      * <b>This should only be called after the editor is initialized.</b>
50316      * @param {String} cmd The Midas command
50317      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50318      */
50319     execCmd : function(cmd, value){
50320         this.doc.execCommand(cmd, false, value === undefined ? null : value);
50321         this.syncValue();
50322     },
50323  
50324  
50325    
50326     /**
50327      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
50328      * to insert tRoo.
50329      * @param {String} text | dom node.. 
50330      */
50331     insertAtCursor : function(text)
50332     {
50333         
50334         if(!this.activated){
50335             return;
50336         }
50337          
50338         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
50339             this.win.focus();
50340             
50341             
50342             // from jquery ui (MIT licenced)
50343             var range, node;
50344             var win = this.win;
50345             
50346             if (win.getSelection && win.getSelection().getRangeAt) {
50347                 
50348                 // delete the existing?
50349                 
50350                 this.createRange(this.getSelection()).deleteContents();
50351                 range = win.getSelection().getRangeAt(0);
50352                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
50353                 range.insertNode(node);
50354                 range = range.cloneRange();
50355                 range.collapse(false);
50356                  
50357                 win.getSelection().removeAllRanges();
50358                 win.getSelection().addRange(range);
50359                 
50360                 
50361                 
50362             } else if (win.document.selection && win.document.selection.createRange) {
50363                 // no firefox support
50364                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50365                 win.document.selection.createRange().pasteHTML(txt);
50366             
50367             } else {
50368                 // no firefox support
50369                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50370                 this.execCmd('InsertHTML', txt);
50371             } 
50372             this.syncValue();
50373             
50374             this.deferFocus();
50375         }
50376     },
50377  // private
50378     mozKeyPress : function(e){
50379         if(e.ctrlKey){
50380             var c = e.getCharCode(), cmd;
50381           
50382             if(c > 0){
50383                 c = String.fromCharCode(c).toLowerCase();
50384                 switch(c){
50385                     case 'b':
50386                         cmd = 'bold';
50387                         break;
50388                     case 'i':
50389                         cmd = 'italic';
50390                         break;
50391                     
50392                     case 'u':
50393                         cmd = 'underline';
50394                         break;
50395                     
50396                     //case 'v':
50397                       //  this.cleanUpPaste.defer(100, this);
50398                       //  return;
50399                         
50400                 }
50401                 if(cmd){
50402                     
50403                     this.relayCmd(cmd);
50404                     //this.win.focus();
50405                     //this.execCmd(cmd);
50406                     //this.deferFocus();
50407                     e.preventDefault();
50408                 }
50409                 
50410             }
50411         }
50412     },
50413
50414     // private
50415     fixKeys : function(){ // load time branching for fastest keydown performance
50416         
50417         
50418         if(Roo.isIE){
50419             return function(e){
50420                 var k = e.getKey(), r;
50421                 if(k == e.TAB){
50422                     e.stopEvent();
50423                     r = this.doc.selection.createRange();
50424                     if(r){
50425                         r.collapse(true);
50426                         r.pasteHTML('&#160;&#160;&#160;&#160;');
50427                         this.deferFocus();
50428                     }
50429                     return;
50430                 }
50431                 /// this is handled by Roo.htmleditor.KeyEnter
50432                  /*
50433                 if(k == e.ENTER){
50434                     r = this.doc.selection.createRange();
50435                     if(r){
50436                         var target = r.parentElement();
50437                         if(!target || target.tagName.toLowerCase() != 'li'){
50438                             e.stopEvent();
50439                             r.pasteHTML('<br/>');
50440                             r.collapse(false);
50441                             r.select();
50442                         }
50443                     }
50444                 }
50445                 */
50446                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50447                 //    this.cleanUpPaste.defer(100, this);
50448                 //    return;
50449                 //}
50450                 
50451                 
50452             };
50453         }else if(Roo.isOpera){
50454             return function(e){
50455                 var k = e.getKey();
50456                 if(k == e.TAB){
50457                     e.stopEvent();
50458                     this.win.focus();
50459                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
50460                     this.deferFocus();
50461                 }
50462                
50463                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50464                 //    this.cleanUpPaste.defer(100, this);
50465                  //   return;
50466                 //}
50467                 
50468             };
50469         }else if(Roo.isSafari){
50470             return function(e){
50471                 var k = e.getKey();
50472                 
50473                 if(k == e.TAB){
50474                     e.stopEvent();
50475                     this.execCmd('InsertText','\t');
50476                     this.deferFocus();
50477                     return;
50478                 }
50479                  this.mozKeyPress(e);
50480                 
50481                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
50482                  //   this.cleanUpPaste.defer(100, this);
50483                  //   return;
50484                // }
50485                 
50486              };
50487         }
50488     }(),
50489     
50490     getAllAncestors: function()
50491     {
50492         var p = this.getSelectedNode();
50493         var a = [];
50494         if (!p) {
50495             a.push(p); // push blank onto stack..
50496             p = this.getParentElement();
50497         }
50498         
50499         
50500         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
50501             a.push(p);
50502             p = p.parentNode;
50503         }
50504         a.push(this.doc.body);
50505         return a;
50506     },
50507     lastSel : false,
50508     lastSelNode : false,
50509     
50510     
50511     getSelection : function() 
50512     {
50513         this.assignDocWin();
50514         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
50515     },
50516     /**
50517      * Select a dom node
50518      * @param {DomElement} node the node to select
50519      */
50520     selectNode : function(node, collapse)
50521     {
50522         var nodeRange = node.ownerDocument.createRange();
50523         try {
50524             nodeRange.selectNode(node);
50525         } catch (e) {
50526             nodeRange.selectNodeContents(node);
50527         }
50528         if (collapse === true) {
50529             nodeRange.collapse(true);
50530         }
50531         //
50532         var s = this.win.getSelection();
50533         s.removeAllRanges();
50534         s.addRange(nodeRange);
50535     },
50536     
50537     getSelectedNode: function() 
50538     {
50539         // this may only work on Gecko!!!
50540         
50541         // should we cache this!!!!
50542         
50543          
50544          
50545         var range = this.createRange(this.getSelection()).cloneRange();
50546         
50547         if (Roo.isIE) {
50548             var parent = range.parentElement();
50549             while (true) {
50550                 var testRange = range.duplicate();
50551                 testRange.moveToElementText(parent);
50552                 if (testRange.inRange(range)) {
50553                     break;
50554                 }
50555                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
50556                     break;
50557                 }
50558                 parent = parent.parentElement;
50559             }
50560             return parent;
50561         }
50562         
50563         // is ancestor a text element.
50564         var ac =  range.commonAncestorContainer;
50565         if (ac.nodeType == 3) {
50566             ac = ac.parentNode;
50567         }
50568         
50569         var ar = ac.childNodes;
50570          
50571         var nodes = [];
50572         var other_nodes = [];
50573         var has_other_nodes = false;
50574         for (var i=0;i<ar.length;i++) {
50575             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
50576                 continue;
50577             }
50578             // fullly contained node.
50579             
50580             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
50581                 nodes.push(ar[i]);
50582                 continue;
50583             }
50584             
50585             // probably selected..
50586             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
50587                 other_nodes.push(ar[i]);
50588                 continue;
50589             }
50590             // outer..
50591             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
50592                 continue;
50593             }
50594             
50595             
50596             has_other_nodes = true;
50597         }
50598         if (!nodes.length && other_nodes.length) {
50599             nodes= other_nodes;
50600         }
50601         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
50602             return false;
50603         }
50604         
50605         return nodes[0];
50606     },
50607     
50608     
50609     createRange: function(sel)
50610     {
50611         // this has strange effects when using with 
50612         // top toolbar - not sure if it's a great idea.
50613         //this.editor.contentWindow.focus();
50614         if (typeof sel != "undefined") {
50615             try {
50616                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
50617             } catch(e) {
50618                 return this.doc.createRange();
50619             }
50620         } else {
50621             return this.doc.createRange();
50622         }
50623     },
50624     getParentElement: function()
50625     {
50626         
50627         this.assignDocWin();
50628         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
50629         
50630         var range = this.createRange(sel);
50631          
50632         try {
50633             var p = range.commonAncestorContainer;
50634             while (p.nodeType == 3) { // text node
50635                 p = p.parentNode;
50636             }
50637             return p;
50638         } catch (e) {
50639             return null;
50640         }
50641     
50642     },
50643     /***
50644      *
50645      * Range intersection.. the hard stuff...
50646      *  '-1' = before
50647      *  '0' = hits..
50648      *  '1' = after.
50649      *         [ -- selected range --- ]
50650      *   [fail]                        [fail]
50651      *
50652      *    basically..
50653      *      if end is before start or  hits it. fail.
50654      *      if start is after end or hits it fail.
50655      *
50656      *   if either hits (but other is outside. - then it's not 
50657      *   
50658      *    
50659      **/
50660     
50661     
50662     // @see http://www.thismuchiknow.co.uk/?p=64.
50663     rangeIntersectsNode : function(range, node)
50664     {
50665         var nodeRange = node.ownerDocument.createRange();
50666         try {
50667             nodeRange.selectNode(node);
50668         } catch (e) {
50669             nodeRange.selectNodeContents(node);
50670         }
50671     
50672         var rangeStartRange = range.cloneRange();
50673         rangeStartRange.collapse(true);
50674     
50675         var rangeEndRange = range.cloneRange();
50676         rangeEndRange.collapse(false);
50677     
50678         var nodeStartRange = nodeRange.cloneRange();
50679         nodeStartRange.collapse(true);
50680     
50681         var nodeEndRange = nodeRange.cloneRange();
50682         nodeEndRange.collapse(false);
50683     
50684         return rangeStartRange.compareBoundaryPoints(
50685                  Range.START_TO_START, nodeEndRange) == -1 &&
50686                rangeEndRange.compareBoundaryPoints(
50687                  Range.START_TO_START, nodeStartRange) == 1;
50688         
50689          
50690     },
50691     rangeCompareNode : function(range, node)
50692     {
50693         var nodeRange = node.ownerDocument.createRange();
50694         try {
50695             nodeRange.selectNode(node);
50696         } catch (e) {
50697             nodeRange.selectNodeContents(node);
50698         }
50699         
50700         
50701         range.collapse(true);
50702     
50703         nodeRange.collapse(true);
50704      
50705         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
50706         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
50707          
50708         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
50709         
50710         var nodeIsBefore   =  ss == 1;
50711         var nodeIsAfter    = ee == -1;
50712         
50713         if (nodeIsBefore && nodeIsAfter) {
50714             return 0; // outer
50715         }
50716         if (!nodeIsBefore && nodeIsAfter) {
50717             return 1; //right trailed.
50718         }
50719         
50720         if (nodeIsBefore && !nodeIsAfter) {
50721             return 2;  // left trailed.
50722         }
50723         // fully contined.
50724         return 3;
50725     },
50726  
50727     cleanWordChars : function(input) {// change the chars to hex code
50728         
50729        var swapCodes  = [ 
50730             [    8211, "&#8211;" ], 
50731             [    8212, "&#8212;" ], 
50732             [    8216,  "'" ],  
50733             [    8217, "'" ],  
50734             [    8220, '"' ],  
50735             [    8221, '"' ],  
50736             [    8226, "*" ],  
50737             [    8230, "..." ]
50738         ]; 
50739         var output = input;
50740         Roo.each(swapCodes, function(sw) { 
50741             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
50742             
50743             output = output.replace(swapper, sw[1]);
50744         });
50745         
50746         return output;
50747     },
50748     
50749      
50750     
50751         
50752     
50753     cleanUpChild : function (node)
50754     {
50755         
50756         new Roo.htmleditor.FilterComment({node : node});
50757         new Roo.htmleditor.FilterAttributes({
50758                 node : node,
50759                 attrib_black : this.ablack,
50760                 attrib_clean : this.aclean,
50761                 style_white : this.cwhite,
50762                 style_black : this.cblack
50763         });
50764         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
50765         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
50766          
50767         
50768     },
50769     
50770     /**
50771      * Clean up MS wordisms...
50772      * @deprecated - use filter directly
50773      */
50774     cleanWord : function(node)
50775     {
50776         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
50777         
50778     },
50779    
50780     
50781     /**
50782
50783      * @deprecated - use filters
50784      */
50785     cleanTableWidths : function(node)
50786     {
50787         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
50788         
50789  
50790     },
50791     
50792      
50793         
50794     applyBlacklists : function()
50795     {
50796         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
50797         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
50798         
50799         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
50800         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
50801         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
50802         
50803         this.white = [];
50804         this.black = [];
50805         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
50806             if (b.indexOf(tag) > -1) {
50807                 return;
50808             }
50809             this.white.push(tag);
50810             
50811         }, this);
50812         
50813         Roo.each(w, function(tag) {
50814             if (b.indexOf(tag) > -1) {
50815                 return;
50816             }
50817             if (this.white.indexOf(tag) > -1) {
50818                 return;
50819             }
50820             this.white.push(tag);
50821             
50822         }, this);
50823         
50824         
50825         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
50826             if (w.indexOf(tag) > -1) {
50827                 return;
50828             }
50829             this.black.push(tag);
50830             
50831         }, this);
50832         
50833         Roo.each(b, function(tag) {
50834             if (w.indexOf(tag) > -1) {
50835                 return;
50836             }
50837             if (this.black.indexOf(tag) > -1) {
50838                 return;
50839             }
50840             this.black.push(tag);
50841             
50842         }, this);
50843         
50844         
50845         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
50846         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
50847         
50848         this.cwhite = [];
50849         this.cblack = [];
50850         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
50851             if (b.indexOf(tag) > -1) {
50852                 return;
50853             }
50854             this.cwhite.push(tag);
50855             
50856         }, this);
50857         
50858         Roo.each(w, function(tag) {
50859             if (b.indexOf(tag) > -1) {
50860                 return;
50861             }
50862             if (this.cwhite.indexOf(tag) > -1) {
50863                 return;
50864             }
50865             this.cwhite.push(tag);
50866             
50867         }, this);
50868         
50869         
50870         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
50871             if (w.indexOf(tag) > -1) {
50872                 return;
50873             }
50874             this.cblack.push(tag);
50875             
50876         }, this);
50877         
50878         Roo.each(b, function(tag) {
50879             if (w.indexOf(tag) > -1) {
50880                 return;
50881             }
50882             if (this.cblack.indexOf(tag) > -1) {
50883                 return;
50884             }
50885             this.cblack.push(tag);
50886             
50887         }, this);
50888     },
50889     
50890     setStylesheets : function(stylesheets)
50891     {
50892         if(typeof(stylesheets) == 'string'){
50893             Roo.get(this.iframe.contentDocument.head).createChild({
50894                 tag : 'link',
50895                 rel : 'stylesheet',
50896                 type : 'text/css',
50897                 href : stylesheets
50898             });
50899             
50900             return;
50901         }
50902         var _this = this;
50903      
50904         Roo.each(stylesheets, function(s) {
50905             if(!s.length){
50906                 return;
50907             }
50908             
50909             Roo.get(_this.iframe.contentDocument.head).createChild({
50910                 tag : 'link',
50911                 rel : 'stylesheet',
50912                 type : 'text/css',
50913                 href : s
50914             });
50915         });
50916
50917         
50918     },
50919     
50920     
50921     updateLanguage : function()
50922     {
50923         if (!this.iframe || !this.iframe.contentDocument) {
50924             return;
50925         }
50926         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
50927     },
50928     
50929     
50930     removeStylesheets : function()
50931     {
50932         var _this = this;
50933         
50934         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
50935             s.remove();
50936         });
50937     },
50938     
50939     setStyle : function(style)
50940     {
50941         Roo.get(this.iframe.contentDocument.head).createChild({
50942             tag : 'style',
50943             type : 'text/css',
50944             html : style
50945         });
50946
50947         return;
50948     }
50949     
50950     // hide stuff that is not compatible
50951     /**
50952      * @event blur
50953      * @hide
50954      */
50955     /**
50956      * @event change
50957      * @hide
50958      */
50959     /**
50960      * @event focus
50961      * @hide
50962      */
50963     /**
50964      * @event specialkey
50965      * @hide
50966      */
50967     /**
50968      * @cfg {String} fieldClass @hide
50969      */
50970     /**
50971      * @cfg {String} focusClass @hide
50972      */
50973     /**
50974      * @cfg {String} autoCreate @hide
50975      */
50976     /**
50977      * @cfg {String} inputType @hide
50978      */
50979     /**
50980      * @cfg {String} invalidClass @hide
50981      */
50982     /**
50983      * @cfg {String} invalidText @hide
50984      */
50985     /**
50986      * @cfg {String} msgFx @hide
50987      */
50988     /**
50989      * @cfg {String} validateOnBlur @hide
50990      */
50991 });
50992
50993 Roo.HtmlEditorCore.white = [
50994         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
50995         
50996        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
50997        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
50998        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
50999        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
51000        'TABLE',   'UL',         'XMP', 
51001        
51002        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
51003       'THEAD',   'TR', 
51004      
51005       'DIR', 'MENU', 'OL', 'UL', 'DL',
51006        
51007       'EMBED',  'OBJECT'
51008 ];
51009
51010
51011 Roo.HtmlEditorCore.black = [
51012     //    'embed',  'object', // enable - backend responsiblity to clean thiese
51013         'APPLET', // 
51014         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
51015         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
51016         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
51017         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
51018         //'FONT' // CLEAN LATER..
51019         'COLGROUP', 'COL'   // messy tables.
51020         
51021         
51022 ];
51023 Roo.HtmlEditorCore.clean = [ // ?? needed???
51024      'SCRIPT', 'STYLE', 'TITLE', 'XML'
51025 ];
51026 Roo.HtmlEditorCore.tag_remove = [
51027     'FONT', 'TBODY'  
51028 ];
51029 // attributes..
51030
51031 Roo.HtmlEditorCore.ablack = [
51032     'on'
51033 ];
51034     
51035 Roo.HtmlEditorCore.aclean = [ 
51036     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
51037 ];
51038
51039 // protocols..
51040 Roo.HtmlEditorCore.pwhite= [
51041         'http',  'https',  'mailto'
51042 ];
51043
51044 // white listed style attributes.
51045 Roo.HtmlEditorCore.cwhite= [
51046       //  'text-align', /// default is to allow most things..
51047       
51048          
51049 //        'font-size'//??
51050 ];
51051
51052 // black listed style attributes.
51053 Roo.HtmlEditorCore.cblack= [
51054       //  'font-size' -- this can be set by the project 
51055 ];
51056
51057
51058
51059
51060     //<script type="text/javascript">
51061
51062 /*
51063  * Ext JS Library 1.1.1
51064  * Copyright(c) 2006-2007, Ext JS, LLC.
51065  * Licence LGPL
51066  * 
51067  */
51068  
51069  
51070 Roo.form.HtmlEditor = function(config){
51071     
51072     
51073     
51074     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
51075     
51076     if (!this.toolbars) {
51077         this.toolbars = [];
51078     }
51079     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
51080     
51081     
51082 };
51083
51084 /**
51085  * @class Roo.form.HtmlEditor
51086  * @extends Roo.form.Field
51087  * Provides a lightweight HTML Editor component.
51088  *
51089  * This has been tested on Fireforx / Chrome.. IE may not be so great..
51090  * 
51091  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
51092  * supported by this editor.</b><br/><br/>
51093  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
51094  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
51095  */
51096 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
51097     /**
51098      * @cfg {Boolean} clearUp
51099      */
51100     clearUp : true,
51101       /**
51102      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
51103      */
51104     toolbars : false,
51105    
51106      /**
51107      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
51108      *                        Roo.resizable.
51109      */
51110     resizable : false,
51111      /**
51112      * @cfg {Number} height (in pixels)
51113      */   
51114     height: 300,
51115    /**
51116      * @cfg {Number} width (in pixels)
51117      */   
51118     width: 500,
51119     
51120     /**
51121      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
51122      * 
51123      */
51124     stylesheets: false,
51125     
51126     
51127      /**
51128      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
51129      * 
51130      */
51131     cblack: false,
51132     /**
51133      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
51134      * 
51135      */
51136     cwhite: false,
51137     
51138      /**
51139      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
51140      * 
51141      */
51142     black: false,
51143     /**
51144      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
51145      * 
51146      */
51147     white: false,
51148     /**
51149      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
51150      */
51151     allowComments: false,
51152     /**
51153      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
51154      */
51155     enableBlocks : true,
51156     
51157     /**
51158      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
51159      *         if you are doing an email editor, this probably needs disabling, it's designed
51160      */
51161     autoClean: true,
51162     /**
51163      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
51164      */
51165     bodyCls : '',
51166     /**
51167      * @cfg {String} language default en - language of text (usefull for rtl languages)
51168      * 
51169      */
51170     language: 'en',
51171     
51172      
51173     // id of frame..
51174     frameId: false,
51175     
51176     // private properties
51177     validationEvent : false,
51178     deferHeight: true,
51179     initialized : false,
51180     activated : false,
51181     
51182     onFocus : Roo.emptyFn,
51183     iframePad:3,
51184     hideMode:'offsets',
51185     
51186     actionMode : 'container', // defaults to hiding it...
51187     
51188     defaultAutoCreate : { // modified by initCompnoent..
51189         tag: "textarea",
51190         style:"width:500px;height:300px;",
51191         autocomplete: "new-password"
51192     },
51193
51194     // private
51195     initComponent : function(){
51196         this.addEvents({
51197             /**
51198              * @event initialize
51199              * Fires when the editor is fully initialized (including the iframe)
51200              * @param {HtmlEditor} this
51201              */
51202             initialize: true,
51203             /**
51204              * @event activate
51205              * Fires when the editor is first receives the focus. Any insertion must wait
51206              * until after this event.
51207              * @param {HtmlEditor} this
51208              */
51209             activate: true,
51210              /**
51211              * @event beforesync
51212              * Fires before the textarea is updated with content from the editor iframe. Return false
51213              * to cancel the sync.
51214              * @param {HtmlEditor} this
51215              * @param {String} html
51216              */
51217             beforesync: true,
51218              /**
51219              * @event beforepush
51220              * Fires before the iframe editor is updated with content from the textarea. Return false
51221              * to cancel the push.
51222              * @param {HtmlEditor} this
51223              * @param {String} html
51224              */
51225             beforepush: true,
51226              /**
51227              * @event sync
51228              * Fires when the textarea is updated with content from the editor iframe.
51229              * @param {HtmlEditor} this
51230              * @param {String} html
51231              */
51232             sync: true,
51233              /**
51234              * @event push
51235              * Fires when the iframe editor is updated with content from the textarea.
51236              * @param {HtmlEditor} this
51237              * @param {String} html
51238              */
51239             push: true,
51240              /**
51241              * @event editmodechange
51242              * Fires when the editor switches edit modes
51243              * @param {HtmlEditor} this
51244              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
51245              */
51246             editmodechange: true,
51247             /**
51248              * @event editorevent
51249              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
51250              * @param {HtmlEditor} this
51251              */
51252             editorevent: true,
51253             /**
51254              * @event firstfocus
51255              * Fires when on first focus - needed by toolbars..
51256              * @param {HtmlEditor} this
51257              */
51258             firstfocus: true,
51259             /**
51260              * @event autosave
51261              * Auto save the htmlEditor value as a file into Events
51262              * @param {HtmlEditor} this
51263              */
51264             autosave: true,
51265             /**
51266              * @event savedpreview
51267              * preview the saved version of htmlEditor
51268              * @param {HtmlEditor} this
51269              */
51270             savedpreview: true,
51271             
51272             /**
51273             * @event stylesheetsclick
51274             * Fires when press the Sytlesheets button
51275             * @param {Roo.HtmlEditorCore} this
51276             */
51277             stylesheetsclick: true,
51278             /**
51279             * @event paste
51280             * Fires when press user pastes into the editor
51281             * @param {Roo.HtmlEditorCore} this
51282             */
51283             paste: true 
51284         });
51285         this.defaultAutoCreate =  {
51286             tag: "textarea",
51287             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
51288             autocomplete: "new-password"
51289         };
51290     },
51291
51292     /**
51293      * Protected method that will not generally be called directly. It
51294      * is called when the editor creates its toolbar. Override this method if you need to
51295      * add custom toolbar buttons.
51296      * @param {HtmlEditor} editor
51297      */
51298     createToolbar : function(editor){
51299         Roo.log("create toolbars");
51300         if (!editor.toolbars || !editor.toolbars.length) {
51301             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
51302         }
51303         
51304         for (var i =0 ; i < editor.toolbars.length;i++) {
51305             editor.toolbars[i] = Roo.factory(
51306                     typeof(editor.toolbars[i]) == 'string' ?
51307                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
51308                 Roo.form.HtmlEditor);
51309             editor.toolbars[i].init(editor);
51310         }
51311          
51312         
51313     },
51314     /**
51315      * get the Context selected node
51316      * @returns {DomElement|boolean} selected node if active or false if none
51317      * 
51318      */
51319     getSelectedNode : function()
51320     {
51321         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
51322             return false;
51323         }
51324         return this.toolbars[1].tb.selectedNode;
51325     
51326     },
51327     // private
51328     onRender : function(ct, position)
51329     {
51330         var _t = this;
51331         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
51332         
51333         this.wrap = this.el.wrap({
51334             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
51335         });
51336         
51337         this.editorcore.onRender(ct, position);
51338          
51339         if (this.resizable) {
51340             this.resizeEl = new Roo.Resizable(this.wrap, {
51341                 pinned : true,
51342                 wrap: true,
51343                 dynamic : true,
51344                 minHeight : this.height,
51345                 height: this.height,
51346                 handles : this.resizable,
51347                 width: this.width,
51348                 listeners : {
51349                     resize : function(r, w, h) {
51350                         _t.onResize(w,h); // -something
51351                     }
51352                 }
51353             });
51354             
51355         }
51356         this.createToolbar(this);
51357        
51358         
51359         if(!this.width){
51360             this.setSize(this.wrap.getSize());
51361         }
51362         if (this.resizeEl) {
51363             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
51364             // should trigger onReize..
51365         }
51366         
51367         this.keyNav = new Roo.KeyNav(this.el, {
51368             
51369             "tab" : function(e){
51370                 e.preventDefault();
51371                 
51372                 var value = this.getValue();
51373                 
51374                 var start = this.el.dom.selectionStart;
51375                 var end = this.el.dom.selectionEnd;
51376                 
51377                 if(!e.shiftKey){
51378                     
51379                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
51380                     this.el.dom.setSelectionRange(end + 1, end + 1);
51381                     return;
51382                 }
51383                 
51384                 var f = value.substring(0, start).split("\t");
51385                 
51386                 if(f.pop().length != 0){
51387                     return;
51388                 }
51389                 
51390                 this.setValue(f.join("\t") + value.substring(end));
51391                 this.el.dom.setSelectionRange(start - 1, start - 1);
51392                 
51393             },
51394             
51395             "home" : function(e){
51396                 e.preventDefault();
51397                 
51398                 var curr = this.el.dom.selectionStart;
51399                 var lines = this.getValue().split("\n");
51400                 
51401                 if(!lines.length){
51402                     return;
51403                 }
51404                 
51405                 if(e.ctrlKey){
51406                     this.el.dom.setSelectionRange(0, 0);
51407                     return;
51408                 }
51409                 
51410                 var pos = 0;
51411                 
51412                 for (var i = 0; i < lines.length;i++) {
51413                     pos += lines[i].length;
51414                     
51415                     if(i != 0){
51416                         pos += 1;
51417                     }
51418                     
51419                     if(pos < curr){
51420                         continue;
51421                     }
51422                     
51423                     pos -= lines[i].length;
51424                     
51425                     break;
51426                 }
51427                 
51428                 if(!e.shiftKey){
51429                     this.el.dom.setSelectionRange(pos, pos);
51430                     return;
51431                 }
51432                 
51433                 this.el.dom.selectionStart = pos;
51434                 this.el.dom.selectionEnd = curr;
51435             },
51436             
51437             "end" : function(e){
51438                 e.preventDefault();
51439                 
51440                 var curr = this.el.dom.selectionStart;
51441                 var lines = this.getValue().split("\n");
51442                 
51443                 if(!lines.length){
51444                     return;
51445                 }
51446                 
51447                 if(e.ctrlKey){
51448                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
51449                     return;
51450                 }
51451                 
51452                 var pos = 0;
51453                 
51454                 for (var i = 0; i < lines.length;i++) {
51455                     
51456                     pos += lines[i].length;
51457                     
51458                     if(i != 0){
51459                         pos += 1;
51460                     }
51461                     
51462                     if(pos < curr){
51463                         continue;
51464                     }
51465                     
51466                     break;
51467                 }
51468                 
51469                 if(!e.shiftKey){
51470                     this.el.dom.setSelectionRange(pos, pos);
51471                     return;
51472                 }
51473                 
51474                 this.el.dom.selectionStart = curr;
51475                 this.el.dom.selectionEnd = pos;
51476             },
51477
51478             scope : this,
51479
51480             doRelay : function(foo, bar, hname){
51481                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
51482             },
51483
51484             forceKeyDown: true
51485         });
51486         
51487 //        if(this.autosave && this.w){
51488 //            this.autoSaveFn = setInterval(this.autosave, 1000);
51489 //        }
51490     },
51491
51492     // private
51493     onResize : function(w, h)
51494     {
51495         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
51496         var ew = false;
51497         var eh = false;
51498         
51499         if(this.el ){
51500             if(typeof w == 'number'){
51501                 var aw = w - this.wrap.getFrameWidth('lr');
51502                 this.el.setWidth(this.adjustWidth('textarea', aw));
51503                 ew = aw;
51504             }
51505             if(typeof h == 'number'){
51506                 var tbh = 0;
51507                 for (var i =0; i < this.toolbars.length;i++) {
51508                     // fixme - ask toolbars for heights?
51509                     tbh += this.toolbars[i].tb.el.getHeight();
51510                     if (this.toolbars[i].footer) {
51511                         tbh += this.toolbars[i].footer.el.getHeight();
51512                     }
51513                 }
51514                 
51515                 
51516                 
51517                 
51518                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
51519                 ah -= 5; // knock a few pixes off for look..
51520 //                Roo.log(ah);
51521                 this.el.setHeight(this.adjustWidth('textarea', ah));
51522                 var eh = ah;
51523             }
51524         }
51525         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
51526         this.editorcore.onResize(ew,eh);
51527         
51528     },
51529
51530     /**
51531      * Toggles the editor between standard and source edit mode.
51532      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
51533      */
51534     toggleSourceEdit : function(sourceEditMode)
51535     {
51536         this.editorcore.toggleSourceEdit(sourceEditMode);
51537         
51538         if(this.editorcore.sourceEditMode){
51539             Roo.log('editor - showing textarea');
51540             
51541 //            Roo.log('in');
51542 //            Roo.log(this.syncValue());
51543             this.editorcore.syncValue();
51544             this.el.removeClass('x-hidden');
51545             this.el.dom.removeAttribute('tabIndex');
51546             this.el.focus();
51547             this.el.dom.scrollTop = 0;
51548             
51549             
51550             for (var i = 0; i < this.toolbars.length; i++) {
51551                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51552                     this.toolbars[i].tb.hide();
51553                     this.toolbars[i].footer.hide();
51554                 }
51555             }
51556             
51557         }else{
51558             Roo.log('editor - hiding textarea');
51559 //            Roo.log('out')
51560 //            Roo.log(this.pushValue()); 
51561             this.editorcore.pushValue();
51562             
51563             this.el.addClass('x-hidden');
51564             this.el.dom.setAttribute('tabIndex', -1);
51565             
51566             for (var i = 0; i < this.toolbars.length; i++) {
51567                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
51568                     this.toolbars[i].tb.show();
51569                     this.toolbars[i].footer.show();
51570                 }
51571             }
51572             
51573             //this.deferFocus();
51574         }
51575         
51576         this.setSize(this.wrap.getSize());
51577         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
51578         
51579         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
51580     },
51581  
51582     // private (for BoxComponent)
51583     adjustSize : Roo.BoxComponent.prototype.adjustSize,
51584
51585     // private (for BoxComponent)
51586     getResizeEl : function(){
51587         return this.wrap;
51588     },
51589
51590     // private (for BoxComponent)
51591     getPositionEl : function(){
51592         return this.wrap;
51593     },
51594
51595     // private
51596     initEvents : function(){
51597         this.originalValue = this.getValue();
51598     },
51599
51600     /**
51601      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
51602      * @method
51603      */
51604     markInvalid : Roo.emptyFn,
51605     /**
51606      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
51607      * @method
51608      */
51609     clearInvalid : Roo.emptyFn,
51610
51611     setValue : function(v){
51612         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
51613         this.editorcore.pushValue();
51614     },
51615
51616     /**
51617      * update the language in the body - really done by core
51618      * @param {String} language - eg. en / ar / zh-CN etc..
51619      */
51620     updateLanguage : function(lang)
51621     {
51622         this.language = lang;
51623         this.editorcore.language = lang;
51624         this.editorcore.updateLanguage();
51625      
51626     },
51627     // private
51628     deferFocus : function(){
51629         this.focus.defer(10, this);
51630     },
51631
51632     // doc'ed in Field
51633     focus : function(){
51634         this.editorcore.focus();
51635         
51636     },
51637       
51638
51639     // private
51640     onDestroy : function(){
51641         
51642         
51643         
51644         if(this.rendered){
51645             
51646             for (var i =0; i < this.toolbars.length;i++) {
51647                 // fixme - ask toolbars for heights?
51648                 this.toolbars[i].onDestroy();
51649             }
51650             
51651             this.wrap.dom.innerHTML = '';
51652             this.wrap.remove();
51653         }
51654     },
51655
51656     // private
51657     onFirstFocus : function(){
51658         //Roo.log("onFirstFocus");
51659         this.editorcore.onFirstFocus();
51660          for (var i =0; i < this.toolbars.length;i++) {
51661             this.toolbars[i].onFirstFocus();
51662         }
51663         
51664     },
51665     
51666     // private
51667     syncValue : function()
51668     {
51669         this.editorcore.syncValue();
51670     },
51671     
51672     pushValue : function()
51673     {
51674         this.editorcore.pushValue();
51675     },
51676     
51677     setStylesheets : function(stylesheets)
51678     {
51679         this.editorcore.setStylesheets(stylesheets);
51680     },
51681     
51682     removeStylesheets : function()
51683     {
51684         this.editorcore.removeStylesheets();
51685     }
51686      
51687     
51688     // hide stuff that is not compatible
51689     /**
51690      * @event blur
51691      * @hide
51692      */
51693     /**
51694      * @event change
51695      * @hide
51696      */
51697     /**
51698      * @event focus
51699      * @hide
51700      */
51701     /**
51702      * @event specialkey
51703      * @hide
51704      */
51705     /**
51706      * @cfg {String} fieldClass @hide
51707      */
51708     /**
51709      * @cfg {String} focusClass @hide
51710      */
51711     /**
51712      * @cfg {String} autoCreate @hide
51713      */
51714     /**
51715      * @cfg {String} inputType @hide
51716      */
51717     /**
51718      * @cfg {String} invalidClass @hide
51719      */
51720     /**
51721      * @cfg {String} invalidText @hide
51722      */
51723     /**
51724      * @cfg {String} msgFx @hide
51725      */
51726     /**
51727      * @cfg {String} validateOnBlur @hide
51728      */
51729 });
51730  
51731     /*
51732  * Based on
51733  * Ext JS Library 1.1.1
51734  * Copyright(c) 2006-2007, Ext JS, LLC.
51735  *  
51736  
51737  */
51738
51739 /**
51740  * @class Roo.form.HtmlEditor.ToolbarStandard
51741  * Basic Toolbar
51742
51743  * Usage:
51744  *
51745  new Roo.form.HtmlEditor({
51746     ....
51747     toolbars : [
51748         new Roo.form.HtmlEditorToolbar1({
51749             disable : { fonts: 1 , format: 1, ..., ... , ...],
51750             btns : [ .... ]
51751         })
51752     }
51753      
51754  * 
51755  * @cfg {Object} disable List of elements to disable..
51756  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
51757  * 
51758  * 
51759  * NEEDS Extra CSS? 
51760  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
51761  */
51762  
51763 Roo.form.HtmlEditor.ToolbarStandard = function(config)
51764 {
51765     
51766     Roo.apply(this, config);
51767     
51768     // default disabled, based on 'good practice'..
51769     this.disable = this.disable || {};
51770     Roo.applyIf(this.disable, {
51771         fontSize : true,
51772         colors : true,
51773         specialElements : true
51774     });
51775     
51776     
51777     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
51778     // dont call parent... till later.
51779 }
51780
51781 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
51782     
51783     tb: false,
51784     
51785     rendered: false,
51786     
51787     editor : false,
51788     editorcore : false,
51789     /**
51790      * @cfg {Object} disable  List of toolbar elements to disable
51791          
51792      */
51793     disable : false,
51794     
51795     
51796      /**
51797      * @cfg {String} createLinkText The default text for the create link prompt
51798      */
51799     createLinkText : 'Please enter the URL for the link:',
51800     /**
51801      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
51802      */
51803     defaultLinkValue : 'http:/'+'/',
51804    
51805     
51806       /**
51807      * @cfg {Array} fontFamilies An array of available font families
51808      */
51809     fontFamilies : [
51810         'Arial',
51811         'Courier New',
51812         'Tahoma',
51813         'Times New Roman',
51814         'Verdana'
51815     ],
51816     
51817     specialChars : [
51818            "&#169;",
51819           "&#174;",     
51820           "&#8482;",    
51821           "&#163;" ,    
51822          // "&#8212;",    
51823           "&#8230;",    
51824           "&#247;" ,    
51825         //  "&#225;" ,     ?? a acute?
51826            "&#8364;"    , //Euro
51827        //   "&#8220;"    ,
51828         //  "&#8221;"    ,
51829         //  "&#8226;"    ,
51830           "&#176;"  //   , // degrees
51831
51832          // "&#233;"     , // e ecute
51833          // "&#250;"     , // u ecute?
51834     ],
51835     
51836     specialElements : [
51837         {
51838             text: "Insert Table",
51839             xtype: 'MenuItem',
51840             xns : Roo.Menu,
51841             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
51842                 
51843         },
51844         {    
51845             text: "Insert Image",
51846             xtype: 'MenuItem',
51847             xns : Roo.Menu,
51848             ihtml : '<img src="about:blank"/>'
51849             
51850         }
51851         
51852          
51853     ],
51854     
51855     
51856     inputElements : [ 
51857             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
51858             "input:submit", "input:button", "select", "textarea", "label" ],
51859     formats : [
51860         ["p"] ,  
51861         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
51862         ["pre"],[ "code"], 
51863         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
51864         ['div'],['span'],
51865         ['sup'],['sub']
51866     ],
51867     
51868     cleanStyles : [
51869         "font-size"
51870     ],
51871      /**
51872      * @cfg {String} defaultFont default font to use.
51873      */
51874     defaultFont: 'tahoma',
51875    
51876     fontSelect : false,
51877     
51878     
51879     formatCombo : false,
51880     
51881     init : function(editor)
51882     {
51883         this.editor = editor;
51884         this.editorcore = editor.editorcore ? editor.editorcore : editor;
51885         var editorcore = this.editorcore;
51886         
51887         var _t = this;
51888         
51889         var fid = editorcore.frameId;
51890         var etb = this;
51891         function btn(id, toggle, handler){
51892             var xid = fid + '-'+ id ;
51893             return {
51894                 id : xid,
51895                 cmd : id,
51896                 cls : 'x-btn-icon x-edit-'+id,
51897                 enableToggle:toggle !== false,
51898                 scope: _t, // was editor...
51899                 handler:handler||_t.relayBtnCmd,
51900                 clickEvent:'mousedown',
51901                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
51902                 tabIndex:-1
51903             };
51904         }
51905         
51906         
51907         
51908         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
51909         this.tb = tb;
51910          // stop form submits
51911         tb.el.on('click', function(e){
51912             e.preventDefault(); // what does this do?
51913         });
51914
51915         if(!this.disable.font) { // && !Roo.isSafari){
51916             /* why no safari for fonts 
51917             editor.fontSelect = tb.el.createChild({
51918                 tag:'select',
51919                 tabIndex: -1,
51920                 cls:'x-font-select',
51921                 html: this.createFontOptions()
51922             });
51923             
51924             editor.fontSelect.on('change', function(){
51925                 var font = editor.fontSelect.dom.value;
51926                 editor.relayCmd('fontname', font);
51927                 editor.deferFocus();
51928             }, editor);
51929             
51930             tb.add(
51931                 editor.fontSelect.dom,
51932                 '-'
51933             );
51934             */
51935             
51936         };
51937         if(!this.disable.formats){
51938             this.formatCombo = new Roo.form.ComboBox({
51939                 store: new Roo.data.SimpleStore({
51940                     id : 'tag',
51941                     fields: ['tag'],
51942                     data : this.formats // from states.js
51943                 }),
51944                 blockFocus : true,
51945                 name : '',
51946                 //autoCreate : {tag: "div",  size: "20"},
51947                 displayField:'tag',
51948                 typeAhead: false,
51949                 mode: 'local',
51950                 editable : false,
51951                 triggerAction: 'all',
51952                 emptyText:'Add tag',
51953                 selectOnFocus:true,
51954                 width:135,
51955                 listeners : {
51956                     'select': function(c, r, i) {
51957                         editorcore.insertTag(r.get('tag'));
51958                         editor.focus();
51959                     }
51960                 }
51961
51962             });
51963             tb.addField(this.formatCombo);
51964             
51965         }
51966         
51967         if(!this.disable.format){
51968             tb.add(
51969                 btn('bold'),
51970                 btn('italic'),
51971                 btn('underline'),
51972                 btn('strikethrough')
51973             );
51974         };
51975         if(!this.disable.fontSize){
51976             tb.add(
51977                 '-',
51978                 
51979                 
51980                 btn('increasefontsize', false, editorcore.adjustFont),
51981                 btn('decreasefontsize', false, editorcore.adjustFont)
51982             );
51983         };
51984         
51985         
51986         if(!this.disable.colors){
51987             tb.add(
51988                 '-', {
51989                     id:editorcore.frameId +'-forecolor',
51990                     cls:'x-btn-icon x-edit-forecolor',
51991                     clickEvent:'mousedown',
51992                     tooltip: this.buttonTips['forecolor'] || undefined,
51993                     tabIndex:-1,
51994                     menu : new Roo.menu.ColorMenu({
51995                         allowReselect: true,
51996                         focus: Roo.emptyFn,
51997                         value:'000000',
51998                         plain:true,
51999                         selectHandler: function(cp, color){
52000                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
52001                             editor.deferFocus();
52002                         },
52003                         scope: editorcore,
52004                         clickEvent:'mousedown'
52005                     })
52006                 }, {
52007                     id:editorcore.frameId +'backcolor',
52008                     cls:'x-btn-icon x-edit-backcolor',
52009                     clickEvent:'mousedown',
52010                     tooltip: this.buttonTips['backcolor'] || undefined,
52011                     tabIndex:-1,
52012                     menu : new Roo.menu.ColorMenu({
52013                         focus: Roo.emptyFn,
52014                         value:'FFFFFF',
52015                         plain:true,
52016                         allowReselect: true,
52017                         selectHandler: function(cp, color){
52018                             if(Roo.isGecko){
52019                                 editorcore.execCmd('useCSS', false);
52020                                 editorcore.execCmd('hilitecolor', color);
52021                                 editorcore.execCmd('useCSS', true);
52022                                 editor.deferFocus();
52023                             }else{
52024                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
52025                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
52026                                 editor.deferFocus();
52027                             }
52028                         },
52029                         scope:editorcore,
52030                         clickEvent:'mousedown'
52031                     })
52032                 }
52033             );
52034         };
52035         // now add all the items...
52036         
52037
52038         if(!this.disable.alignments){
52039             tb.add(
52040                 '-',
52041                 btn('justifyleft'),
52042                 btn('justifycenter'),
52043                 btn('justifyright')
52044             );
52045         };
52046
52047         //if(!Roo.isSafari){
52048             if(!this.disable.links){
52049                 tb.add(
52050                     '-',
52051                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
52052                 );
52053             };
52054
52055             if(!this.disable.lists){
52056                 tb.add(
52057                     '-',
52058                     btn('insertorderedlist'),
52059                     btn('insertunorderedlist')
52060                 );
52061             }
52062             if(!this.disable.sourceEdit){
52063                 tb.add(
52064                     '-',
52065                     btn('sourceedit', true, function(btn){
52066                         this.toggleSourceEdit(btn.pressed);
52067                     })
52068                 );
52069             }
52070         //}
52071         
52072         var smenu = { };
52073         // special menu.. - needs to be tidied up..
52074         if (!this.disable.special) {
52075             smenu = {
52076                 text: "&#169;",
52077                 cls: 'x-edit-none',
52078                 
52079                 menu : {
52080                     items : []
52081                 }
52082             };
52083             for (var i =0; i < this.specialChars.length; i++) {
52084                 smenu.menu.items.push({
52085                     
52086                     html: this.specialChars[i],
52087                     handler: function(a,b) {
52088                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
52089                         //editor.insertAtCursor(a.html);
52090                         
52091                     },
52092                     tabIndex:-1
52093                 });
52094             }
52095             
52096             
52097             tb.add(smenu);
52098             
52099             
52100         }
52101         
52102         var cmenu = { };
52103         if (!this.disable.cleanStyles) {
52104             cmenu = {
52105                 cls: 'x-btn-icon x-btn-clear',
52106                 
52107                 menu : {
52108                     items : []
52109                 }
52110             };
52111             for (var i =0; i < this.cleanStyles.length; i++) {
52112                 cmenu.menu.items.push({
52113                     actiontype : this.cleanStyles[i],
52114                     html: 'Remove ' + this.cleanStyles[i],
52115                     handler: function(a,b) {
52116 //                        Roo.log(a);
52117 //                        Roo.log(b);
52118                         var c = Roo.get(editorcore.doc.body);
52119                         c.select('[style]').each(function(s) {
52120                             s.dom.style.removeProperty(a.actiontype);
52121                         });
52122                         editorcore.syncValue();
52123                     },
52124                     tabIndex:-1
52125                 });
52126             }
52127             cmenu.menu.items.push({
52128                 actiontype : 'tablewidths',
52129                 html: 'Remove Table Widths',
52130                 handler: function(a,b) {
52131                     editorcore.cleanTableWidths();
52132                     editorcore.syncValue();
52133                 },
52134                 tabIndex:-1
52135             });
52136             cmenu.menu.items.push({
52137                 actiontype : 'word',
52138                 html: 'Remove MS Word Formating',
52139                 handler: function(a,b) {
52140                     editorcore.cleanWord();
52141                     editorcore.syncValue();
52142                 },
52143                 tabIndex:-1
52144             });
52145             
52146             cmenu.menu.items.push({
52147                 actiontype : 'all',
52148                 html: 'Remove All Styles',
52149                 handler: function(a,b) {
52150                     
52151                     var c = Roo.get(editorcore.doc.body);
52152                     c.select('[style]').each(function(s) {
52153                         s.dom.removeAttribute('style');
52154                     });
52155                     editorcore.syncValue();
52156                 },
52157                 tabIndex:-1
52158             });
52159             
52160             cmenu.menu.items.push({
52161                 actiontype : 'all',
52162                 html: 'Remove All CSS Classes',
52163                 handler: function(a,b) {
52164                     
52165                     var c = Roo.get(editorcore.doc.body);
52166                     c.select('[class]').each(function(s) {
52167                         s.dom.removeAttribute('class');
52168                     });
52169                     editorcore.cleanWord();
52170                     editorcore.syncValue();
52171                 },
52172                 tabIndex:-1
52173             });
52174             
52175              cmenu.menu.items.push({
52176                 actiontype : 'tidy',
52177                 html: 'Tidy HTML Source',
52178                 handler: function(a,b) {
52179                     new Roo.htmleditor.Tidy(editorcore.doc.body);
52180                     editorcore.syncValue();
52181                 },
52182                 tabIndex:-1
52183             });
52184             
52185             
52186             tb.add(cmenu);
52187         }
52188          
52189         if (!this.disable.specialElements) {
52190             var semenu = {
52191                 text: "Other;",
52192                 cls: 'x-edit-none',
52193                 menu : {
52194                     items : []
52195                 }
52196             };
52197             for (var i =0; i < this.specialElements.length; i++) {
52198                 semenu.menu.items.push(
52199                     Roo.apply({ 
52200                         handler: function(a,b) {
52201                             editor.insertAtCursor(this.ihtml);
52202                         }
52203                     }, this.specialElements[i])
52204                 );
52205                     
52206             }
52207             
52208             tb.add(semenu);
52209             
52210             
52211         }
52212          
52213         
52214         if (this.btns) {
52215             for(var i =0; i< this.btns.length;i++) {
52216                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
52217                 b.cls =  'x-edit-none';
52218                 
52219                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
52220                     b.cls += ' x-init-enable';
52221                 }
52222                 
52223                 b.scope = editorcore;
52224                 tb.add(b);
52225             }
52226         
52227         }
52228         
52229         
52230         
52231         // disable everything...
52232         
52233         this.tb.items.each(function(item){
52234             
52235            if(
52236                 item.id != editorcore.frameId+ '-sourceedit' && 
52237                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
52238             ){
52239                 
52240                 item.disable();
52241             }
52242         });
52243         this.rendered = true;
52244         
52245         // the all the btns;
52246         editor.on('editorevent', this.updateToolbar, this);
52247         // other toolbars need to implement this..
52248         //editor.on('editmodechange', this.updateToolbar, this);
52249     },
52250     
52251     
52252     relayBtnCmd : function(btn) {
52253         this.editorcore.relayCmd(btn.cmd);
52254     },
52255     // private used internally
52256     createLink : function(){
52257         //Roo.log("create link?");
52258         var ec = this.editorcore;
52259         var ar = ec.getAllAncestors();
52260         var n = false;
52261         for(var i = 0;i< ar.length;i++) {
52262             if (ar[i] && ar[i].nodeName == 'A') {
52263                 n = ar[i];
52264                 break;
52265             }
52266         }
52267         
52268         (function() {
52269             
52270             Roo.MessageBox.show({
52271                 title : "Add / Edit Link URL",
52272                 msg : "Enter the url for the link",
52273                 buttons: Roo.MessageBox.OKCANCEL,
52274                 fn: function(btn, url){
52275                     if (btn != 'ok') {
52276                         return;
52277                     }
52278                     if(url && url != 'http:/'+'/'){
52279                         if (n) {
52280                             n.setAttribute('href', url);
52281                         } else {
52282                             ec.relayCmd('createlink', url);
52283                         }
52284                     }
52285                 },
52286                 minWidth:250,
52287                 prompt:true,
52288                 //multiline: multiline,
52289                 modal : true,
52290                 value :  n  ? n.getAttribute('href') : '' 
52291             });
52292             
52293              
52294         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
52295         
52296     },
52297
52298     
52299     /**
52300      * Protected method that will not generally be called directly. It triggers
52301      * a toolbar update by reading the markup state of the current selection in the editor.
52302      */
52303     updateToolbar: function(){
52304
52305         if(!this.editorcore.activated){
52306             this.editor.onFirstFocus();
52307             return;
52308         }
52309
52310         var btns = this.tb.items.map, 
52311             doc = this.editorcore.doc,
52312             frameId = this.editorcore.frameId;
52313
52314         if(!this.disable.font && !Roo.isSafari){
52315             /*
52316             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
52317             if(name != this.fontSelect.dom.value){
52318                 this.fontSelect.dom.value = name;
52319             }
52320             */
52321         }
52322         if(!this.disable.format){
52323             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
52324             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
52325             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
52326             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
52327         }
52328         if(!this.disable.alignments){
52329             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
52330             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
52331             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
52332         }
52333         if(!Roo.isSafari && !this.disable.lists){
52334             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
52335             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
52336         }
52337         
52338         var ans = this.editorcore.getAllAncestors();
52339         if (this.formatCombo) {
52340             
52341             
52342             var store = this.formatCombo.store;
52343             this.formatCombo.setValue("");
52344             for (var i =0; i < ans.length;i++) {
52345                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
52346                     // select it..
52347                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
52348                     break;
52349                 }
52350             }
52351         }
52352         
52353         
52354         
52355         // hides menus... - so this cant be on a menu...
52356         Roo.menu.MenuMgr.hideAll();
52357
52358         //this.editorsyncValue();
52359     },
52360    
52361     
52362     createFontOptions : function(){
52363         var buf = [], fs = this.fontFamilies, ff, lc;
52364         
52365         
52366         
52367         for(var i = 0, len = fs.length; i< len; i++){
52368             ff = fs[i];
52369             lc = ff.toLowerCase();
52370             buf.push(
52371                 '<option value="',lc,'" style="font-family:',ff,';"',
52372                     (this.defaultFont == lc ? ' selected="true">' : '>'),
52373                     ff,
52374                 '</option>'
52375             );
52376         }
52377         return buf.join('');
52378     },
52379     
52380     toggleSourceEdit : function(sourceEditMode){
52381         
52382         Roo.log("toolbar toogle");
52383         if(sourceEditMode === undefined){
52384             sourceEditMode = !this.sourceEditMode;
52385         }
52386         this.sourceEditMode = sourceEditMode === true;
52387         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
52388         // just toggle the button?
52389         if(btn.pressed !== this.sourceEditMode){
52390             btn.toggle(this.sourceEditMode);
52391             return;
52392         }
52393         
52394         if(sourceEditMode){
52395             Roo.log("disabling buttons");
52396             this.tb.items.each(function(item){
52397                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
52398                     item.disable();
52399                 }
52400             });
52401           
52402         }else{
52403             Roo.log("enabling buttons");
52404             if(this.editorcore.initialized){
52405                 this.tb.items.each(function(item){
52406                     item.enable();
52407                 });
52408                 // initialize 'blocks'
52409                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
52410                     Roo.htmleditor.Block.factory(e).updateElement(e);
52411                 },this);
52412             
52413             }
52414             
52415         }
52416         Roo.log("calling toggole on editor");
52417         // tell the editor that it's been pressed..
52418         this.editor.toggleSourceEdit(sourceEditMode);
52419        
52420     },
52421      /**
52422      * Object collection of toolbar tooltips for the buttons in the editor. The key
52423      * is the command id associated with that button and the value is a valid QuickTips object.
52424      * For example:
52425 <pre><code>
52426 {
52427     bold : {
52428         title: 'Bold (Ctrl+B)',
52429         text: 'Make the selected text bold.',
52430         cls: 'x-html-editor-tip'
52431     },
52432     italic : {
52433         title: 'Italic (Ctrl+I)',
52434         text: 'Make the selected text italic.',
52435         cls: 'x-html-editor-tip'
52436     },
52437     ...
52438 </code></pre>
52439     * @type Object
52440      */
52441     buttonTips : {
52442         bold : {
52443             title: 'Bold (Ctrl+B)',
52444             text: 'Make the selected text bold.',
52445             cls: 'x-html-editor-tip'
52446         },
52447         italic : {
52448             title: 'Italic (Ctrl+I)',
52449             text: 'Make the selected text italic.',
52450             cls: 'x-html-editor-tip'
52451         },
52452         underline : {
52453             title: 'Underline (Ctrl+U)',
52454             text: 'Underline the selected text.',
52455             cls: 'x-html-editor-tip'
52456         },
52457         strikethrough : {
52458             title: 'Strikethrough',
52459             text: 'Strikethrough the selected text.',
52460             cls: 'x-html-editor-tip'
52461         },
52462         increasefontsize : {
52463             title: 'Grow Text',
52464             text: 'Increase the font size.',
52465             cls: 'x-html-editor-tip'
52466         },
52467         decreasefontsize : {
52468             title: 'Shrink Text',
52469             text: 'Decrease the font size.',
52470             cls: 'x-html-editor-tip'
52471         },
52472         backcolor : {
52473             title: 'Text Highlight Color',
52474             text: 'Change the background color of the selected text.',
52475             cls: 'x-html-editor-tip'
52476         },
52477         forecolor : {
52478             title: 'Font Color',
52479             text: 'Change the color of the selected text.',
52480             cls: 'x-html-editor-tip'
52481         },
52482         justifyleft : {
52483             title: 'Align Text Left',
52484             text: 'Align text to the left.',
52485             cls: 'x-html-editor-tip'
52486         },
52487         justifycenter : {
52488             title: 'Center Text',
52489             text: 'Center text in the editor.',
52490             cls: 'x-html-editor-tip'
52491         },
52492         justifyright : {
52493             title: 'Align Text Right',
52494             text: 'Align text to the right.',
52495             cls: 'x-html-editor-tip'
52496         },
52497         insertunorderedlist : {
52498             title: 'Bullet List',
52499             text: 'Start a bulleted list.',
52500             cls: 'x-html-editor-tip'
52501         },
52502         insertorderedlist : {
52503             title: 'Numbered List',
52504             text: 'Start a numbered list.',
52505             cls: 'x-html-editor-tip'
52506         },
52507         createlink : {
52508             title: 'Hyperlink',
52509             text: 'Make the selected text a hyperlink.',
52510             cls: 'x-html-editor-tip'
52511         },
52512         sourceedit : {
52513             title: 'Source Edit',
52514             text: 'Switch to source editing mode.',
52515             cls: 'x-html-editor-tip'
52516         }
52517     },
52518     // private
52519     onDestroy : function(){
52520         if(this.rendered){
52521             
52522             this.tb.items.each(function(item){
52523                 if(item.menu){
52524                     item.menu.removeAll();
52525                     if(item.menu.el){
52526                         item.menu.el.destroy();
52527                     }
52528                 }
52529                 item.destroy();
52530             });
52531              
52532         }
52533     },
52534     onFirstFocus: function() {
52535         this.tb.items.each(function(item){
52536            item.enable();
52537         });
52538     }
52539 };
52540
52541
52542
52543
52544 // <script type="text/javascript">
52545 /*
52546  * Based on
52547  * Ext JS Library 1.1.1
52548  * Copyright(c) 2006-2007, Ext JS, LLC.
52549  *  
52550  
52551  */
52552
52553  
52554 /**
52555  * @class Roo.form.HtmlEditor.ToolbarContext
52556  * Context Toolbar
52557  * 
52558  * Usage:
52559  *
52560  new Roo.form.HtmlEditor({
52561     ....
52562     toolbars : [
52563         { xtype: 'ToolbarStandard', styles : {} }
52564         { xtype: 'ToolbarContext', disable : {} }
52565     ]
52566 })
52567
52568      
52569  * 
52570  * @config : {Object} disable List of elements to disable.. (not done yet.)
52571  * @config : {Object} styles  Map of styles available.
52572  * 
52573  */
52574
52575 Roo.form.HtmlEditor.ToolbarContext = function(config)
52576 {
52577     
52578     Roo.apply(this, config);
52579     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
52580     // dont call parent... till later.
52581     this.styles = this.styles || {};
52582 }
52583
52584  
52585
52586 Roo.form.HtmlEditor.ToolbarContext.types = {
52587     'IMG' : [
52588         {
52589             name : 'width',
52590             title: "Width",
52591             width: 40
52592         },
52593         {
52594             name : 'height',
52595             title: "Height",
52596             width: 40
52597         },
52598         {
52599             name : 'align',
52600             title: "Align",
52601             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
52602             width : 80
52603             
52604         },
52605         {
52606             name : 'border',
52607             title: "Border",
52608             width: 40
52609         },
52610         {
52611             name : 'alt',
52612             title: "Alt",
52613             width: 120
52614         },
52615         {
52616             name : 'src',
52617             title: "Src",
52618             width: 220
52619         }
52620         
52621     ],
52622     
52623     'FIGURE' : [
52624         {
52625             name : 'align',
52626             title: "Align",
52627             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
52628             width : 80  
52629         }
52630     ],
52631     'A' : [
52632         {
52633             name : 'name',
52634             title: "Name",
52635             width: 50
52636         },
52637         {
52638             name : 'target',
52639             title: "Target",
52640             width: 120
52641         },
52642         {
52643             name : 'href',
52644             title: "Href",
52645             width: 220
52646         } // border?
52647         
52648     ],
52649     
52650     'INPUT' : [
52651         {
52652             name : 'name',
52653             title: "name",
52654             width: 120
52655         },
52656         {
52657             name : 'value',
52658             title: "Value",
52659             width: 120
52660         },
52661         {
52662             name : 'width',
52663             title: "Width",
52664             width: 40
52665         }
52666     ],
52667     'LABEL' : [
52668          {
52669             name : 'for',
52670             title: "For",
52671             width: 120
52672         }
52673     ],
52674     'TEXTAREA' : [
52675         {
52676             name : 'name',
52677             title: "name",
52678             width: 120
52679         },
52680         {
52681             name : 'rows',
52682             title: "Rows",
52683             width: 20
52684         },
52685         {
52686             name : 'cols',
52687             title: "Cols",
52688             width: 20
52689         }
52690     ],
52691     'SELECT' : [
52692         {
52693             name : 'name',
52694             title: "name",
52695             width: 120
52696         },
52697         {
52698             name : 'selectoptions',
52699             title: "Options",
52700             width: 200
52701         }
52702     ],
52703     
52704     // should we really allow this??
52705     // should this just be 
52706     'BODY' : [
52707         
52708         {
52709             name : 'title',
52710             title: "Title",
52711             width: 200,
52712             disabled : true
52713         }
52714     ],
52715  
52716     '*' : [
52717         // empty.
52718     ]
52719
52720 };
52721
52722 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
52723 Roo.form.HtmlEditor.ToolbarContext.stores = false;
52724
52725 Roo.form.HtmlEditor.ToolbarContext.options = {
52726         'font-family'  : [ 
52727                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
52728                 [ 'Courier New', 'Courier New'],
52729                 [ 'Tahoma', 'Tahoma'],
52730                 [ 'Times New Roman,serif', 'Times'],
52731                 [ 'Verdana','Verdana' ]
52732         ]
52733 };
52734
52735 // fixme - these need to be configurable..
52736  
52737
52738 //Roo.form.HtmlEditor.ToolbarContext.types
52739
52740
52741 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
52742     
52743     tb: false,
52744     
52745     rendered: false,
52746     
52747     editor : false,
52748     editorcore : false,
52749     /**
52750      * @cfg {Object} disable  List of toolbar elements to disable
52751          
52752      */
52753     disable : false,
52754     /**
52755      * @cfg {Object} styles List of styles 
52756      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
52757      *
52758      * These must be defined in the page, so they get rendered correctly..
52759      * .headline { }
52760      * TD.underline { }
52761      * 
52762      */
52763     styles : false,
52764     
52765     options: false,
52766     
52767     toolbars : false,
52768     
52769     init : function(editor)
52770     {
52771         this.editor = editor;
52772         this.editorcore = editor.editorcore ? editor.editorcore : editor;
52773         var editorcore = this.editorcore;
52774         
52775         var fid = editorcore.frameId;
52776         var etb = this;
52777         function btn(id, toggle, handler){
52778             var xid = fid + '-'+ id ;
52779             return {
52780                 id : xid,
52781                 cmd : id,
52782                 cls : 'x-btn-icon x-edit-'+id,
52783                 enableToggle:toggle !== false,
52784                 scope: editorcore, // was editor...
52785                 handler:handler||editorcore.relayBtnCmd,
52786                 clickEvent:'mousedown',
52787                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52788                 tabIndex:-1
52789             };
52790         }
52791         // create a new element.
52792         var wdiv = editor.wrap.createChild({
52793                 tag: 'div'
52794             }, editor.wrap.dom.firstChild.nextSibling, true);
52795         
52796         // can we do this more than once??
52797         
52798          // stop form submits
52799       
52800  
52801         // disable everything...
52802         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
52803         this.toolbars = {};
52804         // block toolbars are built in updateToolbar when needed.
52805         for (var i in  ty) {
52806             
52807             this.toolbars[i] = this.buildToolbar(ty[i],i);
52808         }
52809         this.tb = this.toolbars.BODY;
52810         this.tb.el.show();
52811         this.buildFooter();
52812         this.footer.show();
52813         editor.on('hide', function( ) { this.footer.hide() }, this);
52814         editor.on('show', function( ) { this.footer.show() }, this);
52815         
52816          
52817         this.rendered = true;
52818         
52819         // the all the btns;
52820         editor.on('editorevent', this.updateToolbar, this);
52821         // other toolbars need to implement this..
52822         //editor.on('editmodechange', this.updateToolbar, this);
52823     },
52824     
52825     
52826     
52827     /**
52828      * Protected method that will not generally be called directly. It triggers
52829      * a toolbar update by reading the markup state of the current selection in the editor.
52830      *
52831      * Note you can force an update by calling on('editorevent', scope, false)
52832      */
52833     updateToolbar: function(editor ,ev, sel)
52834     {
52835         
52836         if (ev) {
52837             ev.stopEvent(); // se if we can stop this looping with mutiple events.
52838         }
52839         
52840         //Roo.log(ev);
52841         // capture mouse up - this is handy for selecting images..
52842         // perhaps should go somewhere else...
52843         if(!this.editorcore.activated){
52844              this.editor.onFirstFocus();
52845             return;
52846         }
52847         //Roo.log(ev ? ev.target : 'NOTARGET');
52848         
52849         
52850         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
52851         // selectNode - might want to handle IE?
52852         
52853         
52854         
52855         if (ev &&
52856             (ev.type == 'mouseup' || ev.type == 'click' ) &&
52857             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
52858             // they have click on an image...
52859             // let's see if we can change the selection...
52860             sel = ev.target;
52861             
52862             // this triggers looping?
52863             //this.editorcore.selectNode(sel);
52864              
52865         }
52866         
52867         // this forces an id..
52868         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
52869              e.classList.remove('roo-ed-selection');
52870         });
52871         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
52872         //Roo.get(node).addClass('roo-ed-selection');
52873       
52874         //var updateFooter = sel ? false : true; 
52875         
52876         
52877         var ans = this.editorcore.getAllAncestors();
52878         
52879         // pick
52880         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
52881         
52882         if (!sel) { 
52883             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
52884             sel = sel ? sel : this.editorcore.doc.body;
52885             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
52886             
52887         }
52888         
52889         var tn = sel.tagName.toUpperCase();
52890         var lastSel = this.tb.selectedNode;
52891         this.tb.selectedNode = sel;
52892         var left_label = tn;
52893         
52894         // ok see if we are editing a block?
52895         
52896         var db = false;
52897         // you are not actually selecting the block.
52898         if (sel && sel.hasAttribute('data-block')) {
52899             db = sel;
52900         } else if (sel && sel.closest('[data-block]')) {
52901             
52902             db = sel.closest('[data-block]');
52903             //var cepar = sel.closest('[contenteditable=true]');
52904             //if (db && cepar && cepar.tagName != 'BODY') {
52905             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
52906             //}   
52907         }
52908         
52909         
52910         var block = false;
52911         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
52912         if (db && this.editorcore.enableBlocks) {
52913             block = Roo.htmleditor.Block.factory(db);
52914             
52915             
52916             if (block) {
52917                  db.className = (
52918                         db.classList.length > 0  ? db.className + ' ' : ''
52919                     )  + 'roo-ed-selection';
52920                  
52921                  // since we removed it earlier... its not there..
52922                 tn = 'BLOCK.' + db.getAttribute('data-block');
52923                 
52924                 //this.editorcore.selectNode(db);
52925                 if (typeof(this.toolbars[tn]) == 'undefined') {
52926                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
52927                 }
52928                 this.toolbars[tn].selectedNode = db;
52929                 left_label = block.friendly_name;
52930                 ans = this.editorcore.getAllAncestors();
52931             }
52932             
52933                 
52934             
52935         }
52936         
52937         
52938         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
52939             return; // no change?
52940         }
52941         
52942         
52943           
52944         this.tb.el.hide();
52945         ///console.log("show: " + tn);
52946         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
52947         
52948         this.tb.el.show();
52949         // update name
52950         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
52951         
52952         
52953         // update attributes
52954         if (block && this.tb.fields) {
52955              
52956             this.tb.fields.each(function(e) {
52957                 e.setValue(block[e.name]);
52958             });
52959             
52960             
52961         } else  if (this.tb.fields && this.tb.selectedNode) {
52962             this.tb.fields.each( function(e) {
52963                 if (e.stylename) {
52964                     e.setValue(this.tb.selectedNode.style[e.stylename]);
52965                     return;
52966                 } 
52967                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
52968             }, this);
52969             this.updateToolbarStyles(this.tb.selectedNode);  
52970         }
52971         
52972         
52973        
52974         Roo.menu.MenuMgr.hideAll();
52975
52976         
52977         
52978     
52979         // update the footer
52980         //
52981         this.updateFooter(ans);
52982              
52983     },
52984     
52985     updateToolbarStyles : function(sel)
52986     {
52987         var hasStyles = false;
52988         for(var i in this.styles) {
52989             hasStyles = true;
52990             break;
52991         }
52992         
52993         // update styles
52994         if (hasStyles && this.tb.hasStyles) { 
52995             var st = this.tb.fields.item(0);
52996             
52997             st.store.removeAll();
52998             var cn = sel.className.split(/\s+/);
52999             
53000             var avs = [];
53001             if (this.styles['*']) {
53002                 
53003                 Roo.each(this.styles['*'], function(v) {
53004                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
53005                 });
53006             }
53007             if (this.styles[tn]) { 
53008                 Roo.each(this.styles[tn], function(v) {
53009                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
53010                 });
53011             }
53012             
53013             st.store.loadData(avs);
53014             st.collapse();
53015             st.setValue(cn);
53016         }
53017     },
53018     
53019      
53020     updateFooter : function(ans)
53021     {
53022         var html = '';
53023         if (ans === false) {
53024             this.footDisp.dom.innerHTML = '';
53025             return;
53026         }
53027         
53028         this.footerEls = ans.reverse();
53029         Roo.each(this.footerEls, function(a,i) {
53030             if (!a) { return; }
53031             html += html.length ? ' &gt; '  :  '';
53032             
53033             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
53034             
53035         });
53036        
53037         // 
53038         var sz = this.footDisp.up('td').getSize();
53039         this.footDisp.dom.style.width = (sz.width -10) + 'px';
53040         this.footDisp.dom.style.marginLeft = '5px';
53041         
53042         this.footDisp.dom.style.overflow = 'hidden';
53043         
53044         this.footDisp.dom.innerHTML = html;
53045             
53046         
53047     },
53048    
53049        
53050     // private
53051     onDestroy : function(){
53052         if(this.rendered){
53053             
53054             this.tb.items.each(function(item){
53055                 if(item.menu){
53056                     item.menu.removeAll();
53057                     if(item.menu.el){
53058                         item.menu.el.destroy();
53059                     }
53060                 }
53061                 item.destroy();
53062             });
53063              
53064         }
53065     },
53066     onFirstFocus: function() {
53067         // need to do this for all the toolbars..
53068         this.tb.items.each(function(item){
53069            item.enable();
53070         });
53071     },
53072     buildToolbar: function(tlist, nm, friendly_name, block)
53073     {
53074         var editor = this.editor;
53075         var editorcore = this.editorcore;
53076          // create a new element.
53077         var wdiv = editor.wrap.createChild({
53078                 tag: 'div'
53079             }, editor.wrap.dom.firstChild.nextSibling, true);
53080         
53081        
53082         var tb = new Roo.Toolbar(wdiv);
53083         ///this.tb = tb; // << this sets the active toolbar..
53084         if (tlist === false && block) {
53085             tlist = block.contextMenu(this);
53086         }
53087         
53088         tb.hasStyles = false;
53089         tb.name = nm;
53090         
53091         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
53092         
53093         var styles = Array.from(this.styles);
53094         
53095         
53096         // styles...
53097         if (styles && styles.length) {
53098             tb.hasStyles = true;
53099             // this needs a multi-select checkbox...
53100             tb.addField( new Roo.form.ComboBox({
53101                 store: new Roo.data.SimpleStore({
53102                     id : 'val',
53103                     fields: ['val', 'selected'],
53104                     data : [] 
53105                 }),
53106                 name : '-roo-edit-className',
53107                 attrname : 'className',
53108                 displayField: 'val',
53109                 typeAhead: false,
53110                 mode: 'local',
53111                 editable : false,
53112                 triggerAction: 'all',
53113                 emptyText:'Select Style',
53114                 selectOnFocus:true,
53115                 width: 130,
53116                 listeners : {
53117                     'select': function(c, r, i) {
53118                         // initial support only for on class per el..
53119                         tb.selectedNode.className =  r ? r.get('val') : '';
53120                         editorcore.syncValue();
53121                     }
53122                 }
53123     
53124             }));
53125         }
53126         
53127         var tbc = Roo.form.HtmlEditor.ToolbarContext;
53128         
53129         
53130         for (var i = 0; i < tlist.length; i++) {
53131             
53132             // newer versions will use xtype cfg to create menus.
53133             if (typeof(tlist[i].xtype) != 'undefined') {
53134                 
53135                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
53136                 
53137                 
53138                 continue;
53139             }
53140             
53141             var item = tlist[i];
53142             tb.add(item.title + ":&nbsp;");
53143             
53144             
53145             //optname == used so you can configure the options available..
53146             var opts = item.opts ? item.opts : false;
53147             if (item.optname) { // use the b
53148                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
53149            
53150             }
53151             
53152             if (opts) {
53153                 // opts == pulldown..
53154                 tb.addField( new Roo.form.ComboBox({
53155                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
53156                         id : 'val',
53157                         fields: ['val', 'display'],
53158                         data : opts  
53159                     }),
53160                     name : '-roo-edit-' + tlist[i].name,
53161                     
53162                     attrname : tlist[i].name,
53163                     stylename : item.style ? item.style : false,
53164                     
53165                     displayField: item.displayField ? item.displayField : 'val',
53166                     valueField :  'val',
53167                     typeAhead: false,
53168                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
53169                     editable : false,
53170                     triggerAction: 'all',
53171                     emptyText:'Select',
53172                     selectOnFocus:true,
53173                     width: item.width ? item.width  : 130,
53174                     listeners : {
53175                         'select': function(c, r, i) {
53176                              
53177                             
53178                             if (c.stylename) {
53179                                 tb.selectedNode.style[c.stylename] =  r.get('val');
53180                                 editorcore.syncValue();
53181                                 return;
53182                             }
53183                             if (r === false) {
53184                                 tb.selectedNode.removeAttribute(c.attrname);
53185                                 editorcore.syncValue();
53186                                 return;
53187                             }
53188                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
53189                             editorcore.syncValue();
53190                         }
53191                     }
53192
53193                 }));
53194                 continue;
53195                     
53196                  
53197                 /*
53198                 tb.addField( new Roo.form.TextField({
53199                     name: i,
53200                     width: 100,
53201                     //allowBlank:false,
53202                     value: ''
53203                 }));
53204                 continue;
53205                 */
53206             }
53207             tb.addField( new Roo.form.TextField({
53208                 name: '-roo-edit-' + tlist[i].name,
53209                 attrname : tlist[i].name,
53210                 
53211                 width: item.width,
53212                 //allowBlank:true,
53213                 value: '',
53214                 listeners: {
53215                     'change' : function(f, nv, ov) {
53216                         
53217                          
53218                         tb.selectedNode.setAttribute(f.attrname, nv);
53219                         editorcore.syncValue();
53220                     }
53221                 }
53222             }));
53223              
53224         }
53225         
53226         var _this = this;
53227         var show_delete = !block || block.deleteTitle !== false;
53228         if(nm == 'BODY'){
53229             show_delete = false;
53230             tb.addSeparator();
53231         
53232             tb.addButton( {
53233                 text: 'Stylesheets',
53234
53235                 listeners : {
53236                     click : function ()
53237                     {
53238                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
53239                     }
53240                 }
53241             });
53242         }
53243         
53244         tb.addFill();
53245         if (show_delete) {
53246             tb.addButton({
53247                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
53248         
53249                 listeners : {
53250                     click : function ()
53251                     {
53252                         var sn = tb.selectedNode;
53253                         if (block) {
53254                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
53255                             
53256                         }
53257                         if (!sn) {
53258                             return;
53259                         }
53260                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
53261                         if (sn.hasAttribute('data-block')) {
53262                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
53263                             sn.parentNode.removeChild(sn);
53264                             
53265                         } else if (sn && sn.tagName != 'BODY') {
53266                             // remove and keep parents.
53267                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
53268                             a.replaceTag(sn);
53269                         }
53270                         
53271                         
53272                         var range = editorcore.createRange();
53273             
53274                         range.setStart(stn,0);
53275                         range.setEnd(stn,0); 
53276                         var selection = editorcore.getSelection();
53277                         selection.removeAllRanges();
53278                         selection.addRange(range);
53279                         
53280                         
53281                         //_this.updateToolbar(null, null, pn);
53282                         _this.updateToolbar(null, null, null);
53283                         _this.updateFooter(false);
53284                         
53285                     }
53286                 }
53287                 
53288                         
53289                     
53290                 
53291             });
53292         }    
53293         
53294         tb.el.on('click', function(e){
53295             e.preventDefault(); // what does this do?
53296         });
53297         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
53298         tb.el.hide();
53299         
53300         // dont need to disable them... as they will get hidden
53301         return tb;
53302          
53303         
53304     },
53305     buildFooter : function()
53306     {
53307         
53308         var fel = this.editor.wrap.createChild();
53309         this.footer = new Roo.Toolbar(fel);
53310         // toolbar has scrolly on left / right?
53311         var footDisp= new Roo.Toolbar.Fill();
53312         var _t = this;
53313         this.footer.add(
53314             {
53315                 text : '&lt;',
53316                 xtype: 'Button',
53317                 handler : function() {
53318                     _t.footDisp.scrollTo('left',0,true)
53319                 }
53320             }
53321         );
53322         this.footer.add( footDisp );
53323         this.footer.add( 
53324             {
53325                 text : '&gt;',
53326                 xtype: 'Button',
53327                 handler : function() {
53328                     // no animation..
53329                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
53330                 }
53331             }
53332         );
53333         var fel = Roo.get(footDisp.el);
53334         fel.addClass('x-editor-context');
53335         this.footDispWrap = fel; 
53336         this.footDispWrap.overflow  = 'hidden';
53337         
53338         this.footDisp = fel.createChild();
53339         this.footDispWrap.on('click', this.onContextClick, this)
53340         
53341         
53342     },
53343     // when the footer contect changes
53344     onContextClick : function (ev,dom)
53345     {
53346         ev.preventDefault();
53347         var  cn = dom.className;
53348         //Roo.log(cn);
53349         if (!cn.match(/x-ed-loc-/)) {
53350             return;
53351         }
53352         var n = cn.split('-').pop();
53353         var ans = this.footerEls;
53354         var sel = ans[n];
53355         
53356         this.editorcore.selectNode(sel);
53357         
53358         
53359         this.updateToolbar(null, null, sel);
53360         
53361         
53362     }
53363     
53364     
53365     
53366     
53367     
53368 });
53369
53370
53371
53372
53373
53374 /*
53375  * Based on:
53376  * Ext JS Library 1.1.1
53377  * Copyright(c) 2006-2007, Ext JS, LLC.
53378  *
53379  * Originally Released Under LGPL - original licence link has changed is not relivant.
53380  *
53381  * Fork - LGPL
53382  * <script type="text/javascript">
53383  */
53384  
53385 /**
53386  * @class Roo.form.BasicForm
53387  * @extends Roo.util.Observable
53388  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
53389  * @constructor
53390  * @param {String/HTMLElement/Roo.Element} el The form element or its id
53391  * @param {Object} config Configuration options
53392  */
53393 Roo.form.BasicForm = function(el, config){
53394     this.allItems = [];
53395     this.childForms = [];
53396     Roo.apply(this, config);
53397     /*
53398      * The Roo.form.Field items in this form.
53399      * @type MixedCollection
53400      */
53401      
53402      
53403     this.items = new Roo.util.MixedCollection(false, function(o){
53404         return o.id || (o.id = Roo.id());
53405     });
53406     this.addEvents({
53407         /**
53408          * @event beforeaction
53409          * Fires before any action is performed. Return false to cancel the action.
53410          * @param {Form} this
53411          * @param {Action} action The action to be performed
53412          */
53413         beforeaction: true,
53414         /**
53415          * @event actionfailed
53416          * Fires when an action fails.
53417          * @param {Form} this
53418          * @param {Action} action The action that failed
53419          */
53420         actionfailed : true,
53421         /**
53422          * @event actioncomplete
53423          * Fires when an action is completed.
53424          * @param {Form} this
53425          * @param {Action} action The action that completed
53426          */
53427         actioncomplete : true
53428     });
53429     if(el){
53430         this.initEl(el);
53431     }
53432     Roo.form.BasicForm.superclass.constructor.call(this);
53433     
53434     Roo.form.BasicForm.popover.apply();
53435 };
53436
53437 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
53438     /**
53439      * @cfg {String} method
53440      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
53441      */
53442     /**
53443      * @cfg {DataReader} reader
53444      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
53445      * This is optional as there is built-in support for processing JSON.
53446      */
53447     /**
53448      * @cfg {DataReader} errorReader
53449      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
53450      * This is completely optional as there is built-in support for processing JSON.
53451      */
53452     /**
53453      * @cfg {String} url
53454      * The URL to use for form actions if one isn't supplied in the action options.
53455      */
53456     /**
53457      * @cfg {Boolean} fileUpload
53458      * Set to true if this form is a file upload.
53459      */
53460      
53461     /**
53462      * @cfg {Object} baseParams
53463      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
53464      */
53465      /**
53466      
53467     /**
53468      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
53469      */
53470     timeout: 30,
53471
53472     // private
53473     activeAction : null,
53474
53475     /**
53476      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
53477      * or setValues() data instead of when the form was first created.
53478      */
53479     trackResetOnLoad : false,
53480     
53481     
53482     /**
53483      * childForms - used for multi-tab forms
53484      * @type {Array}
53485      */
53486     childForms : false,
53487     
53488     /**
53489      * allItems - full list of fields.
53490      * @type {Array}
53491      */
53492     allItems : false,
53493     
53494     /**
53495      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
53496      * element by passing it or its id or mask the form itself by passing in true.
53497      * @type Mixed
53498      */
53499     waitMsgTarget : false,
53500     
53501     /**
53502      * @type Boolean
53503      */
53504     disableMask : false,
53505     
53506     /**
53507      * @cfg {Boolean} errorMask (true|false) default false
53508      */
53509     errorMask : false,
53510     
53511     /**
53512      * @cfg {Number} maskOffset Default 100
53513      */
53514     maskOffset : 100,
53515
53516     // private
53517     initEl : function(el){
53518         this.el = Roo.get(el);
53519         this.id = this.el.id || Roo.id();
53520         this.el.on('submit', this.onSubmit, this);
53521         this.el.addClass('x-form');
53522     },
53523
53524     // private
53525     onSubmit : function(e){
53526         e.stopEvent();
53527     },
53528
53529     /**
53530      * Returns true if client-side validation on the form is successful.
53531      * @return Boolean
53532      */
53533     isValid : function(){
53534         var valid = true;
53535         var target = false;
53536         this.items.each(function(f){
53537             if(f.validate()){
53538                 return;
53539             }
53540             
53541             valid = false;
53542                 
53543             if(!target && f.el.isVisible(true)){
53544                 target = f;
53545             }
53546         });
53547         
53548         if(this.errorMask && !valid){
53549             Roo.form.BasicForm.popover.mask(this, target);
53550         }
53551         
53552         return valid;
53553     },
53554     /**
53555      * Returns array of invalid form fields.
53556      * @return Array
53557      */
53558     
53559     invalidFields : function()
53560     {
53561         var ret = [];
53562         this.items.each(function(f){
53563             if(f.validate()){
53564                 return;
53565             }
53566             ret.push(f);
53567             
53568         });
53569         
53570         return ret;
53571     },
53572     
53573     
53574     /**
53575      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
53576      * @return Boolean
53577      */
53578     isDirty : function(){
53579         var dirty = false;
53580         this.items.each(function(f){
53581            if(f.isDirty()){
53582                dirty = true;
53583                return false;
53584            }
53585         });
53586         return dirty;
53587     },
53588     
53589     /**
53590      * Returns true if any fields in this form have changed since their original load. (New version)
53591      * @return Boolean
53592      */
53593     
53594     hasChanged : function()
53595     {
53596         var dirty = false;
53597         this.items.each(function(f){
53598            if(f.hasChanged()){
53599                dirty = true;
53600                return false;
53601            }
53602         });
53603         return dirty;
53604         
53605     },
53606     /**
53607      * Resets all hasChanged to 'false' -
53608      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
53609      * So hasChanged storage is only to be used for this purpose
53610      * @return Boolean
53611      */
53612     resetHasChanged : function()
53613     {
53614         this.items.each(function(f){
53615            f.resetHasChanged();
53616         });
53617         
53618     },
53619     
53620     
53621     /**
53622      * Performs a predefined action (submit or load) or custom actions you define on this form.
53623      * @param {String} actionName The name of the action type
53624      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
53625      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
53626      * accept other config options):
53627      * <pre>
53628 Property          Type             Description
53629 ----------------  ---------------  ----------------------------------------------------------------------------------
53630 url               String           The url for the action (defaults to the form's url)
53631 method            String           The form method to use (defaults to the form's method, or POST if not defined)
53632 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
53633 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
53634                                    validate the form on the client (defaults to false)
53635      * </pre>
53636      * @return {BasicForm} this
53637      */
53638     doAction : function(action, options){
53639         if(typeof action == 'string'){
53640             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
53641         }
53642         if(this.fireEvent('beforeaction', this, action) !== false){
53643             this.beforeAction(action);
53644             action.run.defer(100, action);
53645         }
53646         return this;
53647     },
53648
53649     /**
53650      * Shortcut to do a submit action.
53651      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
53652      * @return {BasicForm} this
53653      */
53654     submit : function(options){
53655         this.doAction('submit', options);
53656         return this;
53657     },
53658
53659     /**
53660      * Shortcut to do a load action.
53661      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
53662      * @return {BasicForm} this
53663      */
53664     load : function(options){
53665         this.doAction('load', options);
53666         return this;
53667     },
53668
53669     /**
53670      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
53671      * @param {Record} record The record to edit
53672      * @return {BasicForm} this
53673      */
53674     updateRecord : function(record){
53675         record.beginEdit();
53676         var fs = record.fields;
53677         fs.each(function(f){
53678             var field = this.findField(f.name);
53679             if(field){
53680                 record.set(f.name, field.getValue());
53681             }
53682         }, this);
53683         record.endEdit();
53684         return this;
53685     },
53686
53687     /**
53688      * Loads an Roo.data.Record into this form.
53689      * @param {Record} record The record to load
53690      * @return {BasicForm} this
53691      */
53692     loadRecord : function(record){
53693         this.setValues(record.data);
53694         return this;
53695     },
53696
53697     // private
53698     beforeAction : function(action){
53699         var o = action.options;
53700         
53701         if(!this.disableMask) {
53702             if(this.waitMsgTarget === true){
53703                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
53704             }else if(this.waitMsgTarget){
53705                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
53706                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
53707             }else {
53708                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
53709             }
53710         }
53711         
53712          
53713     },
53714
53715     // private
53716     afterAction : function(action, success){
53717         this.activeAction = null;
53718         var o = action.options;
53719         
53720         if(!this.disableMask) {
53721             if(this.waitMsgTarget === true){
53722                 this.el.unmask();
53723             }else if(this.waitMsgTarget){
53724                 this.waitMsgTarget.unmask();
53725             }else{
53726                 Roo.MessageBox.updateProgress(1);
53727                 Roo.MessageBox.hide();
53728             }
53729         }
53730         
53731         if(success){
53732             if(o.reset){
53733                 this.reset();
53734             }
53735             Roo.callback(o.success, o.scope, [this, action]);
53736             this.fireEvent('actioncomplete', this, action);
53737             
53738         }else{
53739             
53740             // failure condition..
53741             // we have a scenario where updates need confirming.
53742             // eg. if a locking scenario exists..
53743             // we look for { errors : { needs_confirm : true }} in the response.
53744             if (
53745                 (typeof(action.result) != 'undefined')  &&
53746                 (typeof(action.result.errors) != 'undefined')  &&
53747                 (typeof(action.result.errors.needs_confirm) != 'undefined')
53748            ){
53749                 var _t = this;
53750                 Roo.MessageBox.confirm(
53751                     "Change requires confirmation",
53752                     action.result.errorMsg,
53753                     function(r) {
53754                         if (r != 'yes') {
53755                             return;
53756                         }
53757                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
53758                     }
53759                     
53760                 );
53761                 
53762                 
53763                 
53764                 return;
53765             }
53766             
53767             Roo.callback(o.failure, o.scope, [this, action]);
53768             // show an error message if no failed handler is set..
53769             if (!this.hasListener('actionfailed')) {
53770                 Roo.MessageBox.alert("Error",
53771                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
53772                         action.result.errorMsg :
53773                         "Saving Failed, please check your entries or try again"
53774                 );
53775             }
53776             
53777             this.fireEvent('actionfailed', this, action);
53778         }
53779         
53780     },
53781
53782     /**
53783      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
53784      * @param {String} id The value to search for
53785      * @return Field
53786      */
53787     findField : function(id){
53788         var field = this.items.get(id);
53789         if(!field){
53790             this.items.each(function(f){
53791                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
53792                     field = f;
53793                     return false;
53794                 }
53795             });
53796         }
53797         return field || null;
53798     },
53799
53800     /**
53801      * Add a secondary form to this one, 
53802      * Used to provide tabbed forms. One form is primary, with hidden values 
53803      * which mirror the elements from the other forms.
53804      * 
53805      * @param {Roo.form.Form} form to add.
53806      * 
53807      */
53808     addForm : function(form)
53809     {
53810        
53811         if (this.childForms.indexOf(form) > -1) {
53812             // already added..
53813             return;
53814         }
53815         this.childForms.push(form);
53816         var n = '';
53817         Roo.each(form.allItems, function (fe) {
53818             
53819             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
53820             if (this.findField(n)) { // already added..
53821                 return;
53822             }
53823             var add = new Roo.form.Hidden({
53824                 name : n
53825             });
53826             add.render(this.el);
53827             
53828             this.add( add );
53829         }, this);
53830         
53831     },
53832     /**
53833      * Mark fields in this form invalid in bulk.
53834      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
53835      * @return {BasicForm} this
53836      */
53837     markInvalid : function(errors){
53838         if(errors instanceof Array){
53839             for(var i = 0, len = errors.length; i < len; i++){
53840                 var fieldError = errors[i];
53841                 var f = this.findField(fieldError.id);
53842                 if(f){
53843                     f.markInvalid(fieldError.msg);
53844                 }
53845             }
53846         }else{
53847             var field, id;
53848             for(id in errors){
53849                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
53850                     field.markInvalid(errors[id]);
53851                 }
53852             }
53853         }
53854         Roo.each(this.childForms || [], function (f) {
53855             f.markInvalid(errors);
53856         });
53857         
53858         return this;
53859     },
53860
53861     /**
53862      * Set values for fields in this form in bulk.
53863      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
53864      * @return {BasicForm} this
53865      */
53866     setValues : function(values){
53867         if(values instanceof Array){ // array of objects
53868             for(var i = 0, len = values.length; i < len; i++){
53869                 var v = values[i];
53870                 var f = this.findField(v.id);
53871                 if(f){
53872                     f.setValue(v.value);
53873                     if(this.trackResetOnLoad){
53874                         f.originalValue = f.getValue();
53875                     }
53876                 }
53877             }
53878         }else{ // object hash
53879             var field, id;
53880             for(id in values){
53881                 if(typeof values[id] != 'function' && (field = this.findField(id))){
53882                     
53883                     if (field.setFromData && 
53884                         field.valueField && 
53885                         field.displayField &&
53886                         // combos' with local stores can 
53887                         // be queried via setValue()
53888                         // to set their value..
53889                         (field.store && !field.store.isLocal)
53890                         ) {
53891                         // it's a combo
53892                         var sd = { };
53893                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
53894                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
53895                         field.setFromData(sd);
53896                         
53897                     } else {
53898                         field.setValue(values[id]);
53899                     }
53900                     
53901                     
53902                     if(this.trackResetOnLoad){
53903                         field.originalValue = field.getValue();
53904                     }
53905                 }
53906             }
53907         }
53908         this.resetHasChanged();
53909         
53910         
53911         Roo.each(this.childForms || [], function (f) {
53912             f.setValues(values);
53913             f.resetHasChanged();
53914         });
53915                 
53916         return this;
53917     },
53918  
53919     /**
53920      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
53921      * they are returned as an array.
53922      * @param {Boolean} asString
53923      * @return {Object}
53924      */
53925     getValues : function(asString)
53926     {
53927         if (this.childForms) {
53928             // copy values from the child forms
53929             Roo.each(this.childForms, function (f) {
53930                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
53931             }, this);
53932         }
53933         
53934         // use formdata
53935         if (typeof(FormData) != 'undefined' && asString !== true) {
53936             // this relies on a 'recent' version of chrome apparently...
53937             try {
53938                 var fd = (new FormData(this.el.dom)).entries();
53939                 var ret = {};
53940                 var ent = fd.next();
53941                 while (!ent.done) {
53942                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
53943                     ent = fd.next();
53944                 };
53945                 return ret;
53946             } catch(e) {
53947                 
53948             }
53949             
53950         }
53951         
53952         
53953         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
53954         if(asString === true){
53955             return fs;
53956         }
53957         return Roo.urlDecode(fs);
53958     },
53959     
53960     /**
53961      * Returns the fields in this form as an object with key/value pairs. 
53962      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
53963      * Normally this will not return readOnly data 
53964      * @param {Boolean} with_readonly return readonly field data.
53965      * @return {Object}
53966      */
53967     getFieldValues : function(with_readonly)
53968     {
53969         if (this.childForms) {
53970             // copy values from the child forms
53971             // should this call getFieldValues - probably not as we do not currently copy
53972             // hidden fields when we generate..
53973             Roo.each(this.childForms, function (f) {
53974                 this.setValues(f.getFieldValues());
53975             }, this);
53976         }
53977         
53978         var ret = {};
53979         this.items.each(function(f){
53980             
53981             if (f.readOnly && with_readonly !== true) {
53982                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
53983                         // if a subform contains a copy of them.
53984                         // if you have subforms with the same editable data, you will need to copy the data back
53985                         // and forth.
53986             }
53987             
53988             if (!f.getName()) {
53989                 return;
53990             }
53991             var v = f.getValue();
53992             if (f.inputType =='radio') {
53993                 if (typeof(ret[f.getName()]) == 'undefined') {
53994                     ret[f.getName()] = ''; // empty..
53995                 }
53996                 
53997                 if (!f.el.dom.checked) {
53998                     return;
53999                     
54000                 }
54001                 v = f.el.dom.value;
54002                 
54003             }
54004             
54005             // not sure if this supported any more..
54006             if ((typeof(v) == 'object') && f.getRawValue) {
54007                 v = f.getRawValue() ; // dates..
54008             }
54009             // combo boxes where name != hiddenName...
54010             if (f.name != f.getName()) {
54011                 ret[f.name] = f.getRawValue();
54012             }
54013             ret[f.getName()] = v;
54014         });
54015         
54016         return ret;
54017     },
54018
54019     /**
54020      * Clears all invalid messages in this form.
54021      * @return {BasicForm} this
54022      */
54023     clearInvalid : function(){
54024         this.items.each(function(f){
54025            f.clearInvalid();
54026         });
54027         
54028         Roo.each(this.childForms || [], function (f) {
54029             f.clearInvalid();
54030         });
54031         
54032         
54033         return this;
54034     },
54035
54036     /**
54037      * Resets this form.
54038      * @return {BasicForm} this
54039      */
54040     reset : function(){
54041         this.items.each(function(f){
54042             f.reset();
54043         });
54044         
54045         Roo.each(this.childForms || [], function (f) {
54046             f.reset();
54047         });
54048         this.resetHasChanged();
54049         
54050         return this;
54051     },
54052
54053     /**
54054      * Add Roo.form components to this form.
54055      * @param {Field} field1
54056      * @param {Field} field2 (optional)
54057      * @param {Field} etc (optional)
54058      * @return {BasicForm} this
54059      */
54060     add : function(){
54061         this.items.addAll(Array.prototype.slice.call(arguments, 0));
54062         return this;
54063     },
54064
54065
54066     /**
54067      * Removes a field from the items collection (does NOT remove its markup).
54068      * @param {Field} field
54069      * @return {BasicForm} this
54070      */
54071     remove : function(field){
54072         this.items.remove(field);
54073         return this;
54074     },
54075
54076     /**
54077      * Looks at the fields in this form, checks them for an id attribute,
54078      * and calls applyTo on the existing dom element with that id.
54079      * @return {BasicForm} this
54080      */
54081     render : function(){
54082         this.items.each(function(f){
54083             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
54084                 f.applyTo(f.id);
54085             }
54086         });
54087         return this;
54088     },
54089
54090     /**
54091      * Calls {@link Ext#apply} for all fields in this form with the passed object.
54092      * @param {Object} values
54093      * @return {BasicForm} this
54094      */
54095     applyToFields : function(o){
54096         this.items.each(function(f){
54097            Roo.apply(f, o);
54098         });
54099         return this;
54100     },
54101
54102     /**
54103      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
54104      * @param {Object} values
54105      * @return {BasicForm} this
54106      */
54107     applyIfToFields : function(o){
54108         this.items.each(function(f){
54109            Roo.applyIf(f, o);
54110         });
54111         return this;
54112     }
54113 });
54114
54115 // back compat
54116 Roo.BasicForm = Roo.form.BasicForm;
54117
54118 Roo.apply(Roo.form.BasicForm, {
54119     
54120     popover : {
54121         
54122         padding : 5,
54123         
54124         isApplied : false,
54125         
54126         isMasked : false,
54127         
54128         form : false,
54129         
54130         target : false,
54131         
54132         intervalID : false,
54133         
54134         maskEl : false,
54135         
54136         apply : function()
54137         {
54138             if(this.isApplied){
54139                 return;
54140             }
54141             
54142             this.maskEl = {
54143                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
54144                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
54145                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
54146                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
54147             };
54148             
54149             this.maskEl.top.enableDisplayMode("block");
54150             this.maskEl.left.enableDisplayMode("block");
54151             this.maskEl.bottom.enableDisplayMode("block");
54152             this.maskEl.right.enableDisplayMode("block");
54153             
54154             Roo.get(document.body).on('click', function(){
54155                 this.unmask();
54156             }, this);
54157             
54158             Roo.get(document.body).on('touchstart', function(){
54159                 this.unmask();
54160             }, this);
54161             
54162             this.isApplied = true
54163         },
54164         
54165         mask : function(form, target)
54166         {
54167             this.form = form;
54168             
54169             this.target = target;
54170             
54171             if(!this.form.errorMask || !target.el){
54172                 return;
54173             }
54174             
54175             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
54176             
54177             var ot = this.target.el.calcOffsetsTo(scrollable);
54178             
54179             var scrollTo = ot[1] - this.form.maskOffset;
54180             
54181             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
54182             
54183             scrollable.scrollTo('top', scrollTo);
54184             
54185             var el = this.target.wrap || this.target.el;
54186             
54187             var box = el.getBox();
54188             
54189             this.maskEl.top.setStyle('position', 'absolute');
54190             this.maskEl.top.setStyle('z-index', 10000);
54191             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
54192             this.maskEl.top.setLeft(0);
54193             this.maskEl.top.setTop(0);
54194             this.maskEl.top.show();
54195             
54196             this.maskEl.left.setStyle('position', 'absolute');
54197             this.maskEl.left.setStyle('z-index', 10000);
54198             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
54199             this.maskEl.left.setLeft(0);
54200             this.maskEl.left.setTop(box.y - this.padding);
54201             this.maskEl.left.show();
54202
54203             this.maskEl.bottom.setStyle('position', 'absolute');
54204             this.maskEl.bottom.setStyle('z-index', 10000);
54205             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
54206             this.maskEl.bottom.setLeft(0);
54207             this.maskEl.bottom.setTop(box.bottom + this.padding);
54208             this.maskEl.bottom.show();
54209
54210             this.maskEl.right.setStyle('position', 'absolute');
54211             this.maskEl.right.setStyle('z-index', 10000);
54212             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
54213             this.maskEl.right.setLeft(box.right + this.padding);
54214             this.maskEl.right.setTop(box.y - this.padding);
54215             this.maskEl.right.show();
54216
54217             this.intervalID = window.setInterval(function() {
54218                 Roo.form.BasicForm.popover.unmask();
54219             }, 10000);
54220
54221             window.onwheel = function(){ return false;};
54222             
54223             (function(){ this.isMasked = true; }).defer(500, this);
54224             
54225         },
54226         
54227         unmask : function()
54228         {
54229             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
54230                 return;
54231             }
54232             
54233             this.maskEl.top.setStyle('position', 'absolute');
54234             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
54235             this.maskEl.top.hide();
54236
54237             this.maskEl.left.setStyle('position', 'absolute');
54238             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
54239             this.maskEl.left.hide();
54240
54241             this.maskEl.bottom.setStyle('position', 'absolute');
54242             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
54243             this.maskEl.bottom.hide();
54244
54245             this.maskEl.right.setStyle('position', 'absolute');
54246             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
54247             this.maskEl.right.hide();
54248             
54249             window.onwheel = function(){ return true;};
54250             
54251             if(this.intervalID){
54252                 window.clearInterval(this.intervalID);
54253                 this.intervalID = false;
54254             }
54255             
54256             this.isMasked = false;
54257             
54258         }
54259         
54260     }
54261     
54262 });/*
54263  * Based on:
54264  * Ext JS Library 1.1.1
54265  * Copyright(c) 2006-2007, Ext JS, LLC.
54266  *
54267  * Originally Released Under LGPL - original licence link has changed is not relivant.
54268  *
54269  * Fork - LGPL
54270  * <script type="text/javascript">
54271  */
54272
54273 /**
54274  * @class Roo.form.Form
54275  * @extends Roo.form.BasicForm
54276  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
54277  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
54278  * @constructor
54279  * @param {Object} config Configuration options
54280  */
54281 Roo.form.Form = function(config){
54282     var xitems =  [];
54283     if (config.items) {
54284         xitems = config.items;
54285         delete config.items;
54286     }
54287    
54288     
54289     Roo.form.Form.superclass.constructor.call(this, null, config);
54290     this.url = this.url || this.action;
54291     if(!this.root){
54292         this.root = new Roo.form.Layout(Roo.applyIf({
54293             id: Roo.id()
54294         }, config));
54295     }
54296     this.active = this.root;
54297     /**
54298      * Array of all the buttons that have been added to this form via {@link addButton}
54299      * @type Array
54300      */
54301     this.buttons = [];
54302     this.allItems = [];
54303     this.addEvents({
54304         /**
54305          * @event clientvalidation
54306          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
54307          * @param {Form} this
54308          * @param {Boolean} valid true if the form has passed client-side validation
54309          */
54310         clientvalidation: true,
54311         /**
54312          * @event rendered
54313          * Fires when the form is rendered
54314          * @param {Roo.form.Form} form
54315          */
54316         rendered : true
54317     });
54318     
54319     if (this.progressUrl) {
54320             // push a hidden field onto the list of fields..
54321             this.addxtype( {
54322                     xns: Roo.form, 
54323                     xtype : 'Hidden', 
54324                     name : 'UPLOAD_IDENTIFIER' 
54325             });
54326         }
54327         
54328     
54329     Roo.each(xitems, this.addxtype, this);
54330     
54331 };
54332
54333 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
54334      /**
54335      * @cfg {Roo.Button} buttons[] buttons at bottom of form
54336      */
54337     
54338     /**
54339      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
54340      */
54341     /**
54342      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
54343      */
54344     /**
54345      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
54346      */
54347     buttonAlign:'center',
54348
54349     /**
54350      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
54351      */
54352     minButtonWidth:75,
54353
54354     /**
54355      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
54356      * This property cascades to child containers if not set.
54357      */
54358     labelAlign:'left',
54359
54360     /**
54361      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
54362      * fires a looping event with that state. This is required to bind buttons to the valid
54363      * state using the config value formBind:true on the button.
54364      */
54365     monitorValid : false,
54366
54367     /**
54368      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
54369      */
54370     monitorPoll : 200,
54371     
54372     /**
54373      * @cfg {String} progressUrl - Url to return progress data 
54374      */
54375     
54376     progressUrl : false,
54377     /**
54378      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
54379      * sending a formdata with extra parameters - eg uploaded elements.
54380      */
54381     
54382     formData : false,
54383     
54384     /**
54385      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
54386      * fields are added and the column is closed. If no fields are passed the column remains open
54387      * until end() is called.
54388      * @param {Object} config The config to pass to the column
54389      * @param {Field} field1 (optional)
54390      * @param {Field} field2 (optional)
54391      * @param {Field} etc (optional)
54392      * @return Column The column container object
54393      */
54394     column : function(c){
54395         var col = new Roo.form.Column(c);
54396         this.start(col);
54397         if(arguments.length > 1){ // duplicate code required because of Opera
54398             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54399             this.end();
54400         }
54401         return col;
54402     },
54403
54404     /**
54405      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
54406      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
54407      * until end() is called.
54408      * @param {Object} config The config to pass to the fieldset
54409      * @param {Field} field1 (optional)
54410      * @param {Field} field2 (optional)
54411      * @param {Field} etc (optional)
54412      * @return FieldSet The fieldset container object
54413      */
54414     fieldset : function(c){
54415         var fs = new Roo.form.FieldSet(c);
54416         this.start(fs);
54417         if(arguments.length > 1){ // duplicate code required because of Opera
54418             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54419             this.end();
54420         }
54421         return fs;
54422     },
54423
54424     /**
54425      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
54426      * fields are added and the container is closed. If no fields are passed the container remains open
54427      * until end() is called.
54428      * @param {Object} config The config to pass to the Layout
54429      * @param {Field} field1 (optional)
54430      * @param {Field} field2 (optional)
54431      * @param {Field} etc (optional)
54432      * @return Layout The container object
54433      */
54434     container : function(c){
54435         var l = new Roo.form.Layout(c);
54436         this.start(l);
54437         if(arguments.length > 1){ // duplicate code required because of Opera
54438             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54439             this.end();
54440         }
54441         return l;
54442     },
54443
54444     /**
54445      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
54446      * @param {Object} container A Roo.form.Layout or subclass of Layout
54447      * @return {Form} this
54448      */
54449     start : function(c){
54450         // cascade label info
54451         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
54452         this.active.stack.push(c);
54453         c.ownerCt = this.active;
54454         this.active = c;
54455         return this;
54456     },
54457
54458     /**
54459      * Closes the current open container
54460      * @return {Form} this
54461      */
54462     end : function(){
54463         if(this.active == this.root){
54464             return this;
54465         }
54466         this.active = this.active.ownerCt;
54467         return this;
54468     },
54469
54470     /**
54471      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
54472      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
54473      * as the label of the field.
54474      * @param {Field} field1
54475      * @param {Field} field2 (optional)
54476      * @param {Field} etc. (optional)
54477      * @return {Form} this
54478      */
54479     add : function(){
54480         this.active.stack.push.apply(this.active.stack, arguments);
54481         this.allItems.push.apply(this.allItems,arguments);
54482         var r = [];
54483         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
54484             if(a[i].isFormField){
54485                 r.push(a[i]);
54486             }
54487         }
54488         if(r.length > 0){
54489             Roo.form.Form.superclass.add.apply(this, r);
54490         }
54491         return this;
54492     },
54493     
54494
54495     
54496     
54497     
54498      /**
54499      * Find any element that has been added to a form, using it's ID or name
54500      * This can include framesets, columns etc. along with regular fields..
54501      * @param {String} id - id or name to find.
54502      
54503      * @return {Element} e - or false if nothing found.
54504      */
54505     findbyId : function(id)
54506     {
54507         var ret = false;
54508         if (!id) {
54509             return ret;
54510         }
54511         Roo.each(this.allItems, function(f){
54512             if (f.id == id || f.name == id ){
54513                 ret = f;
54514                 return false;
54515             }
54516         });
54517         return ret;
54518     },
54519
54520     
54521     
54522     /**
54523      * Render this form into the passed container. This should only be called once!
54524      * @param {String/HTMLElement/Element} container The element this component should be rendered into
54525      * @return {Form} this
54526      */
54527     render : function(ct)
54528     {
54529         
54530         
54531         
54532         ct = Roo.get(ct);
54533         var o = this.autoCreate || {
54534             tag: 'form',
54535             method : this.method || 'POST',
54536             id : this.id || Roo.id()
54537         };
54538         this.initEl(ct.createChild(o));
54539
54540         this.root.render(this.el);
54541         
54542        
54543              
54544         this.items.each(function(f){
54545             f.render('x-form-el-'+f.id);
54546         });
54547
54548         if(this.buttons.length > 0){
54549             // tables are required to maintain order and for correct IE layout
54550             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
54551                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
54552                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
54553             }}, null, true);
54554             var tr = tb.getElementsByTagName('tr')[0];
54555             for(var i = 0, len = this.buttons.length; i < len; i++) {
54556                 var b = this.buttons[i];
54557                 var td = document.createElement('td');
54558                 td.className = 'x-form-btn-td';
54559                 b.render(tr.appendChild(td));
54560             }
54561         }
54562         if(this.monitorValid){ // initialize after render
54563             this.startMonitoring();
54564         }
54565         this.fireEvent('rendered', this);
54566         return this;
54567     },
54568
54569     /**
54570      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
54571      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
54572      * object or a valid Roo.DomHelper element config
54573      * @param {Function} handler The function called when the button is clicked
54574      * @param {Object} scope (optional) The scope of the handler function
54575      * @return {Roo.Button}
54576      */
54577     addButton : function(config, handler, scope){
54578         var bc = {
54579             handler: handler,
54580             scope: scope,
54581             minWidth: this.minButtonWidth,
54582             hideParent:true
54583         };
54584         if(typeof config == "string"){
54585             bc.text = config;
54586         }else{
54587             Roo.apply(bc, config);
54588         }
54589         var btn = new Roo.Button(null, bc);
54590         this.buttons.push(btn);
54591         return btn;
54592     },
54593
54594      /**
54595      * Adds a series of form elements (using the xtype property as the factory method.
54596      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
54597      * @param {Object} config 
54598      */
54599     
54600     addxtype : function()
54601     {
54602         var ar = Array.prototype.slice.call(arguments, 0);
54603         var ret = false;
54604         for(var i = 0; i < ar.length; i++) {
54605             if (!ar[i]) {
54606                 continue; // skip -- if this happends something invalid got sent, we 
54607                 // should ignore it, as basically that interface element will not show up
54608                 // and that should be pretty obvious!!
54609             }
54610             
54611             if (Roo.form[ar[i].xtype]) {
54612                 ar[i].form = this;
54613                 var fe = Roo.factory(ar[i], Roo.form);
54614                 if (!ret) {
54615                     ret = fe;
54616                 }
54617                 fe.form = this;
54618                 if (fe.store) {
54619                     fe.store.form = this;
54620                 }
54621                 if (fe.isLayout) {  
54622                          
54623                     this.start(fe);
54624                     this.allItems.push(fe);
54625                     if (fe.items && fe.addxtype) {
54626                         fe.addxtype.apply(fe, fe.items);
54627                         delete fe.items;
54628                     }
54629                      this.end();
54630                     continue;
54631                 }
54632                 
54633                 
54634                  
54635                 this.add(fe);
54636               //  console.log('adding ' + ar[i].xtype);
54637             }
54638             if (ar[i].xtype == 'Button') {  
54639                 //console.log('adding button');
54640                 //console.log(ar[i]);
54641                 this.addButton(ar[i]);
54642                 this.allItems.push(fe);
54643                 continue;
54644             }
54645             
54646             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
54647                 alert('end is not supported on xtype any more, use items');
54648             //    this.end();
54649             //    //console.log('adding end');
54650             }
54651             
54652         }
54653         return ret;
54654     },
54655     
54656     /**
54657      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
54658      * option "monitorValid"
54659      */
54660     startMonitoring : function(){
54661         if(!this.bound){
54662             this.bound = true;
54663             Roo.TaskMgr.start({
54664                 run : this.bindHandler,
54665                 interval : this.monitorPoll || 200,
54666                 scope: this
54667             });
54668         }
54669     },
54670
54671     /**
54672      * Stops monitoring of the valid state of this form
54673      */
54674     stopMonitoring : function(){
54675         this.bound = false;
54676     },
54677
54678     // private
54679     bindHandler : function(){
54680         if(!this.bound){
54681             return false; // stops binding
54682         }
54683         var valid = true;
54684         this.items.each(function(f){
54685             if(!f.isValid(true)){
54686                 valid = false;
54687                 return false;
54688             }
54689         });
54690         for(var i = 0, len = this.buttons.length; i < len; i++){
54691             var btn = this.buttons[i];
54692             if(btn.formBind === true && btn.disabled === valid){
54693                 btn.setDisabled(!valid);
54694             }
54695         }
54696         this.fireEvent('clientvalidation', this, valid);
54697     }
54698     
54699     
54700     
54701     
54702     
54703     
54704     
54705     
54706 });
54707
54708
54709 // back compat
54710 Roo.Form = Roo.form.Form;
54711 /*
54712  * Based on:
54713  * Ext JS Library 1.1.1
54714  * Copyright(c) 2006-2007, Ext JS, LLC.
54715  *
54716  * Originally Released Under LGPL - original licence link has changed is not relivant.
54717  *
54718  * Fork - LGPL
54719  * <script type="text/javascript">
54720  */
54721
54722 // as we use this in bootstrap.
54723 Roo.namespace('Roo.form');
54724  /**
54725  * @class Roo.form.Action
54726  * Internal Class used to handle form actions
54727  * @constructor
54728  * @param {Roo.form.BasicForm} el The form element or its id
54729  * @param {Object} config Configuration options
54730  */
54731
54732  
54733  
54734 // define the action interface
54735 Roo.form.Action = function(form, options){
54736     this.form = form;
54737     this.options = options || {};
54738 };
54739 /**
54740  * Client Validation Failed
54741  * @const 
54742  */
54743 Roo.form.Action.CLIENT_INVALID = 'client';
54744 /**
54745  * Server Validation Failed
54746  * @const 
54747  */
54748 Roo.form.Action.SERVER_INVALID = 'server';
54749  /**
54750  * Connect to Server Failed
54751  * @const 
54752  */
54753 Roo.form.Action.CONNECT_FAILURE = 'connect';
54754 /**
54755  * Reading Data from Server Failed
54756  * @const 
54757  */
54758 Roo.form.Action.LOAD_FAILURE = 'load';
54759
54760 Roo.form.Action.prototype = {
54761     type : 'default',
54762     failureType : undefined,
54763     response : undefined,
54764     result : undefined,
54765
54766     // interface method
54767     run : function(options){
54768
54769     },
54770
54771     // interface method
54772     success : function(response){
54773
54774     },
54775
54776     // interface method
54777     handleResponse : function(response){
54778
54779     },
54780
54781     // default connection failure
54782     failure : function(response){
54783         
54784         this.response = response;
54785         this.failureType = Roo.form.Action.CONNECT_FAILURE;
54786         this.form.afterAction(this, false);
54787     },
54788
54789     processResponse : function(response){
54790         this.response = response;
54791         if(!response.responseText){
54792             return true;
54793         }
54794         this.result = this.handleResponse(response);
54795         return this.result;
54796     },
54797
54798     // utility functions used internally
54799     getUrl : function(appendParams){
54800         var url = this.options.url || this.form.url || this.form.el.dom.action;
54801         if(appendParams){
54802             var p = this.getParams();
54803             if(p){
54804                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
54805             }
54806         }
54807         return url;
54808     },
54809
54810     getMethod : function(){
54811         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
54812     },
54813
54814     getParams : function(){
54815         var bp = this.form.baseParams;
54816         var p = this.options.params;
54817         if(p){
54818             if(typeof p == "object"){
54819                 p = Roo.urlEncode(Roo.applyIf(p, bp));
54820             }else if(typeof p == 'string' && bp){
54821                 p += '&' + Roo.urlEncode(bp);
54822             }
54823         }else if(bp){
54824             p = Roo.urlEncode(bp);
54825         }
54826         return p;
54827     },
54828
54829     createCallback : function(){
54830         return {
54831             success: this.success,
54832             failure: this.failure,
54833             scope: this,
54834             timeout: (this.form.timeout*1000),
54835             upload: this.form.fileUpload ? this.success : undefined
54836         };
54837     }
54838 };
54839
54840 Roo.form.Action.Submit = function(form, options){
54841     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
54842 };
54843
54844 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
54845     type : 'submit',
54846
54847     haveProgress : false,
54848     uploadComplete : false,
54849     
54850     // uploadProgress indicator.
54851     uploadProgress : function()
54852     {
54853         if (!this.form.progressUrl) {
54854             return;
54855         }
54856         
54857         if (!this.haveProgress) {
54858             Roo.MessageBox.progress("Uploading", "Uploading");
54859         }
54860         if (this.uploadComplete) {
54861            Roo.MessageBox.hide();
54862            return;
54863         }
54864         
54865         this.haveProgress = true;
54866    
54867         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
54868         
54869         var c = new Roo.data.Connection();
54870         c.request({
54871             url : this.form.progressUrl,
54872             params: {
54873                 id : uid
54874             },
54875             method: 'GET',
54876             success : function(req){
54877                //console.log(data);
54878                 var rdata = false;
54879                 var edata;
54880                 try  {
54881                    rdata = Roo.decode(req.responseText)
54882                 } catch (e) {
54883                     Roo.log("Invalid data from server..");
54884                     Roo.log(edata);
54885                     return;
54886                 }
54887                 if (!rdata || !rdata.success) {
54888                     Roo.log(rdata);
54889                     Roo.MessageBox.alert(Roo.encode(rdata));
54890                     return;
54891                 }
54892                 var data = rdata.data;
54893                 
54894                 if (this.uploadComplete) {
54895                    Roo.MessageBox.hide();
54896                    return;
54897                 }
54898                    
54899                 if (data){
54900                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
54901                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
54902                     );
54903                 }
54904                 this.uploadProgress.defer(2000,this);
54905             },
54906        
54907             failure: function(data) {
54908                 Roo.log('progress url failed ');
54909                 Roo.log(data);
54910             },
54911             scope : this
54912         });
54913            
54914     },
54915     
54916     
54917     run : function()
54918     {
54919         // run get Values on the form, so it syncs any secondary forms.
54920         this.form.getValues();
54921         
54922         var o = this.options;
54923         var method = this.getMethod();
54924         var isPost = method == 'POST';
54925         if(o.clientValidation === false || this.form.isValid()){
54926             
54927             if (this.form.progressUrl) {
54928                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
54929                     (new Date() * 1) + '' + Math.random());
54930                     
54931             } 
54932             
54933             
54934             Roo.Ajax.request(Roo.apply(this.createCallback(), {
54935                 form:this.form.el.dom,
54936                 url:this.getUrl(!isPost),
54937                 method: method,
54938                 params:isPost ? this.getParams() : null,
54939                 isUpload: this.form.fileUpload,
54940                 formData : this.form.formData
54941             }));
54942             
54943             this.uploadProgress();
54944
54945         }else if (o.clientValidation !== false){ // client validation failed
54946             this.failureType = Roo.form.Action.CLIENT_INVALID;
54947             this.form.afterAction(this, false);
54948         }
54949     },
54950
54951     success : function(response)
54952     {
54953         this.uploadComplete= true;
54954         if (this.haveProgress) {
54955             Roo.MessageBox.hide();
54956         }
54957         
54958         
54959         var result = this.processResponse(response);
54960         if(result === true || result.success){
54961             this.form.afterAction(this, true);
54962             return;
54963         }
54964         if(result.errors){
54965             this.form.markInvalid(result.errors);
54966             this.failureType = Roo.form.Action.SERVER_INVALID;
54967         }
54968         this.form.afterAction(this, false);
54969     },
54970     failure : function(response)
54971     {
54972         this.uploadComplete= true;
54973         if (this.haveProgress) {
54974             Roo.MessageBox.hide();
54975         }
54976         
54977         this.response = response;
54978         this.failureType = Roo.form.Action.CONNECT_FAILURE;
54979         this.form.afterAction(this, false);
54980     },
54981     
54982     handleResponse : function(response){
54983         if(this.form.errorReader){
54984             var rs = this.form.errorReader.read(response);
54985             var errors = [];
54986             if(rs.records){
54987                 for(var i = 0, len = rs.records.length; i < len; i++) {
54988                     var r = rs.records[i];
54989                     errors[i] = r.data;
54990                 }
54991             }
54992             if(errors.length < 1){
54993                 errors = null;
54994             }
54995             return {
54996                 success : rs.success,
54997                 errors : errors
54998             };
54999         }
55000         var ret = false;
55001         try {
55002             ret = Roo.decode(response.responseText);
55003         } catch (e) {
55004             ret = {
55005                 success: false,
55006                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
55007                 errors : []
55008             };
55009         }
55010         return ret;
55011         
55012     }
55013 });
55014
55015
55016 Roo.form.Action.Load = function(form, options){
55017     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
55018     this.reader = this.form.reader;
55019 };
55020
55021 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
55022     type : 'load',
55023
55024     run : function(){
55025         
55026         Roo.Ajax.request(Roo.apply(
55027                 this.createCallback(), {
55028                     method:this.getMethod(),
55029                     url:this.getUrl(false),
55030                     params:this.getParams()
55031         }));
55032     },
55033
55034     success : function(response){
55035         
55036         var result = this.processResponse(response);
55037         if(result === true || !result.success || !result.data){
55038             this.failureType = Roo.form.Action.LOAD_FAILURE;
55039             this.form.afterAction(this, false);
55040             return;
55041         }
55042         this.form.clearInvalid();
55043         this.form.setValues(result.data);
55044         this.form.afterAction(this, true);
55045     },
55046
55047     handleResponse : function(response){
55048         if(this.form.reader){
55049             var rs = this.form.reader.read(response);
55050             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
55051             return {
55052                 success : rs.success,
55053                 data : data
55054             };
55055         }
55056         return Roo.decode(response.responseText);
55057     }
55058 });
55059
55060 Roo.form.Action.ACTION_TYPES = {
55061     'load' : Roo.form.Action.Load,
55062     'submit' : Roo.form.Action.Submit
55063 };/*
55064  * Based on:
55065  * Ext JS Library 1.1.1
55066  * Copyright(c) 2006-2007, Ext JS, LLC.
55067  *
55068  * Originally Released Under LGPL - original licence link has changed is not relivant.
55069  *
55070  * Fork - LGPL
55071  * <script type="text/javascript">
55072  */
55073  
55074 /**
55075  * @class Roo.form.Layout
55076  * @extends Roo.Component
55077  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55078  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
55079  * @constructor
55080  * @param {Object} config Configuration options
55081  */
55082 Roo.form.Layout = function(config){
55083     var xitems = [];
55084     if (config.items) {
55085         xitems = config.items;
55086         delete config.items;
55087     }
55088     Roo.form.Layout.superclass.constructor.call(this, config);
55089     this.stack = [];
55090     Roo.each(xitems, this.addxtype, this);
55091      
55092 };
55093
55094 Roo.extend(Roo.form.Layout, Roo.Component, {
55095     /**
55096      * @cfg {String/Object} autoCreate
55097      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
55098      */
55099     /**
55100      * @cfg {String/Object/Function} style
55101      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
55102      * a function which returns such a specification.
55103      */
55104     /**
55105      * @cfg {String} labelAlign
55106      * Valid values are "left," "top" and "right" (defaults to "left")
55107      */
55108     /**
55109      * @cfg {Number} labelWidth
55110      * Fixed width in pixels of all field labels (defaults to undefined)
55111      */
55112     /**
55113      * @cfg {Boolean} clear
55114      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
55115      */
55116     clear : true,
55117     /**
55118      * @cfg {String} labelSeparator
55119      * The separator to use after field labels (defaults to ':')
55120      */
55121     labelSeparator : ':',
55122     /**
55123      * @cfg {Boolean} hideLabels
55124      * True to suppress the display of field labels in this layout (defaults to false)
55125      */
55126     hideLabels : false,
55127
55128     // private
55129     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
55130     
55131     isLayout : true,
55132     
55133     // private
55134     onRender : function(ct, position){
55135         if(this.el){ // from markup
55136             this.el = Roo.get(this.el);
55137         }else {  // generate
55138             var cfg = this.getAutoCreate();
55139             this.el = ct.createChild(cfg, position);
55140         }
55141         if(this.style){
55142             this.el.applyStyles(this.style);
55143         }
55144         if(this.labelAlign){
55145             this.el.addClass('x-form-label-'+this.labelAlign);
55146         }
55147         if(this.hideLabels){
55148             this.labelStyle = "display:none";
55149             this.elementStyle = "padding-left:0;";
55150         }else{
55151             if(typeof this.labelWidth == 'number'){
55152                 this.labelStyle = "width:"+this.labelWidth+"px;";
55153                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
55154             }
55155             if(this.labelAlign == 'top'){
55156                 this.labelStyle = "width:auto;";
55157                 this.elementStyle = "padding-left:0;";
55158             }
55159         }
55160         var stack = this.stack;
55161         var slen = stack.length;
55162         if(slen > 0){
55163             if(!this.fieldTpl){
55164                 var t = new Roo.Template(
55165                     '<div class="x-form-item {5}">',
55166                         '<label for="{0}" style="{2}">{1}{4}</label>',
55167                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55168                         '</div>',
55169                     '</div><div class="x-form-clear-left"></div>'
55170                 );
55171                 t.disableFormats = true;
55172                 t.compile();
55173                 Roo.form.Layout.prototype.fieldTpl = t;
55174             }
55175             for(var i = 0; i < slen; i++) {
55176                 if(stack[i].isFormField){
55177                     this.renderField(stack[i]);
55178                 }else{
55179                     this.renderComponent(stack[i]);
55180                 }
55181             }
55182         }
55183         if(this.clear){
55184             this.el.createChild({cls:'x-form-clear'});
55185         }
55186     },
55187
55188     // private
55189     renderField : function(f){
55190         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
55191                f.id, //0
55192                f.fieldLabel, //1
55193                f.labelStyle||this.labelStyle||'', //2
55194                this.elementStyle||'', //3
55195                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
55196                f.itemCls||this.itemCls||''  //5
55197        ], true).getPrevSibling());
55198     },
55199
55200     // private
55201     renderComponent : function(c){
55202         c.render(c.isLayout ? this.el : this.el.createChild());    
55203     },
55204     /**
55205      * Adds a object form elements (using the xtype property as the factory method.)
55206      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
55207      * @param {Object} config 
55208      */
55209     addxtype : function(o)
55210     {
55211         // create the lement.
55212         o.form = this.form;
55213         var fe = Roo.factory(o, Roo.form);
55214         this.form.allItems.push(fe);
55215         this.stack.push(fe);
55216         
55217         if (fe.isFormField) {
55218             this.form.items.add(fe);
55219         }
55220          
55221         return fe;
55222     }
55223 });
55224
55225 /**
55226  * @class Roo.form.Column
55227  * @extends Roo.form.Layout
55228  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55229  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
55230  * @constructor
55231  * @param {Object} config Configuration options
55232  */
55233 Roo.form.Column = function(config){
55234     Roo.form.Column.superclass.constructor.call(this, config);
55235 };
55236
55237 Roo.extend(Roo.form.Column, Roo.form.Layout, {
55238     /**
55239      * @cfg {Number/String} width
55240      * The fixed width of the column in pixels or CSS value (defaults to "auto")
55241      */
55242     /**
55243      * @cfg {String/Object} autoCreate
55244      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
55245      */
55246
55247     // private
55248     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
55249
55250     // private
55251     onRender : function(ct, position){
55252         Roo.form.Column.superclass.onRender.call(this, ct, position);
55253         if(this.width){
55254             this.el.setWidth(this.width);
55255         }
55256     }
55257 });
55258
55259
55260 /**
55261  * @class Roo.form.Row
55262  * @extends Roo.form.Layout
55263  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55264  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
55265  * @constructor
55266  * @param {Object} config Configuration options
55267  */
55268
55269  
55270 Roo.form.Row = function(config){
55271     Roo.form.Row.superclass.constructor.call(this, config);
55272 };
55273  
55274 Roo.extend(Roo.form.Row, Roo.form.Layout, {
55275       /**
55276      * @cfg {Number/String} width
55277      * The fixed width of the column in pixels or CSS value (defaults to "auto")
55278      */
55279     /**
55280      * @cfg {Number/String} height
55281      * The fixed height of the column in pixels or CSS value (defaults to "auto")
55282      */
55283     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
55284     
55285     padWidth : 20,
55286     // private
55287     onRender : function(ct, position){
55288         //console.log('row render');
55289         if(!this.rowTpl){
55290             var t = new Roo.Template(
55291                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
55292                     '<label for="{0}" style="{2}">{1}{4}</label>',
55293                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55294                     '</div>',
55295                 '</div>'
55296             );
55297             t.disableFormats = true;
55298             t.compile();
55299             Roo.form.Layout.prototype.rowTpl = t;
55300         }
55301         this.fieldTpl = this.rowTpl;
55302         
55303         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
55304         var labelWidth = 100;
55305         
55306         if ((this.labelAlign != 'top')) {
55307             if (typeof this.labelWidth == 'number') {
55308                 labelWidth = this.labelWidth
55309             }
55310             this.padWidth =  20 + labelWidth;
55311             
55312         }
55313         
55314         Roo.form.Column.superclass.onRender.call(this, ct, position);
55315         if(this.width){
55316             this.el.setWidth(this.width);
55317         }
55318         if(this.height){
55319             this.el.setHeight(this.height);
55320         }
55321     },
55322     
55323     // private
55324     renderField : function(f){
55325         f.fieldEl = this.fieldTpl.append(this.el, [
55326                f.id, f.fieldLabel,
55327                f.labelStyle||this.labelStyle||'',
55328                this.elementStyle||'',
55329                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
55330                f.itemCls||this.itemCls||'',
55331                f.width ? f.width + this.padWidth : 160 + this.padWidth
55332        ],true);
55333     }
55334 });
55335  
55336
55337 /**
55338  * @class Roo.form.FieldSet
55339  * @extends Roo.form.Layout
55340  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
55341  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
55342  * @constructor
55343  * @param {Object} config Configuration options
55344  */
55345 Roo.form.FieldSet = function(config){
55346     Roo.form.FieldSet.superclass.constructor.call(this, config);
55347 };
55348
55349 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
55350     /**
55351      * @cfg {String} legend
55352      * The text to display as the legend for the FieldSet (defaults to '')
55353      */
55354     /**
55355      * @cfg {String/Object} autoCreate
55356      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
55357      */
55358
55359     // private
55360     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
55361
55362     // private
55363     onRender : function(ct, position){
55364         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
55365         if(this.legend){
55366             this.setLegend(this.legend);
55367         }
55368     },
55369
55370     // private
55371     setLegend : function(text){
55372         if(this.rendered){
55373             this.el.child('legend').update(text);
55374         }
55375     }
55376 });/*
55377  * Based on:
55378  * Ext JS Library 1.1.1
55379  * Copyright(c) 2006-2007, Ext JS, LLC.
55380  *
55381  * Originally Released Under LGPL - original licence link has changed is not relivant.
55382  *
55383  * Fork - LGPL
55384  * <script type="text/javascript">
55385  */
55386 /**
55387  * @class Roo.form.VTypes
55388  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
55389  * @static
55390  */
55391 Roo.form.VTypes = function(){
55392     // closure these in so they are only created once.
55393     var alpha = /^[a-zA-Z_]+$/;
55394     var alphanum = /^[a-zA-Z0-9_]+$/;
55395     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
55396     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
55397
55398     // All these messages and functions are configurable
55399     return {
55400         /**
55401          * The function used to validate email addresses
55402          * @param {String} value The email address
55403          */
55404         'email' : function(v){
55405             return email.test(v);
55406         },
55407         /**
55408          * The error text to display when the email validation function returns false
55409          * @type String
55410          */
55411         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
55412         /**
55413          * The keystroke filter mask to be applied on email input
55414          * @type RegExp
55415          */
55416         'emailMask' : /[a-z0-9_\.\-@]/i,
55417
55418         /**
55419          * The function used to validate URLs
55420          * @param {String} value The URL
55421          */
55422         'url' : function(v){
55423             return url.test(v);
55424         },
55425         /**
55426          * The error text to display when the url validation function returns false
55427          * @type String
55428          */
55429         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
55430         
55431         /**
55432          * The function used to validate alpha values
55433          * @param {String} value The value
55434          */
55435         'alpha' : function(v){
55436             return alpha.test(v);
55437         },
55438         /**
55439          * The error text to display when the alpha validation function returns false
55440          * @type String
55441          */
55442         'alphaText' : 'This field should only contain letters and _',
55443         /**
55444          * The keystroke filter mask to be applied on alpha input
55445          * @type RegExp
55446          */
55447         'alphaMask' : /[a-z_]/i,
55448
55449         /**
55450          * The function used to validate alphanumeric values
55451          * @param {String} value The value
55452          */
55453         'alphanum' : function(v){
55454             return alphanum.test(v);
55455         },
55456         /**
55457          * The error text to display when the alphanumeric validation function returns false
55458          * @type String
55459          */
55460         'alphanumText' : 'This field should only contain letters, numbers and _',
55461         /**
55462          * The keystroke filter mask to be applied on alphanumeric input
55463          * @type RegExp
55464          */
55465         'alphanumMask' : /[a-z0-9_]/i
55466     };
55467 }();//<script type="text/javascript">
55468
55469 /**
55470  * @class Roo.form.FCKeditor
55471  * @extends Roo.form.TextArea
55472  * Wrapper around the FCKEditor http://www.fckeditor.net
55473  * @constructor
55474  * Creates a new FCKeditor
55475  * @param {Object} config Configuration options
55476  */
55477 Roo.form.FCKeditor = function(config){
55478     Roo.form.FCKeditor.superclass.constructor.call(this, config);
55479     this.addEvents({
55480          /**
55481          * @event editorinit
55482          * Fired when the editor is initialized - you can add extra handlers here..
55483          * @param {FCKeditor} this
55484          * @param {Object} the FCK object.
55485          */
55486         editorinit : true
55487     });
55488     
55489     
55490 };
55491 Roo.form.FCKeditor.editors = { };
55492 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
55493 {
55494     //defaultAutoCreate : {
55495     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
55496     //},
55497     // private
55498     /**
55499      * @cfg {Object} fck options - see fck manual for details.
55500      */
55501     fckconfig : false,
55502     
55503     /**
55504      * @cfg {Object} fck toolbar set (Basic or Default)
55505      */
55506     toolbarSet : 'Basic',
55507     /**
55508      * @cfg {Object} fck BasePath
55509      */ 
55510     basePath : '/fckeditor/',
55511     
55512     
55513     frame : false,
55514     
55515     value : '',
55516     
55517    
55518     onRender : function(ct, position)
55519     {
55520         if(!this.el){
55521             this.defaultAutoCreate = {
55522                 tag: "textarea",
55523                 style:"width:300px;height:60px;",
55524                 autocomplete: "new-password"
55525             };
55526         }
55527         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
55528         /*
55529         if(this.grow){
55530             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
55531             if(this.preventScrollbars){
55532                 this.el.setStyle("overflow", "hidden");
55533             }
55534             this.el.setHeight(this.growMin);
55535         }
55536         */
55537         //console.log('onrender' + this.getId() );
55538         Roo.form.FCKeditor.editors[this.getId()] = this;
55539          
55540
55541         this.replaceTextarea() ;
55542         
55543     },
55544     
55545     getEditor : function() {
55546         return this.fckEditor;
55547     },
55548     /**
55549      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
55550      * @param {Mixed} value The value to set
55551      */
55552     
55553     
55554     setValue : function(value)
55555     {
55556         //console.log('setValue: ' + value);
55557         
55558         if(typeof(value) == 'undefined') { // not sure why this is happending...
55559             return;
55560         }
55561         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55562         
55563         //if(!this.el || !this.getEditor()) {
55564         //    this.value = value;
55565             //this.setValue.defer(100,this,[value]);    
55566         //    return;
55567         //} 
55568         
55569         if(!this.getEditor()) {
55570             return;
55571         }
55572         
55573         this.getEditor().SetData(value);
55574         
55575         //
55576
55577     },
55578
55579     /**
55580      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
55581      * @return {Mixed} value The field value
55582      */
55583     getValue : function()
55584     {
55585         
55586         if (this.frame && this.frame.dom.style.display == 'none') {
55587             return Roo.form.FCKeditor.superclass.getValue.call(this);
55588         }
55589         
55590         if(!this.el || !this.getEditor()) {
55591            
55592            // this.getValue.defer(100,this); 
55593             return this.value;
55594         }
55595        
55596         
55597         var value=this.getEditor().GetData();
55598         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
55599         return Roo.form.FCKeditor.superclass.getValue.call(this);
55600         
55601
55602     },
55603
55604     /**
55605      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
55606      * @return {Mixed} value The field value
55607      */
55608     getRawValue : function()
55609     {
55610         if (this.frame && this.frame.dom.style.display == 'none') {
55611             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
55612         }
55613         
55614         if(!this.el || !this.getEditor()) {
55615             //this.getRawValue.defer(100,this); 
55616             return this.value;
55617             return;
55618         }
55619         
55620         
55621         
55622         var value=this.getEditor().GetData();
55623         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
55624         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
55625          
55626     },
55627     
55628     setSize : function(w,h) {
55629         
55630         
55631         
55632         //if (this.frame && this.frame.dom.style.display == 'none') {
55633         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
55634         //    return;
55635         //}
55636         //if(!this.el || !this.getEditor()) {
55637         //    this.setSize.defer(100,this, [w,h]); 
55638         //    return;
55639         //}
55640         
55641         
55642         
55643         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
55644         
55645         this.frame.dom.setAttribute('width', w);
55646         this.frame.dom.setAttribute('height', h);
55647         this.frame.setSize(w,h);
55648         
55649     },
55650     
55651     toggleSourceEdit : function(value) {
55652         
55653       
55654          
55655         this.el.dom.style.display = value ? '' : 'none';
55656         this.frame.dom.style.display = value ?  'none' : '';
55657         
55658     },
55659     
55660     
55661     focus: function(tag)
55662     {
55663         if (this.frame.dom.style.display == 'none') {
55664             return Roo.form.FCKeditor.superclass.focus.call(this);
55665         }
55666         if(!this.el || !this.getEditor()) {
55667             this.focus.defer(100,this, [tag]); 
55668             return;
55669         }
55670         
55671         
55672         
55673         
55674         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
55675         this.getEditor().Focus();
55676         if (tgs.length) {
55677             if (!this.getEditor().Selection.GetSelection()) {
55678                 this.focus.defer(100,this, [tag]); 
55679                 return;
55680             }
55681             
55682             
55683             var r = this.getEditor().EditorDocument.createRange();
55684             r.setStart(tgs[0],0);
55685             r.setEnd(tgs[0],0);
55686             this.getEditor().Selection.GetSelection().removeAllRanges();
55687             this.getEditor().Selection.GetSelection().addRange(r);
55688             this.getEditor().Focus();
55689         }
55690         
55691     },
55692     
55693     
55694     
55695     replaceTextarea : function()
55696     {
55697         if ( document.getElementById( this.getId() + '___Frame' ) ) {
55698             return ;
55699         }
55700         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
55701         //{
55702             // We must check the elements firstly using the Id and then the name.
55703         var oTextarea = document.getElementById( this.getId() );
55704         
55705         var colElementsByName = document.getElementsByName( this.getId() ) ;
55706          
55707         oTextarea.style.display = 'none' ;
55708
55709         if ( oTextarea.tabIndex ) {            
55710             this.TabIndex = oTextarea.tabIndex ;
55711         }
55712         
55713         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
55714         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
55715         this.frame = Roo.get(this.getId() + '___Frame')
55716     },
55717     
55718     _getConfigHtml : function()
55719     {
55720         var sConfig = '' ;
55721
55722         for ( var o in this.fckconfig ) {
55723             sConfig += sConfig.length > 0  ? '&amp;' : '';
55724             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
55725         }
55726
55727         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
55728     },
55729     
55730     
55731     _getIFrameHtml : function()
55732     {
55733         var sFile = 'fckeditor.html' ;
55734         /* no idea what this is about..
55735         try
55736         {
55737             if ( (/fcksource=true/i).test( window.top.location.search ) )
55738                 sFile = 'fckeditor.original.html' ;
55739         }
55740         catch (e) { 
55741         */
55742
55743         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
55744         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
55745         
55746         
55747         var html = '<iframe id="' + this.getId() +
55748             '___Frame" src="' + sLink +
55749             '" width="' + this.width +
55750             '" height="' + this.height + '"' +
55751             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
55752             ' frameborder="0" scrolling="no"></iframe>' ;
55753
55754         return html ;
55755     },
55756     
55757     _insertHtmlBefore : function( html, element )
55758     {
55759         if ( element.insertAdjacentHTML )       {
55760             // IE
55761             element.insertAdjacentHTML( 'beforeBegin', html ) ;
55762         } else { // Gecko
55763             var oRange = document.createRange() ;
55764             oRange.setStartBefore( element ) ;
55765             var oFragment = oRange.createContextualFragment( html );
55766             element.parentNode.insertBefore( oFragment, element ) ;
55767         }
55768     }
55769     
55770     
55771   
55772     
55773     
55774     
55775     
55776
55777 });
55778
55779 //Roo.reg('fckeditor', Roo.form.FCKeditor);
55780
55781 function FCKeditor_OnComplete(editorInstance){
55782     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
55783     f.fckEditor = editorInstance;
55784     //console.log("loaded");
55785     f.fireEvent('editorinit', f, editorInstance);
55786
55787   
55788
55789  
55790
55791
55792
55793
55794
55795
55796
55797
55798
55799
55800
55801
55802
55803
55804
55805 //<script type="text/javascript">
55806 /**
55807  * @class Roo.form.GridField
55808  * @extends Roo.form.Field
55809  * Embed a grid (or editable grid into a form)
55810  * STATUS ALPHA
55811  * 
55812  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
55813  * it needs 
55814  * xgrid.store = Roo.data.Store
55815  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
55816  * xgrid.store.reader = Roo.data.JsonReader 
55817  * 
55818  * 
55819  * @constructor
55820  * Creates a new GridField
55821  * @param {Object} config Configuration options
55822  */
55823 Roo.form.GridField = function(config){
55824     Roo.form.GridField.superclass.constructor.call(this, config);
55825      
55826 };
55827
55828 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
55829     /**
55830      * @cfg {Number} width  - used to restrict width of grid..
55831      */
55832     width : 100,
55833     /**
55834      * @cfg {Number} height - used to restrict height of grid..
55835      */
55836     height : 50,
55837      /**
55838      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
55839          * 
55840          *}
55841      */
55842     xgrid : false, 
55843     /**
55844      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
55845      * {tag: "input", type: "checkbox", autocomplete: "off"})
55846      */
55847    // defaultAutoCreate : { tag: 'div' },
55848     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
55849     /**
55850      * @cfg {String} addTitle Text to include for adding a title.
55851      */
55852     addTitle : false,
55853     //
55854     onResize : function(){
55855         Roo.form.Field.superclass.onResize.apply(this, arguments);
55856     },
55857
55858     initEvents : function(){
55859         // Roo.form.Checkbox.superclass.initEvents.call(this);
55860         // has no events...
55861        
55862     },
55863
55864
55865     getResizeEl : function(){
55866         return this.wrap;
55867     },
55868
55869     getPositionEl : function(){
55870         return this.wrap;
55871     },
55872
55873     // private
55874     onRender : function(ct, position){
55875         
55876         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
55877         var style = this.style;
55878         delete this.style;
55879         
55880         Roo.form.GridField.superclass.onRender.call(this, ct, position);
55881         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
55882         this.viewEl = this.wrap.createChild({ tag: 'div' });
55883         if (style) {
55884             this.viewEl.applyStyles(style);
55885         }
55886         if (this.width) {
55887             this.viewEl.setWidth(this.width);
55888         }
55889         if (this.height) {
55890             this.viewEl.setHeight(this.height);
55891         }
55892         //if(this.inputValue !== undefined){
55893         //this.setValue(this.value);
55894         
55895         
55896         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
55897         
55898         
55899         this.grid.render();
55900         this.grid.getDataSource().on('remove', this.refreshValue, this);
55901         this.grid.getDataSource().on('update', this.refreshValue, this);
55902         this.grid.on('afteredit', this.refreshValue, this);
55903  
55904     },
55905      
55906     
55907     /**
55908      * Sets the value of the item. 
55909      * @param {String} either an object  or a string..
55910      */
55911     setValue : function(v){
55912         //this.value = v;
55913         v = v || []; // empty set..
55914         // this does not seem smart - it really only affects memoryproxy grids..
55915         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
55916             var ds = this.grid.getDataSource();
55917             // assumes a json reader..
55918             var data = {}
55919             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
55920             ds.loadData( data);
55921         }
55922         // clear selection so it does not get stale.
55923         if (this.grid.sm) { 
55924             this.grid.sm.clearSelections();
55925         }
55926         
55927         Roo.form.GridField.superclass.setValue.call(this, v);
55928         this.refreshValue();
55929         // should load data in the grid really....
55930     },
55931     
55932     // private
55933     refreshValue: function() {
55934          var val = [];
55935         this.grid.getDataSource().each(function(r) {
55936             val.push(r.data);
55937         });
55938         this.el.dom.value = Roo.encode(val);
55939     }
55940     
55941      
55942     
55943     
55944 });/*
55945  * Based on:
55946  * Ext JS Library 1.1.1
55947  * Copyright(c) 2006-2007, Ext JS, LLC.
55948  *
55949  * Originally Released Under LGPL - original licence link has changed is not relivant.
55950  *
55951  * Fork - LGPL
55952  * <script type="text/javascript">
55953  */
55954 /**
55955  * @class Roo.form.DisplayField
55956  * @extends Roo.form.Field
55957  * A generic Field to display non-editable data.
55958  * @cfg {Boolean} closable (true|false) default false
55959  * @constructor
55960  * Creates a new Display Field item.
55961  * @param {Object} config Configuration options
55962  */
55963 Roo.form.DisplayField = function(config){
55964     Roo.form.DisplayField.superclass.constructor.call(this, config);
55965     
55966     this.addEvents({
55967         /**
55968          * @event close
55969          * Fires after the click the close btn
55970              * @param {Roo.form.DisplayField} this
55971              */
55972         close : true
55973     });
55974 };
55975
55976 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
55977     inputType:      'hidden',
55978     allowBlank:     true,
55979     readOnly:         true,
55980     
55981  
55982     /**
55983      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
55984      */
55985     focusClass : undefined,
55986     /**
55987      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
55988      */
55989     fieldClass: 'x-form-field',
55990     
55991      /**
55992      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
55993      */
55994     valueRenderer: undefined,
55995     
55996     width: 100,
55997     /**
55998      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
55999      * {tag: "input", type: "checkbox", autocomplete: "off"})
56000      */
56001      
56002  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
56003  
56004     closable : false,
56005     
56006     onResize : function(){
56007         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
56008         
56009     },
56010
56011     initEvents : function(){
56012         // Roo.form.Checkbox.superclass.initEvents.call(this);
56013         // has no events...
56014         
56015         if(this.closable){
56016             this.closeEl.on('click', this.onClose, this);
56017         }
56018        
56019     },
56020
56021
56022     getResizeEl : function(){
56023         return this.wrap;
56024     },
56025
56026     getPositionEl : function(){
56027         return this.wrap;
56028     },
56029
56030     // private
56031     onRender : function(ct, position){
56032         
56033         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
56034         //if(this.inputValue !== undefined){
56035         this.wrap = this.el.wrap();
56036         
56037         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
56038         
56039         if(this.closable){
56040             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
56041         }
56042         
56043         if (this.bodyStyle) {
56044             this.viewEl.applyStyles(this.bodyStyle);
56045         }
56046         //this.viewEl.setStyle('padding', '2px');
56047         
56048         this.setValue(this.value);
56049         
56050     },
56051 /*
56052     // private
56053     initValue : Roo.emptyFn,
56054
56055   */
56056
56057         // private
56058     onClick : function(){
56059         
56060     },
56061
56062     /**
56063      * Sets the checked state of the checkbox.
56064      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
56065      */
56066     setValue : function(v){
56067         this.value = v;
56068         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
56069         // this might be called before we have a dom element..
56070         if (!this.viewEl) {
56071             return;
56072         }
56073         this.viewEl.dom.innerHTML = html;
56074         Roo.form.DisplayField.superclass.setValue.call(this, v);
56075
56076     },
56077     
56078     onClose : function(e)
56079     {
56080         e.preventDefault();
56081         
56082         this.fireEvent('close', this);
56083     }
56084 });/*
56085  * 
56086  * Licence- LGPL
56087  * 
56088  */
56089
56090 /**
56091  * @class Roo.form.DayPicker
56092  * @extends Roo.form.Field
56093  * A Day picker show [M] [T] [W] ....
56094  * @constructor
56095  * Creates a new Day Picker
56096  * @param {Object} config Configuration options
56097  */
56098 Roo.form.DayPicker= function(config){
56099     Roo.form.DayPicker.superclass.constructor.call(this, config);
56100      
56101 };
56102
56103 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
56104     /**
56105      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56106      */
56107     focusClass : undefined,
56108     /**
56109      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56110      */
56111     fieldClass: "x-form-field",
56112    
56113     /**
56114      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56115      * {tag: "input", type: "checkbox", autocomplete: "off"})
56116      */
56117     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
56118     
56119    
56120     actionMode : 'viewEl', 
56121     //
56122     // private
56123  
56124     inputType : 'hidden',
56125     
56126      
56127     inputElement: false, // real input element?
56128     basedOn: false, // ????
56129     
56130     isFormField: true, // not sure where this is needed!!!!
56131
56132     onResize : function(){
56133         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
56134         if(!this.boxLabel){
56135             this.el.alignTo(this.wrap, 'c-c');
56136         }
56137     },
56138
56139     initEvents : function(){
56140         Roo.form.Checkbox.superclass.initEvents.call(this);
56141         this.el.on("click", this.onClick,  this);
56142         this.el.on("change", this.onClick,  this);
56143     },
56144
56145
56146     getResizeEl : function(){
56147         return this.wrap;
56148     },
56149
56150     getPositionEl : function(){
56151         return this.wrap;
56152     },
56153
56154     
56155     // private
56156     onRender : function(ct, position){
56157         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
56158        
56159         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
56160         
56161         var r1 = '<table><tr>';
56162         var r2 = '<tr class="x-form-daypick-icons">';
56163         for (var i=0; i < 7; i++) {
56164             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
56165             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
56166         }
56167         
56168         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
56169         viewEl.select('img').on('click', this.onClick, this);
56170         this.viewEl = viewEl;   
56171         
56172         
56173         // this will not work on Chrome!!!
56174         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
56175         this.el.on('propertychange', this.setFromHidden,  this);  //ie
56176         
56177         
56178           
56179
56180     },
56181
56182     // private
56183     initValue : Roo.emptyFn,
56184
56185     /**
56186      * Returns the checked state of the checkbox.
56187      * @return {Boolean} True if checked, else false
56188      */
56189     getValue : function(){
56190         return this.el.dom.value;
56191         
56192     },
56193
56194         // private
56195     onClick : function(e){ 
56196         //this.setChecked(!this.checked);
56197         Roo.get(e.target).toggleClass('x-menu-item-checked');
56198         this.refreshValue();
56199         //if(this.el.dom.checked != this.checked){
56200         //    this.setValue(this.el.dom.checked);
56201        // }
56202     },
56203     
56204     // private
56205     refreshValue : function()
56206     {
56207         var val = '';
56208         this.viewEl.select('img',true).each(function(e,i,n)  {
56209             val += e.is(".x-menu-item-checked") ? String(n) : '';
56210         });
56211         this.setValue(val, true);
56212     },
56213
56214     /**
56215      * Sets the checked state of the checkbox.
56216      * On is always based on a string comparison between inputValue and the param.
56217      * @param {Boolean/String} value - the value to set 
56218      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
56219      */
56220     setValue : function(v,suppressEvent){
56221         if (!this.el.dom) {
56222             return;
56223         }
56224         var old = this.el.dom.value ;
56225         this.el.dom.value = v;
56226         if (suppressEvent) {
56227             return ;
56228         }
56229          
56230         // update display..
56231         this.viewEl.select('img',true).each(function(e,i,n)  {
56232             
56233             var on = e.is(".x-menu-item-checked");
56234             var newv = v.indexOf(String(n)) > -1;
56235             if (on != newv) {
56236                 e.toggleClass('x-menu-item-checked');
56237             }
56238             
56239         });
56240         
56241         
56242         this.fireEvent('change', this, v, old);
56243         
56244         
56245     },
56246    
56247     // handle setting of hidden value by some other method!!?!?
56248     setFromHidden: function()
56249     {
56250         if(!this.el){
56251             return;
56252         }
56253         //console.log("SET FROM HIDDEN");
56254         //alert('setFrom hidden');
56255         this.setValue(this.el.dom.value);
56256     },
56257     
56258     onDestroy : function()
56259     {
56260         if(this.viewEl){
56261             Roo.get(this.viewEl).remove();
56262         }
56263          
56264         Roo.form.DayPicker.superclass.onDestroy.call(this);
56265     }
56266
56267 });/*
56268  * RooJS Library 1.1.1
56269  * Copyright(c) 2008-2011  Alan Knowles
56270  *
56271  * License - LGPL
56272  */
56273  
56274
56275 /**
56276  * @class Roo.form.ComboCheck
56277  * @extends Roo.form.ComboBox
56278  * A combobox for multiple select items.
56279  *
56280  * FIXME - could do with a reset button..
56281  * 
56282  * @constructor
56283  * Create a new ComboCheck
56284  * @param {Object} config Configuration options
56285  */
56286 Roo.form.ComboCheck = function(config){
56287     Roo.form.ComboCheck.superclass.constructor.call(this, config);
56288     // should verify some data...
56289     // like
56290     // hiddenName = required..
56291     // displayField = required
56292     // valudField == required
56293     var req= [ 'hiddenName', 'displayField', 'valueField' ];
56294     var _t = this;
56295     Roo.each(req, function(e) {
56296         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
56297             throw "Roo.form.ComboCheck : missing value for: " + e;
56298         }
56299     });
56300     
56301     
56302 };
56303
56304 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
56305      
56306      
56307     editable : false,
56308      
56309     selectedClass: 'x-menu-item-checked', 
56310     
56311     // private
56312     onRender : function(ct, position){
56313         var _t = this;
56314         
56315         
56316         
56317         if(!this.tpl){
56318             var cls = 'x-combo-list';
56319
56320             
56321             this.tpl =  new Roo.Template({
56322                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
56323                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
56324                    '<span>{' + this.displayField + '}</span>' +
56325                     '</div>' 
56326                 
56327             });
56328         }
56329  
56330         
56331         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
56332         this.view.singleSelect = false;
56333         this.view.multiSelect = true;
56334         this.view.toggleSelect = true;
56335         this.pageTb.add(new Roo.Toolbar.Fill(), {
56336             
56337             text: 'Done',
56338             handler: function()
56339             {
56340                 _t.collapse();
56341             }
56342         });
56343     },
56344     
56345     onViewOver : function(e, t){
56346         // do nothing...
56347         return;
56348         
56349     },
56350     
56351     onViewClick : function(doFocus,index){
56352         return;
56353         
56354     },
56355     select: function () {
56356         //Roo.log("SELECT CALLED");
56357     },
56358      
56359     selectByValue : function(xv, scrollIntoView){
56360         var ar = this.getValueArray();
56361         var sels = [];
56362         
56363         Roo.each(ar, function(v) {
56364             if(v === undefined || v === null){
56365                 return;
56366             }
56367             var r = this.findRecord(this.valueField, v);
56368             if(r){
56369                 sels.push(this.store.indexOf(r))
56370                 
56371             }
56372         },this);
56373         this.view.select(sels);
56374         return false;
56375     },
56376     
56377     
56378     
56379     onSelect : function(record, index){
56380        // Roo.log("onselect Called");
56381        // this is only called by the clear button now..
56382         this.view.clearSelections();
56383         this.setValue('[]');
56384         if (this.value != this.valueBefore) {
56385             this.fireEvent('change', this, this.value, this.valueBefore);
56386             this.valueBefore = this.value;
56387         }
56388     },
56389     getValueArray : function()
56390     {
56391         var ar = [] ;
56392         
56393         try {
56394             //Roo.log(this.value);
56395             if (typeof(this.value) == 'undefined') {
56396                 return [];
56397             }
56398             var ar = Roo.decode(this.value);
56399             return  ar instanceof Array ? ar : []; //?? valid?
56400             
56401         } catch(e) {
56402             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
56403             return [];
56404         }
56405          
56406     },
56407     expand : function ()
56408     {
56409         
56410         Roo.form.ComboCheck.superclass.expand.call(this);
56411         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
56412         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
56413         
56414
56415     },
56416     
56417     collapse : function(){
56418         Roo.form.ComboCheck.superclass.collapse.call(this);
56419         var sl = this.view.getSelectedIndexes();
56420         var st = this.store;
56421         var nv = [];
56422         var tv = [];
56423         var r;
56424         Roo.each(sl, function(i) {
56425             r = st.getAt(i);
56426             nv.push(r.get(this.valueField));
56427         },this);
56428         this.setValue(Roo.encode(nv));
56429         if (this.value != this.valueBefore) {
56430
56431             this.fireEvent('change', this, this.value, this.valueBefore);
56432             this.valueBefore = this.value;
56433         }
56434         
56435     },
56436     
56437     setValue : function(v){
56438         // Roo.log(v);
56439         this.value = v;
56440         
56441         var vals = this.getValueArray();
56442         var tv = [];
56443         Roo.each(vals, function(k) {
56444             var r = this.findRecord(this.valueField, k);
56445             if(r){
56446                 tv.push(r.data[this.displayField]);
56447             }else if(this.valueNotFoundText !== undefined){
56448                 tv.push( this.valueNotFoundText );
56449             }
56450         },this);
56451        // Roo.log(tv);
56452         
56453         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
56454         this.hiddenField.value = v;
56455         this.value = v;
56456     }
56457     
56458 });/*
56459  * Based on:
56460  * Ext JS Library 1.1.1
56461  * Copyright(c) 2006-2007, Ext JS, LLC.
56462  *
56463  * Originally Released Under LGPL - original licence link has changed is not relivant.
56464  *
56465  * Fork - LGPL
56466  * <script type="text/javascript">
56467  */
56468  
56469 /**
56470  * @class Roo.form.Signature
56471  * @extends Roo.form.Field
56472  * Signature field.  
56473  * @constructor
56474  * 
56475  * @param {Object} config Configuration options
56476  */
56477
56478 Roo.form.Signature = function(config){
56479     Roo.form.Signature.superclass.constructor.call(this, config);
56480     
56481     this.addEvents({// not in used??
56482          /**
56483          * @event confirm
56484          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
56485              * @param {Roo.form.Signature} combo This combo box
56486              */
56487         'confirm' : true,
56488         /**
56489          * @event reset
56490          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
56491              * @param {Roo.form.ComboBox} combo This combo box
56492              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
56493              */
56494         'reset' : true
56495     });
56496 };
56497
56498 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
56499     /**
56500      * @cfg {Object} labels Label to use when rendering a form.
56501      * defaults to 
56502      * labels : { 
56503      *      clear : "Clear",
56504      *      confirm : "Confirm"
56505      *  }
56506      */
56507     labels : { 
56508         clear : "Clear",
56509         confirm : "Confirm"
56510     },
56511     /**
56512      * @cfg {Number} width The signature panel width (defaults to 300)
56513      */
56514     width: 300,
56515     /**
56516      * @cfg {Number} height The signature panel height (defaults to 100)
56517      */
56518     height : 100,
56519     /**
56520      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
56521      */
56522     allowBlank : false,
56523     
56524     //private
56525     // {Object} signPanel The signature SVG panel element (defaults to {})
56526     signPanel : {},
56527     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
56528     isMouseDown : false,
56529     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
56530     isConfirmed : false,
56531     // {String} signatureTmp SVG mapping string (defaults to empty string)
56532     signatureTmp : '',
56533     
56534     
56535     defaultAutoCreate : { // modified by initCompnoent..
56536         tag: "input",
56537         type:"hidden"
56538     },
56539
56540     // private
56541     onRender : function(ct, position){
56542         
56543         Roo.form.Signature.superclass.onRender.call(this, ct, position);
56544         
56545         this.wrap = this.el.wrap({
56546             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
56547         });
56548         
56549         this.createToolbar(this);
56550         this.signPanel = this.wrap.createChild({
56551                 tag: 'div',
56552                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
56553             }, this.el
56554         );
56555             
56556         this.svgID = Roo.id();
56557         this.svgEl = this.signPanel.createChild({
56558               xmlns : 'http://www.w3.org/2000/svg',
56559               tag : 'svg',
56560               id : this.svgID + "-svg",
56561               width: this.width,
56562               height: this.height,
56563               viewBox: '0 0 '+this.width+' '+this.height,
56564               cn : [
56565                 {
56566                     tag: "rect",
56567                     id: this.svgID + "-svg-r",
56568                     width: this.width,
56569                     height: this.height,
56570                     fill: "#ffa"
56571                 },
56572                 {
56573                     tag: "line",
56574                     id: this.svgID + "-svg-l",
56575                     x1: "0", // start
56576                     y1: (this.height*0.8), // start set the line in 80% of height
56577                     x2: this.width, // end
56578                     y2: (this.height*0.8), // end set the line in 80% of height
56579                     'stroke': "#666",
56580                     'stroke-width': "1",
56581                     'stroke-dasharray': "3",
56582                     'shape-rendering': "crispEdges",
56583                     'pointer-events': "none"
56584                 },
56585                 {
56586                     tag: "path",
56587                     id: this.svgID + "-svg-p",
56588                     'stroke': "navy",
56589                     'stroke-width': "3",
56590                     'fill': "none",
56591                     'pointer-events': 'none'
56592                 }
56593               ]
56594         });
56595         this.createSVG();
56596         this.svgBox = this.svgEl.dom.getScreenCTM();
56597     },
56598     createSVG : function(){ 
56599         var svg = this.signPanel;
56600         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
56601         var t = this;
56602
56603         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
56604         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
56605         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
56606         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
56607         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
56608         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
56609         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
56610         
56611     },
56612     isTouchEvent : function(e){
56613         return e.type.match(/^touch/);
56614     },
56615     getCoords : function (e) {
56616         var pt    = this.svgEl.dom.createSVGPoint();
56617         pt.x = e.clientX; 
56618         pt.y = e.clientY;
56619         if (this.isTouchEvent(e)) {
56620             pt.x =  e.targetTouches[0].clientX;
56621             pt.y = e.targetTouches[0].clientY;
56622         }
56623         var a = this.svgEl.dom.getScreenCTM();
56624         var b = a.inverse();
56625         var mx = pt.matrixTransform(b);
56626         return mx.x + ',' + mx.y;
56627     },
56628     //mouse event headler 
56629     down : function (e) {
56630         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
56631         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
56632         
56633         this.isMouseDown = true;
56634         
56635         e.preventDefault();
56636     },
56637     move : function (e) {
56638         if (this.isMouseDown) {
56639             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
56640             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
56641         }
56642         
56643         e.preventDefault();
56644     },
56645     up : function (e) {
56646         this.isMouseDown = false;
56647         var sp = this.signatureTmp.split(' ');
56648         
56649         if(sp.length > 1){
56650             if(!sp[sp.length-2].match(/^L/)){
56651                 sp.pop();
56652                 sp.pop();
56653                 sp.push("");
56654                 this.signatureTmp = sp.join(" ");
56655             }
56656         }
56657         if(this.getValue() != this.signatureTmp){
56658             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56659             this.isConfirmed = false;
56660         }
56661         e.preventDefault();
56662     },
56663     
56664     /**
56665      * Protected method that will not generally be called directly. It
56666      * is called when the editor creates its toolbar. Override this method if you need to
56667      * add custom toolbar buttons.
56668      * @param {HtmlEditor} editor
56669      */
56670     createToolbar : function(editor){
56671          function btn(id, toggle, handler){
56672             var xid = fid + '-'+ id ;
56673             return {
56674                 id : xid,
56675                 cmd : id,
56676                 cls : 'x-btn-icon x-edit-'+id,
56677                 enableToggle:toggle !== false,
56678                 scope: editor, // was editor...
56679                 handler:handler||editor.relayBtnCmd,
56680                 clickEvent:'mousedown',
56681                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
56682                 tabIndex:-1
56683             };
56684         }
56685         
56686         
56687         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
56688         this.tb = tb;
56689         this.tb.add(
56690            {
56691                 cls : ' x-signature-btn x-signature-'+id,
56692                 scope: editor, // was editor...
56693                 handler: this.reset,
56694                 clickEvent:'mousedown',
56695                 text: this.labels.clear
56696             },
56697             {
56698                  xtype : 'Fill',
56699                  xns: Roo.Toolbar
56700             }, 
56701             {
56702                 cls : '  x-signature-btn x-signature-'+id,
56703                 scope: editor, // was editor...
56704                 handler: this.confirmHandler,
56705                 clickEvent:'mousedown',
56706                 text: this.labels.confirm
56707             }
56708         );
56709     
56710     },
56711     //public
56712     /**
56713      * when user is clicked confirm then show this image.....
56714      * 
56715      * @return {String} Image Data URI
56716      */
56717     getImageDataURI : function(){
56718         var svg = this.svgEl.dom.parentNode.innerHTML;
56719         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
56720         return src; 
56721     },
56722     /**
56723      * 
56724      * @return {Boolean} this.isConfirmed
56725      */
56726     getConfirmed : function(){
56727         return this.isConfirmed;
56728     },
56729     /**
56730      * 
56731      * @return {Number} this.width
56732      */
56733     getWidth : function(){
56734         return this.width;
56735     },
56736     /**
56737      * 
56738      * @return {Number} this.height
56739      */
56740     getHeight : function(){
56741         return this.height;
56742     },
56743     // private
56744     getSignature : function(){
56745         return this.signatureTmp;
56746     },
56747     // private
56748     reset : function(){
56749         this.signatureTmp = '';
56750         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56751         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
56752         this.isConfirmed = false;
56753         Roo.form.Signature.superclass.reset.call(this);
56754     },
56755     setSignature : function(s){
56756         this.signatureTmp = s;
56757         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
56758         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
56759         this.setValue(s);
56760         this.isConfirmed = false;
56761         Roo.form.Signature.superclass.reset.call(this);
56762     }, 
56763     test : function(){
56764 //        Roo.log(this.signPanel.dom.contentWindow.up())
56765     },
56766     //private
56767     setConfirmed : function(){
56768         
56769         
56770         
56771 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
56772     },
56773     // private
56774     confirmHandler : function(){
56775         if(!this.getSignature()){
56776             return;
56777         }
56778         
56779         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
56780         this.setValue(this.getSignature());
56781         this.isConfirmed = true;
56782         
56783         this.fireEvent('confirm', this);
56784     },
56785     // private
56786     // Subclasses should provide the validation implementation by overriding this
56787     validateValue : function(value){
56788         if(this.allowBlank){
56789             return true;
56790         }
56791         
56792         if(this.isConfirmed){
56793             return true;
56794         }
56795         return false;
56796     }
56797 });/*
56798  * Based on:
56799  * Ext JS Library 1.1.1
56800  * Copyright(c) 2006-2007, Ext JS, LLC.
56801  *
56802  * Originally Released Under LGPL - original licence link has changed is not relivant.
56803  *
56804  * Fork - LGPL
56805  * <script type="text/javascript">
56806  */
56807  
56808
56809 /**
56810  * @class Roo.form.ComboBox
56811  * @extends Roo.form.TriggerField
56812  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
56813  * @constructor
56814  * Create a new ComboBox.
56815  * @param {Object} config Configuration options
56816  */
56817 Roo.form.Select = function(config){
56818     Roo.form.Select.superclass.constructor.call(this, config);
56819      
56820 };
56821
56822 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
56823     /**
56824      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
56825      */
56826     /**
56827      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
56828      * rendering into an Roo.Editor, defaults to false)
56829      */
56830     /**
56831      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
56832      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
56833      */
56834     /**
56835      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
56836      */
56837     /**
56838      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
56839      * the dropdown list (defaults to undefined, with no header element)
56840      */
56841
56842      /**
56843      * @cfg {String/Roo.Template} tpl The template to use to render the output
56844      */
56845      
56846     // private
56847     defaultAutoCreate : {tag: "select"  },
56848     /**
56849      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
56850      */
56851     listWidth: undefined,
56852     /**
56853      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
56854      * mode = 'remote' or 'text' if mode = 'local')
56855      */
56856     displayField: undefined,
56857     /**
56858      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
56859      * mode = 'remote' or 'value' if mode = 'local'). 
56860      * Note: use of a valueField requires the user make a selection
56861      * in order for a value to be mapped.
56862      */
56863     valueField: undefined,
56864     
56865     
56866     /**
56867      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
56868      * field's data value (defaults to the underlying DOM element's name)
56869      */
56870     hiddenName: undefined,
56871     /**
56872      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
56873      */
56874     listClass: '',
56875     /**
56876      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
56877      */
56878     selectedClass: 'x-combo-selected',
56879     /**
56880      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
56881      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
56882      * which displays a downward arrow icon).
56883      */
56884     triggerClass : 'x-form-arrow-trigger',
56885     /**
56886      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
56887      */
56888     shadow:'sides',
56889     /**
56890      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
56891      * anchor positions (defaults to 'tl-bl')
56892      */
56893     listAlign: 'tl-bl?',
56894     /**
56895      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
56896      */
56897     maxHeight: 300,
56898     /**
56899      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
56900      * query specified by the allQuery config option (defaults to 'query')
56901      */
56902     triggerAction: 'query',
56903     /**
56904      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
56905      * (defaults to 4, does not apply if editable = false)
56906      */
56907     minChars : 4,
56908     /**
56909      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
56910      * delay (typeAheadDelay) if it matches a known value (defaults to false)
56911      */
56912     typeAhead: false,
56913     /**
56914      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
56915      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
56916      */
56917     queryDelay: 500,
56918     /**
56919      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
56920      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
56921      */
56922     pageSize: 0,
56923     /**
56924      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
56925      * when editable = true (defaults to false)
56926      */
56927     selectOnFocus:false,
56928     /**
56929      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
56930      */
56931     queryParam: 'query',
56932     /**
56933      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
56934      * when mode = 'remote' (defaults to 'Loading...')
56935      */
56936     loadingText: 'Loading...',
56937     /**
56938      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
56939      */
56940     resizable: false,
56941     /**
56942      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
56943      */
56944     handleHeight : 8,
56945     /**
56946      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
56947      * traditional select (defaults to true)
56948      */
56949     editable: true,
56950     /**
56951      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
56952      */
56953     allQuery: '',
56954     /**
56955      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
56956      */
56957     mode: 'remote',
56958     /**
56959      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
56960      * listWidth has a higher value)
56961      */
56962     minListWidth : 70,
56963     /**
56964      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
56965      * allow the user to set arbitrary text into the field (defaults to false)
56966      */
56967     forceSelection:false,
56968     /**
56969      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
56970      * if typeAhead = true (defaults to 250)
56971      */
56972     typeAheadDelay : 250,
56973     /**
56974      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
56975      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
56976      */
56977     valueNotFoundText : undefined,
56978     
56979     /**
56980      * @cfg {String} defaultValue The value displayed after loading the store.
56981      */
56982     defaultValue: '',
56983     
56984     /**
56985      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
56986      */
56987     blockFocus : false,
56988     
56989     /**
56990      * @cfg {Boolean} disableClear Disable showing of clear button.
56991      */
56992     disableClear : false,
56993     /**
56994      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
56995      */
56996     alwaysQuery : false,
56997     
56998     //private
56999     addicon : false,
57000     editicon: false,
57001     
57002     // element that contains real text value.. (when hidden is used..)
57003      
57004     // private
57005     onRender : function(ct, position){
57006         Roo.form.Field.prototype.onRender.call(this, ct, position);
57007         
57008         if(this.store){
57009             this.store.on('beforeload', this.onBeforeLoad, this);
57010             this.store.on('load', this.onLoad, this);
57011             this.store.on('loadexception', this.onLoadException, this);
57012             this.store.load({});
57013         }
57014         
57015         
57016         
57017     },
57018
57019     // private
57020     initEvents : function(){
57021         //Roo.form.ComboBox.superclass.initEvents.call(this);
57022  
57023     },
57024
57025     onDestroy : function(){
57026        
57027         if(this.store){
57028             this.store.un('beforeload', this.onBeforeLoad, this);
57029             this.store.un('load', this.onLoad, this);
57030             this.store.un('loadexception', this.onLoadException, this);
57031         }
57032         //Roo.form.ComboBox.superclass.onDestroy.call(this);
57033     },
57034
57035     // private
57036     fireKey : function(e){
57037         if(e.isNavKeyPress() && !this.list.isVisible()){
57038             this.fireEvent("specialkey", this, e);
57039         }
57040     },
57041
57042     // private
57043     onResize: function(w, h){
57044         
57045         return; 
57046     
57047         
57048     },
57049
57050     /**
57051      * Allow or prevent the user from directly editing the field text.  If false is passed,
57052      * the user will only be able to select from the items defined in the dropdown list.  This method
57053      * is the runtime equivalent of setting the 'editable' config option at config time.
57054      * @param {Boolean} value True to allow the user to directly edit the field text
57055      */
57056     setEditable : function(value){
57057          
57058     },
57059
57060     // private
57061     onBeforeLoad : function(){
57062         
57063         Roo.log("Select before load");
57064         return;
57065     
57066         this.innerList.update(this.loadingText ?
57067                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
57068         //this.restrictHeight();
57069         this.selectedIndex = -1;
57070     },
57071
57072     // private
57073     onLoad : function(){
57074
57075     
57076         var dom = this.el.dom;
57077         dom.innerHTML = '';
57078          var od = dom.ownerDocument;
57079          
57080         if (this.emptyText) {
57081             var op = od.createElement('option');
57082             op.setAttribute('value', '');
57083             op.innerHTML = String.format('{0}', this.emptyText);
57084             dom.appendChild(op);
57085         }
57086         if(this.store.getCount() > 0){
57087            
57088             var vf = this.valueField;
57089             var df = this.displayField;
57090             this.store.data.each(function(r) {
57091                 // which colmsn to use... testing - cdoe / title..
57092                 var op = od.createElement('option');
57093                 op.setAttribute('value', r.data[vf]);
57094                 op.innerHTML = String.format('{0}', r.data[df]);
57095                 dom.appendChild(op);
57096             });
57097             if (typeof(this.defaultValue != 'undefined')) {
57098                 this.setValue(this.defaultValue);
57099             }
57100             
57101              
57102         }else{
57103             //this.onEmptyResults();
57104         }
57105         //this.el.focus();
57106     },
57107     // private
57108     onLoadException : function()
57109     {
57110         dom.innerHTML = '';
57111             
57112         Roo.log("Select on load exception");
57113         return;
57114     
57115         this.collapse();
57116         Roo.log(this.store.reader.jsonData);
57117         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57118             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57119         }
57120         
57121         
57122     },
57123     // private
57124     onTypeAhead : function(){
57125          
57126     },
57127
57128     // private
57129     onSelect : function(record, index){
57130         Roo.log('on select?');
57131         return;
57132         if(this.fireEvent('beforeselect', this, record, index) !== false){
57133             this.setFromData(index > -1 ? record.data : false);
57134             this.collapse();
57135             this.fireEvent('select', this, record, index);
57136         }
57137     },
57138
57139     /**
57140      * Returns the currently selected field value or empty string if no value is set.
57141      * @return {String} value The selected value
57142      */
57143     getValue : function(){
57144         var dom = this.el.dom;
57145         this.value = dom.options[dom.selectedIndex].value;
57146         return this.value;
57147         
57148     },
57149
57150     /**
57151      * Clears any text/value currently set in the field
57152      */
57153     clearValue : function(){
57154         this.value = '';
57155         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
57156         
57157     },
57158
57159     /**
57160      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
57161      * will be displayed in the field.  If the value does not match the data value of an existing item,
57162      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
57163      * Otherwise the field will be blank (although the value will still be set).
57164      * @param {String} value The value to match
57165      */
57166     setValue : function(v){
57167         var d = this.el.dom;
57168         for (var i =0; i < d.options.length;i++) {
57169             if (v == d.options[i].value) {
57170                 d.selectedIndex = i;
57171                 this.value = v;
57172                 return;
57173             }
57174         }
57175         this.clearValue();
57176     },
57177     /**
57178      * @property {Object} the last set data for the element
57179      */
57180     
57181     lastData : false,
57182     /**
57183      * Sets the value of the field based on a object which is related to the record format for the store.
57184      * @param {Object} value the value to set as. or false on reset?
57185      */
57186     setFromData : function(o){
57187         Roo.log('setfrom data?');
57188          
57189         
57190         
57191     },
57192     // private
57193     reset : function(){
57194         this.clearValue();
57195     },
57196     // private
57197     findRecord : function(prop, value){
57198         
57199         return false;
57200     
57201         var record;
57202         if(this.store.getCount() > 0){
57203             this.store.each(function(r){
57204                 if(r.data[prop] == value){
57205                     record = r;
57206                     return false;
57207                 }
57208                 return true;
57209             });
57210         }
57211         return record;
57212     },
57213     
57214     getName: function()
57215     {
57216         // returns hidden if it's set..
57217         if (!this.rendered) {return ''};
57218         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
57219         
57220     },
57221      
57222
57223     
57224
57225     // private
57226     onEmptyResults : function(){
57227         Roo.log('empty results');
57228         //this.collapse();
57229     },
57230
57231     /**
57232      * Returns true if the dropdown list is expanded, else false.
57233      */
57234     isExpanded : function(){
57235         return false;
57236     },
57237
57238     /**
57239      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
57240      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57241      * @param {String} value The data value of the item to select
57242      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57243      * selected item if it is not currently in view (defaults to true)
57244      * @return {Boolean} True if the value matched an item in the list, else false
57245      */
57246     selectByValue : function(v, scrollIntoView){
57247         Roo.log('select By Value');
57248         return false;
57249     
57250         if(v !== undefined && v !== null){
57251             var r = this.findRecord(this.valueField || this.displayField, v);
57252             if(r){
57253                 this.select(this.store.indexOf(r), scrollIntoView);
57254                 return true;
57255             }
57256         }
57257         return false;
57258     },
57259
57260     /**
57261      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
57262      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57263      * @param {Number} index The zero-based index of the list item to select
57264      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57265      * selected item if it is not currently in view (defaults to true)
57266      */
57267     select : function(index, scrollIntoView){
57268         Roo.log('select ');
57269         return  ;
57270         
57271         this.selectedIndex = index;
57272         this.view.select(index);
57273         if(scrollIntoView !== false){
57274             var el = this.view.getNode(index);
57275             if(el){
57276                 this.innerList.scrollChildIntoView(el, false);
57277             }
57278         }
57279     },
57280
57281       
57282
57283     // private
57284     validateBlur : function(){
57285         
57286         return;
57287         
57288     },
57289
57290     // private
57291     initQuery : function(){
57292         this.doQuery(this.getRawValue());
57293     },
57294
57295     // private
57296     doForce : function(){
57297         if(this.el.dom.value.length > 0){
57298             this.el.dom.value =
57299                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
57300              
57301         }
57302     },
57303
57304     /**
57305      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
57306      * query allowing the query action to be canceled if needed.
57307      * @param {String} query The SQL query to execute
57308      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
57309      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
57310      * saved in the current store (defaults to false)
57311      */
57312     doQuery : function(q, forceAll){
57313         
57314         Roo.log('doQuery?');
57315         if(q === undefined || q === null){
57316             q = '';
57317         }
57318         var qe = {
57319             query: q,
57320             forceAll: forceAll,
57321             combo: this,
57322             cancel:false
57323         };
57324         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
57325             return false;
57326         }
57327         q = qe.query;
57328         forceAll = qe.forceAll;
57329         if(forceAll === true || (q.length >= this.minChars)){
57330             if(this.lastQuery != q || this.alwaysQuery){
57331                 this.lastQuery = q;
57332                 if(this.mode == 'local'){
57333                     this.selectedIndex = -1;
57334                     if(forceAll){
57335                         this.store.clearFilter();
57336                     }else{
57337                         this.store.filter(this.displayField, q);
57338                     }
57339                     this.onLoad();
57340                 }else{
57341                     this.store.baseParams[this.queryParam] = q;
57342                     this.store.load({
57343                         params: this.getParams(q)
57344                     });
57345                     this.expand();
57346                 }
57347             }else{
57348                 this.selectedIndex = -1;
57349                 this.onLoad();   
57350             }
57351         }
57352     },
57353
57354     // private
57355     getParams : function(q){
57356         var p = {};
57357         //p[this.queryParam] = q;
57358         if(this.pageSize){
57359             p.start = 0;
57360             p.limit = this.pageSize;
57361         }
57362         return p;
57363     },
57364
57365     /**
57366      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
57367      */
57368     collapse : function(){
57369         
57370     },
57371
57372     // private
57373     collapseIf : function(e){
57374         
57375     },
57376
57377     /**
57378      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
57379      */
57380     expand : function(){
57381         
57382     } ,
57383
57384     // private
57385      
57386
57387     /** 
57388     * @cfg {Boolean} grow 
57389     * @hide 
57390     */
57391     /** 
57392     * @cfg {Number} growMin 
57393     * @hide 
57394     */
57395     /** 
57396     * @cfg {Number} growMax 
57397     * @hide 
57398     */
57399     /**
57400      * @hide
57401      * @method autoSize
57402      */
57403     
57404     setWidth : function()
57405     {
57406         
57407     },
57408     getResizeEl : function(){
57409         return this.el;
57410     }
57411 });//<script type="text/javasscript">
57412  
57413
57414 /**
57415  * @class Roo.DDView
57416  * A DnD enabled version of Roo.View.
57417  * @param {Element/String} container The Element in which to create the View.
57418  * @param {String} tpl The template string used to create the markup for each element of the View
57419  * @param {Object} config The configuration properties. These include all the config options of
57420  * {@link Roo.View} plus some specific to this class.<br>
57421  * <p>
57422  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
57423  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
57424  * <p>
57425  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
57426 .x-view-drag-insert-above {
57427         border-top:1px dotted #3366cc;
57428 }
57429 .x-view-drag-insert-below {
57430         border-bottom:1px dotted #3366cc;
57431 }
57432 </code></pre>
57433  * 
57434  */
57435  
57436 Roo.DDView = function(container, tpl, config) {
57437     Roo.DDView.superclass.constructor.apply(this, arguments);
57438     this.getEl().setStyle("outline", "0px none");
57439     this.getEl().unselectable();
57440     if (this.dragGroup) {
57441         this.setDraggable(this.dragGroup.split(","));
57442     }
57443     if (this.dropGroup) {
57444         this.setDroppable(this.dropGroup.split(","));
57445     }
57446     if (this.deletable) {
57447         this.setDeletable();
57448     }
57449     this.isDirtyFlag = false;
57450         this.addEvents({
57451                 "drop" : true
57452         });
57453 };
57454
57455 Roo.extend(Roo.DDView, Roo.View, {
57456 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
57457 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
57458 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
57459 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
57460
57461         isFormField: true,
57462
57463         reset: Roo.emptyFn,
57464         
57465         clearInvalid: Roo.form.Field.prototype.clearInvalid,
57466
57467         validate: function() {
57468                 return true;
57469         },
57470         
57471         destroy: function() {
57472                 this.purgeListeners();
57473                 this.getEl.removeAllListeners();
57474                 this.getEl().remove();
57475                 if (this.dragZone) {
57476                         if (this.dragZone.destroy) {
57477                                 this.dragZone.destroy();
57478                         }
57479                 }
57480                 if (this.dropZone) {
57481                         if (this.dropZone.destroy) {
57482                                 this.dropZone.destroy();
57483                         }
57484                 }
57485         },
57486
57487 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
57488         getName: function() {
57489                 return this.name;
57490         },
57491
57492 /**     Loads the View from a JSON string representing the Records to put into the Store. */
57493         setValue: function(v) {
57494                 if (!this.store) {
57495                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
57496                 }
57497                 var data = {};
57498                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
57499                 this.store.proxy = new Roo.data.MemoryProxy(data);
57500                 this.store.load();
57501         },
57502
57503 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
57504         getValue: function() {
57505                 var result = '(';
57506                 this.store.each(function(rec) {
57507                         result += rec.id + ',';
57508                 });
57509                 return result.substr(0, result.length - 1) + ')';
57510         },
57511         
57512         getIds: function() {
57513                 var i = 0, result = new Array(this.store.getCount());
57514                 this.store.each(function(rec) {
57515                         result[i++] = rec.id;
57516                 });
57517                 return result;
57518         },
57519         
57520         isDirty: function() {
57521                 return this.isDirtyFlag;
57522         },
57523
57524 /**
57525  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
57526  *      whole Element becomes the target, and this causes the drop gesture to append.
57527  */
57528     getTargetFromEvent : function(e) {
57529                 var target = e.getTarget();
57530                 while ((target !== null) && (target.parentNode != this.el.dom)) {
57531                 target = target.parentNode;
57532                 }
57533                 if (!target) {
57534                         target = this.el.dom.lastChild || this.el.dom;
57535                 }
57536                 return target;
57537     },
57538
57539 /**
57540  *      Create the drag data which consists of an object which has the property "ddel" as
57541  *      the drag proxy element. 
57542  */
57543     getDragData : function(e) {
57544         var target = this.findItemFromChild(e.getTarget());
57545                 if(target) {
57546                         this.handleSelection(e);
57547                         var selNodes = this.getSelectedNodes();
57548             var dragData = {
57549                 source: this,
57550                 copy: this.copy || (this.allowCopy && e.ctrlKey),
57551                 nodes: selNodes,
57552                 records: []
57553                         };
57554                         var selectedIndices = this.getSelectedIndexes();
57555                         for (var i = 0; i < selectedIndices.length; i++) {
57556                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
57557                         }
57558                         if (selNodes.length == 1) {
57559                                 dragData.ddel = target.cloneNode(true); // the div element
57560                         } else {
57561                                 var div = document.createElement('div'); // create the multi element drag "ghost"
57562                                 div.className = 'multi-proxy';
57563                                 for (var i = 0, len = selNodes.length; i < len; i++) {
57564                                         div.appendChild(selNodes[i].cloneNode(true));
57565                                 }
57566                                 dragData.ddel = div;
57567                         }
57568             //console.log(dragData)
57569             //console.log(dragData.ddel.innerHTML)
57570                         return dragData;
57571                 }
57572         //console.log('nodragData')
57573                 return false;
57574     },
57575     
57576 /**     Specify to which ddGroup items in this DDView may be dragged. */
57577     setDraggable: function(ddGroup) {
57578         if (ddGroup instanceof Array) {
57579                 Roo.each(ddGroup, this.setDraggable, this);
57580                 return;
57581         }
57582         if (this.dragZone) {
57583                 this.dragZone.addToGroup(ddGroup);
57584         } else {
57585                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
57586                                 containerScroll: true,
57587                                 ddGroup: ddGroup 
57588
57589                         });
57590 //                      Draggability implies selection. DragZone's mousedown selects the element.
57591                         if (!this.multiSelect) { this.singleSelect = true; }
57592
57593 //                      Wire the DragZone's handlers up to methods in *this*
57594                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
57595                 }
57596     },
57597
57598 /**     Specify from which ddGroup this DDView accepts drops. */
57599     setDroppable: function(ddGroup) {
57600         if (ddGroup instanceof Array) {
57601                 Roo.each(ddGroup, this.setDroppable, this);
57602                 return;
57603         }
57604         if (this.dropZone) {
57605                 this.dropZone.addToGroup(ddGroup);
57606         } else {
57607                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
57608                                 containerScroll: true,
57609                                 ddGroup: ddGroup
57610                         });
57611
57612 //                      Wire the DropZone's handlers up to methods in *this*
57613                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
57614                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
57615                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
57616                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
57617                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
57618                 }
57619     },
57620
57621 /**     Decide whether to drop above or below a View node. */
57622     getDropPoint : function(e, n, dd){
57623         if (n == this.el.dom) { return "above"; }
57624                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
57625                 var c = t + (b - t) / 2;
57626                 var y = Roo.lib.Event.getPageY(e);
57627                 if(y <= c) {
57628                         return "above";
57629                 }else{
57630                         return "below";
57631                 }
57632     },
57633
57634     onNodeEnter : function(n, dd, e, data){
57635                 return false;
57636     },
57637     
57638     onNodeOver : function(n, dd, e, data){
57639                 var pt = this.getDropPoint(e, n, dd);
57640                 // set the insert point style on the target node
57641                 var dragElClass = this.dropNotAllowed;
57642                 if (pt) {
57643                         var targetElClass;
57644                         if (pt == "above"){
57645                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
57646                                 targetElClass = "x-view-drag-insert-above";
57647                         } else {
57648                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
57649                                 targetElClass = "x-view-drag-insert-below";
57650                         }
57651                         if (this.lastInsertClass != targetElClass){
57652                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
57653                                 this.lastInsertClass = targetElClass;
57654                         }
57655                 }
57656                 return dragElClass;
57657         },
57658
57659     onNodeOut : function(n, dd, e, data){
57660                 this.removeDropIndicators(n);
57661     },
57662
57663     onNodeDrop : function(n, dd, e, data){
57664         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
57665                 return false;
57666         }
57667         var pt = this.getDropPoint(e, n, dd);
57668                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
57669                 if (pt == "below") { insertAt++; }
57670                 for (var i = 0; i < data.records.length; i++) {
57671                         var r = data.records[i];
57672                         var dup = this.store.getById(r.id);
57673                         if (dup && (dd != this.dragZone)) {
57674                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
57675                         } else {
57676                                 if (data.copy) {
57677                                         this.store.insert(insertAt++, r.copy());
57678                                 } else {
57679                                         data.source.isDirtyFlag = true;
57680                                         r.store.remove(r);
57681                                         this.store.insert(insertAt++, r);
57682                                 }
57683                                 this.isDirtyFlag = true;
57684                         }
57685                 }
57686                 this.dragZone.cachedTarget = null;
57687                 return true;
57688     },
57689
57690     removeDropIndicators : function(n){
57691                 if(n){
57692                         Roo.fly(n).removeClass([
57693                                 "x-view-drag-insert-above",
57694                                 "x-view-drag-insert-below"]);
57695                         this.lastInsertClass = "_noclass";
57696                 }
57697     },
57698
57699 /**
57700  *      Utility method. Add a delete option to the DDView's context menu.
57701  *      @param {String} imageUrl The URL of the "delete" icon image.
57702  */
57703         setDeletable: function(imageUrl) {
57704                 if (!this.singleSelect && !this.multiSelect) {
57705                         this.singleSelect = true;
57706                 }
57707                 var c = this.getContextMenu();
57708                 this.contextMenu.on("itemclick", function(item) {
57709                         switch (item.id) {
57710                                 case "delete":
57711                                         this.remove(this.getSelectedIndexes());
57712                                         break;
57713                         }
57714                 }, this);
57715                 this.contextMenu.add({
57716                         icon: imageUrl,
57717                         id: "delete",
57718                         text: 'Delete'
57719                 });
57720         },
57721         
57722 /**     Return the context menu for this DDView. */
57723         getContextMenu: function() {
57724                 if (!this.contextMenu) {
57725 //                      Create the View's context menu
57726                         this.contextMenu = new Roo.menu.Menu({
57727                                 id: this.id + "-contextmenu"
57728                         });
57729                         this.el.on("contextmenu", this.showContextMenu, this);
57730                 }
57731                 return this.contextMenu;
57732         },
57733         
57734         disableContextMenu: function() {
57735                 if (this.contextMenu) {
57736                         this.el.un("contextmenu", this.showContextMenu, this);
57737                 }
57738         },
57739
57740         showContextMenu: function(e, item) {
57741         item = this.findItemFromChild(e.getTarget());
57742                 if (item) {
57743                         e.stopEvent();
57744                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
57745                         this.contextMenu.showAt(e.getXY());
57746             }
57747     },
57748
57749 /**
57750  *      Remove {@link Roo.data.Record}s at the specified indices.
57751  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
57752  */
57753     remove: function(selectedIndices) {
57754                 selectedIndices = [].concat(selectedIndices);
57755                 for (var i = 0; i < selectedIndices.length; i++) {
57756                         var rec = this.store.getAt(selectedIndices[i]);
57757                         this.store.remove(rec);
57758                 }
57759     },
57760
57761 /**
57762  *      Double click fires the event, but also, if this is draggable, and there is only one other
57763  *      related DropZone, it transfers the selected node.
57764  */
57765     onDblClick : function(e){
57766         var item = this.findItemFromChild(e.getTarget());
57767         if(item){
57768             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
57769                 return false;
57770             }
57771             if (this.dragGroup) {
57772                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
57773                     while (targets.indexOf(this.dropZone) > -1) {
57774                             targets.remove(this.dropZone);
57775                                 }
57776                     if (targets.length == 1) {
57777                                         this.dragZone.cachedTarget = null;
57778                         var el = Roo.get(targets[0].getEl());
57779                         var box = el.getBox(true);
57780                         targets[0].onNodeDrop(el.dom, {
57781                                 target: el.dom,
57782                                 xy: [box.x, box.y + box.height - 1]
57783                         }, null, this.getDragData(e));
57784                     }
57785                 }
57786         }
57787     },
57788     
57789     handleSelection: function(e) {
57790                 this.dragZone.cachedTarget = null;
57791         var item = this.findItemFromChild(e.getTarget());
57792         if (!item) {
57793                 this.clearSelections(true);
57794                 return;
57795         }
57796                 if (item && (this.multiSelect || this.singleSelect)){
57797                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
57798                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
57799                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
57800                                 this.unselect(item);
57801                         } else {
57802                                 this.select(item, this.multiSelect && e.ctrlKey);
57803                                 this.lastSelection = item;
57804                         }
57805                 }
57806     },
57807
57808     onItemClick : function(item, index, e){
57809                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
57810                         return false;
57811                 }
57812                 return true;
57813     },
57814
57815     unselect : function(nodeInfo, suppressEvent){
57816                 var node = this.getNode(nodeInfo);
57817                 if(node && this.isSelected(node)){
57818                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
57819                                 Roo.fly(node).removeClass(this.selectedClass);
57820                                 this.selections.remove(node);
57821                                 if(!suppressEvent){
57822                                         this.fireEvent("selectionchange", this, this.selections);
57823                                 }
57824                         }
57825                 }
57826     }
57827 });
57828 /*
57829  * Based on:
57830  * Ext JS Library 1.1.1
57831  * Copyright(c) 2006-2007, Ext JS, LLC.
57832  *
57833  * Originally Released Under LGPL - original licence link has changed is not relivant.
57834  *
57835  * Fork - LGPL
57836  * <script type="text/javascript">
57837  */
57838  
57839 /**
57840  * @class Roo.LayoutManager
57841  * @extends Roo.util.Observable
57842  * Base class for layout managers.
57843  */
57844 Roo.LayoutManager = function(container, config){
57845     Roo.LayoutManager.superclass.constructor.call(this);
57846     this.el = Roo.get(container);
57847     // ie scrollbar fix
57848     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
57849         document.body.scroll = "no";
57850     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
57851         this.el.position('relative');
57852     }
57853     this.id = this.el.id;
57854     this.el.addClass("x-layout-container");
57855     /** false to disable window resize monitoring @type Boolean */
57856     this.monitorWindowResize = true;
57857     this.regions = {};
57858     this.addEvents({
57859         /**
57860          * @event layout
57861          * Fires when a layout is performed. 
57862          * @param {Roo.LayoutManager} this
57863          */
57864         "layout" : true,
57865         /**
57866          * @event regionresized
57867          * Fires when the user resizes a region. 
57868          * @param {Roo.LayoutRegion} region The resized region
57869          * @param {Number} newSize The new size (width for east/west, height for north/south)
57870          */
57871         "regionresized" : true,
57872         /**
57873          * @event regioncollapsed
57874          * Fires when a region is collapsed. 
57875          * @param {Roo.LayoutRegion} region The collapsed region
57876          */
57877         "regioncollapsed" : true,
57878         /**
57879          * @event regionexpanded
57880          * Fires when a region is expanded.  
57881          * @param {Roo.LayoutRegion} region The expanded region
57882          */
57883         "regionexpanded" : true
57884     });
57885     this.updating = false;
57886     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57887 };
57888
57889 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
57890     /**
57891      * Returns true if this layout is currently being updated
57892      * @return {Boolean}
57893      */
57894     isUpdating : function(){
57895         return this.updating; 
57896     },
57897     
57898     /**
57899      * Suspend the LayoutManager from doing auto-layouts while
57900      * making multiple add or remove calls
57901      */
57902     beginUpdate : function(){
57903         this.updating = true;    
57904     },
57905     
57906     /**
57907      * Restore auto-layouts and optionally disable the manager from performing a layout
57908      * @param {Boolean} noLayout true to disable a layout update 
57909      */
57910     endUpdate : function(noLayout){
57911         this.updating = false;
57912         if(!noLayout){
57913             this.layout();
57914         }    
57915     },
57916     
57917     layout: function(){
57918         
57919     },
57920     
57921     onRegionResized : function(region, newSize){
57922         this.fireEvent("regionresized", region, newSize);
57923         this.layout();
57924     },
57925     
57926     onRegionCollapsed : function(region){
57927         this.fireEvent("regioncollapsed", region);
57928     },
57929     
57930     onRegionExpanded : function(region){
57931         this.fireEvent("regionexpanded", region);
57932     },
57933         
57934     /**
57935      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
57936      * performs box-model adjustments.
57937      * @return {Object} The size as an object {width: (the width), height: (the height)}
57938      */
57939     getViewSize : function(){
57940         var size;
57941         if(this.el.dom != document.body){
57942             size = this.el.getSize();
57943         }else{
57944             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
57945         }
57946         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
57947         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
57948         return size;
57949     },
57950     
57951     /**
57952      * Returns the Element this layout is bound to.
57953      * @return {Roo.Element}
57954      */
57955     getEl : function(){
57956         return this.el;
57957     },
57958     
57959     /**
57960      * Returns the specified region.
57961      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
57962      * @return {Roo.LayoutRegion}
57963      */
57964     getRegion : function(target){
57965         return this.regions[target.toLowerCase()];
57966     },
57967     
57968     onWindowResize : function(){
57969         if(this.monitorWindowResize){
57970             this.layout();
57971         }
57972     }
57973 });/*
57974  * Based on:
57975  * Ext JS Library 1.1.1
57976  * Copyright(c) 2006-2007, Ext JS, LLC.
57977  *
57978  * Originally Released Under LGPL - original licence link has changed is not relivant.
57979  *
57980  * Fork - LGPL
57981  * <script type="text/javascript">
57982  */
57983 /**
57984  * @class Roo.BorderLayout
57985  * @extends Roo.LayoutManager
57986  * @children Roo.ContentPanel
57987  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
57988  * please see: <br><br>
57989  * <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>
57990  * <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>
57991  * Example:
57992  <pre><code>
57993  var layout = new Roo.BorderLayout(document.body, {
57994     north: {
57995         initialSize: 25,
57996         titlebar: false
57997     },
57998     west: {
57999         split:true,
58000         initialSize: 200,
58001         minSize: 175,
58002         maxSize: 400,
58003         titlebar: true,
58004         collapsible: true
58005     },
58006     east: {
58007         split:true,
58008         initialSize: 202,
58009         minSize: 175,
58010         maxSize: 400,
58011         titlebar: true,
58012         collapsible: true
58013     },
58014     south: {
58015         split:true,
58016         initialSize: 100,
58017         minSize: 100,
58018         maxSize: 200,
58019         titlebar: true,
58020         collapsible: true
58021     },
58022     center: {
58023         titlebar: true,
58024         autoScroll:true,
58025         resizeTabs: true,
58026         minTabWidth: 50,
58027         preferredTabWidth: 150
58028     }
58029 });
58030
58031 // shorthand
58032 var CP = Roo.ContentPanel;
58033
58034 layout.beginUpdate();
58035 layout.add("north", new CP("north", "North"));
58036 layout.add("south", new CP("south", {title: "South", closable: true}));
58037 layout.add("west", new CP("west", {title: "West"}));
58038 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
58039 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
58040 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
58041 layout.getRegion("center").showPanel("center1");
58042 layout.endUpdate();
58043 </code></pre>
58044
58045 <b>The container the layout is rendered into can be either the body element or any other element.
58046 If it is not the body element, the container needs to either be an absolute positioned element,
58047 or you will need to add "position:relative" to the css of the container.  You will also need to specify
58048 the container size if it is not the body element.</b>
58049
58050 * @constructor
58051 * Create a new BorderLayout
58052 * @param {String/HTMLElement/Element} container The container this layout is bound to
58053 * @param {Object} config Configuration options
58054  */
58055 Roo.BorderLayout = function(container, config){
58056     config = config || {};
58057     Roo.BorderLayout.superclass.constructor.call(this, container, config);
58058     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
58059     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
58060         var target = this.factory.validRegions[i];
58061         if(config[target]){
58062             this.addRegion(target, config[target]);
58063         }
58064     }
58065 };
58066
58067 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
58068         
58069         /**
58070          * @cfg {Roo.LayoutRegion} east
58071          */
58072         /**
58073          * @cfg {Roo.LayoutRegion} west
58074          */
58075         /**
58076          * @cfg {Roo.LayoutRegion} north
58077          */
58078         /**
58079          * @cfg {Roo.LayoutRegion} south
58080          */
58081         /**
58082          * @cfg {Roo.LayoutRegion} center
58083          */
58084     /**
58085      * Creates and adds a new region if it doesn't already exist.
58086      * @param {String} target The target region key (north, south, east, west or center).
58087      * @param {Object} config The regions config object
58088      * @return {BorderLayoutRegion} The new region
58089      */
58090     addRegion : function(target, config){
58091         if(!this.regions[target]){
58092             var r = this.factory.create(target, this, config);
58093             this.bindRegion(target, r);
58094         }
58095         return this.regions[target];
58096     },
58097
58098     // private (kinda)
58099     bindRegion : function(name, r){
58100         this.regions[name] = r;
58101         r.on("visibilitychange", this.layout, this);
58102         r.on("paneladded", this.layout, this);
58103         r.on("panelremoved", this.layout, this);
58104         r.on("invalidated", this.layout, this);
58105         r.on("resized", this.onRegionResized, this);
58106         r.on("collapsed", this.onRegionCollapsed, this);
58107         r.on("expanded", this.onRegionExpanded, this);
58108     },
58109
58110     /**
58111      * Performs a layout update.
58112      */
58113     layout : function(){
58114         if(this.updating) {
58115             return;
58116         }
58117         var size = this.getViewSize();
58118         var w = size.width;
58119         var h = size.height;
58120         var centerW = w;
58121         var centerH = h;
58122         var centerY = 0;
58123         var centerX = 0;
58124         //var x = 0, y = 0;
58125
58126         var rs = this.regions;
58127         var north = rs["north"];
58128         var south = rs["south"]; 
58129         var west = rs["west"];
58130         var east = rs["east"];
58131         var center = rs["center"];
58132         //if(this.hideOnLayout){ // not supported anymore
58133             //c.el.setStyle("display", "none");
58134         //}
58135         if(north && north.isVisible()){
58136             var b = north.getBox();
58137             var m = north.getMargins();
58138             b.width = w - (m.left+m.right);
58139             b.x = m.left;
58140             b.y = m.top;
58141             centerY = b.height + b.y + m.bottom;
58142             centerH -= centerY;
58143             north.updateBox(this.safeBox(b));
58144         }
58145         if(south && south.isVisible()){
58146             var b = south.getBox();
58147             var m = south.getMargins();
58148             b.width = w - (m.left+m.right);
58149             b.x = m.left;
58150             var totalHeight = (b.height + m.top + m.bottom);
58151             b.y = h - totalHeight + m.top;
58152             centerH -= totalHeight;
58153             south.updateBox(this.safeBox(b));
58154         }
58155         if(west && west.isVisible()){
58156             var b = west.getBox();
58157             var m = west.getMargins();
58158             b.height = centerH - (m.top+m.bottom);
58159             b.x = m.left;
58160             b.y = centerY + m.top;
58161             var totalWidth = (b.width + m.left + m.right);
58162             centerX += totalWidth;
58163             centerW -= totalWidth;
58164             west.updateBox(this.safeBox(b));
58165         }
58166         if(east && east.isVisible()){
58167             var b = east.getBox();
58168             var m = east.getMargins();
58169             b.height = centerH - (m.top+m.bottom);
58170             var totalWidth = (b.width + m.left + m.right);
58171             b.x = w - totalWidth + m.left;
58172             b.y = centerY + m.top;
58173             centerW -= totalWidth;
58174             east.updateBox(this.safeBox(b));
58175         }
58176         if(center){
58177             var m = center.getMargins();
58178             var centerBox = {
58179                 x: centerX + m.left,
58180                 y: centerY + m.top,
58181                 width: centerW - (m.left+m.right),
58182                 height: centerH - (m.top+m.bottom)
58183             };
58184             //if(this.hideOnLayout){
58185                 //center.el.setStyle("display", "block");
58186             //}
58187             center.updateBox(this.safeBox(centerBox));
58188         }
58189         this.el.repaint();
58190         this.fireEvent("layout", this);
58191     },
58192
58193     // private
58194     safeBox : function(box){
58195         box.width = Math.max(0, box.width);
58196         box.height = Math.max(0, box.height);
58197         return box;
58198     },
58199
58200     /**
58201      * Adds a ContentPanel (or subclass) to this layout.
58202      * @param {String} target The target region key (north, south, east, west or center).
58203      * @param {Roo.ContentPanel} panel The panel to add
58204      * @return {Roo.ContentPanel} The added panel
58205      */
58206     add : function(target, panel){
58207          
58208         target = target.toLowerCase();
58209         return this.regions[target].add(panel);
58210     },
58211
58212     /**
58213      * Remove a ContentPanel (or subclass) to this layout.
58214      * @param {String} target The target region key (north, south, east, west or center).
58215      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
58216      * @return {Roo.ContentPanel} The removed panel
58217      */
58218     remove : function(target, panel){
58219         target = target.toLowerCase();
58220         return this.regions[target].remove(panel);
58221     },
58222
58223     /**
58224      * Searches all regions for a panel with the specified id
58225      * @param {String} panelId
58226      * @return {Roo.ContentPanel} The panel or null if it wasn't found
58227      */
58228     findPanel : function(panelId){
58229         var rs = this.regions;
58230         for(var target in rs){
58231             if(typeof rs[target] != "function"){
58232                 var p = rs[target].getPanel(panelId);
58233                 if(p){
58234                     return p;
58235                 }
58236             }
58237         }
58238         return null;
58239     },
58240
58241     /**
58242      * Searches all regions for a panel with the specified id and activates (shows) it.
58243      * @param {String/ContentPanel} panelId The panels id or the panel itself
58244      * @return {Roo.ContentPanel} The shown panel or null
58245      */
58246     showPanel : function(panelId) {
58247       var rs = this.regions;
58248       for(var target in rs){
58249          var r = rs[target];
58250          if(typeof r != "function"){
58251             if(r.hasPanel(panelId)){
58252                return r.showPanel(panelId);
58253             }
58254          }
58255       }
58256       return null;
58257    },
58258
58259    /**
58260      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
58261      * @param {Roo.state.Provider} provider (optional) An alternate state provider
58262      */
58263     restoreState : function(provider){
58264         if(!provider){
58265             provider = Roo.state.Manager;
58266         }
58267         var sm = new Roo.LayoutStateManager();
58268         sm.init(this, provider);
58269     },
58270
58271     /**
58272      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
58273      * object should contain properties for each region to add ContentPanels to, and each property's value should be
58274      * a valid ContentPanel config object.  Example:
58275      * <pre><code>
58276 // Create the main layout
58277 var layout = new Roo.BorderLayout('main-ct', {
58278     west: {
58279         split:true,
58280         minSize: 175,
58281         titlebar: true
58282     },
58283     center: {
58284         title:'Components'
58285     }
58286 }, 'main-ct');
58287
58288 // Create and add multiple ContentPanels at once via configs
58289 layout.batchAdd({
58290    west: {
58291        id: 'source-files',
58292        autoCreate:true,
58293        title:'Ext Source Files',
58294        autoScroll:true,
58295        fitToFrame:true
58296    },
58297    center : {
58298        el: cview,
58299        autoScroll:true,
58300        fitToFrame:true,
58301        toolbar: tb,
58302        resizeEl:'cbody'
58303    }
58304 });
58305 </code></pre>
58306      * @param {Object} regions An object containing ContentPanel configs by region name
58307      */
58308     batchAdd : function(regions){
58309         this.beginUpdate();
58310         for(var rname in regions){
58311             var lr = this.regions[rname];
58312             if(lr){
58313                 this.addTypedPanels(lr, regions[rname]);
58314             }
58315         }
58316         this.endUpdate();
58317     },
58318
58319     // private
58320     addTypedPanels : function(lr, ps){
58321         if(typeof ps == 'string'){
58322             lr.add(new Roo.ContentPanel(ps));
58323         }
58324         else if(ps instanceof Array){
58325             for(var i =0, len = ps.length; i < len; i++){
58326                 this.addTypedPanels(lr, ps[i]);
58327             }
58328         }
58329         else if(!ps.events){ // raw config?
58330             var el = ps.el;
58331             delete ps.el; // prevent conflict
58332             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
58333         }
58334         else {  // panel object assumed!
58335             lr.add(ps);
58336         }
58337     },
58338     /**
58339      * Adds a xtype elements to the layout.
58340      * <pre><code>
58341
58342 layout.addxtype({
58343        xtype : 'ContentPanel',
58344        region: 'west',
58345        items: [ .... ]
58346    }
58347 );
58348
58349 layout.addxtype({
58350         xtype : 'NestedLayoutPanel',
58351         region: 'west',
58352         layout: {
58353            center: { },
58354            west: { }   
58355         },
58356         items : [ ... list of content panels or nested layout panels.. ]
58357    }
58358 );
58359 </code></pre>
58360      * @param {Object} cfg Xtype definition of item to add.
58361      */
58362     addxtype : function(cfg)
58363     {
58364         // basically accepts a pannel...
58365         // can accept a layout region..!?!?
58366         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
58367         
58368         if (!cfg.xtype.match(/Panel$/)) {
58369             return false;
58370         }
58371         var ret = false;
58372         
58373         if (typeof(cfg.region) == 'undefined') {
58374             Roo.log("Failed to add Panel, region was not set");
58375             Roo.log(cfg);
58376             return false;
58377         }
58378         var region = cfg.region;
58379         delete cfg.region;
58380         
58381           
58382         var xitems = [];
58383         if (cfg.items) {
58384             xitems = cfg.items;
58385             delete cfg.items;
58386         }
58387         var nb = false;
58388         
58389         switch(cfg.xtype) 
58390         {
58391             case 'ContentPanel':  // ContentPanel (el, cfg)
58392             case 'ScrollPanel':  // ContentPanel (el, cfg)
58393             case 'ViewPanel': 
58394                 if(cfg.autoCreate) {
58395                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58396                 } else {
58397                     var el = this.el.createChild();
58398                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
58399                 }
58400                 
58401                 this.add(region, ret);
58402                 break;
58403             
58404             
58405             case 'TreePanel': // our new panel!
58406                 cfg.el = this.el.createChild();
58407                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58408                 this.add(region, ret);
58409                 break;
58410             
58411             case 'NestedLayoutPanel': 
58412                 // create a new Layout (which is  a Border Layout...
58413                 var el = this.el.createChild();
58414                 var clayout = cfg.layout;
58415                 delete cfg.layout;
58416                 clayout.items   = clayout.items  || [];
58417                 // replace this exitems with the clayout ones..
58418                 xitems = clayout.items;
58419                  
58420                 
58421                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
58422                     cfg.background = false;
58423                 }
58424                 var layout = new Roo.BorderLayout(el, clayout);
58425                 
58426                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
58427                 //console.log('adding nested layout panel '  + cfg.toSource());
58428                 this.add(region, ret);
58429                 nb = {}; /// find first...
58430                 break;
58431                 
58432             case 'GridPanel': 
58433             
58434                 // needs grid and region
58435                 
58436                 //var el = this.getRegion(region).el.createChild();
58437                 var el = this.el.createChild();
58438                 // create the grid first...
58439                 
58440                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
58441                 delete cfg.grid;
58442                 if (region == 'center' && this.active ) {
58443                     cfg.background = false;
58444                 }
58445                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
58446                 
58447                 this.add(region, ret);
58448                 if (cfg.background) {
58449                     ret.on('activate', function(gp) {
58450                         if (!gp.grid.rendered) {
58451                             gp.grid.render();
58452                         }
58453                     });
58454                 } else {
58455                     grid.render();
58456                 }
58457                 break;
58458            
58459            
58460            
58461                 
58462                 
58463                 
58464             default:
58465                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
58466                     
58467                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58468                     this.add(region, ret);
58469                 } else {
58470                 
58471                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
58472                     return null;
58473                 }
58474                 
58475              // GridPanel (grid, cfg)
58476             
58477         }
58478         this.beginUpdate();
58479         // add children..
58480         var region = '';
58481         var abn = {};
58482         Roo.each(xitems, function(i)  {
58483             region = nb && i.region ? i.region : false;
58484             
58485             var add = ret.addxtype(i);
58486            
58487             if (region) {
58488                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
58489                 if (!i.background) {
58490                     abn[region] = nb[region] ;
58491                 }
58492             }
58493             
58494         });
58495         this.endUpdate();
58496
58497         // make the last non-background panel active..
58498         //if (nb) { Roo.log(abn); }
58499         if (nb) {
58500             
58501             for(var r in abn) {
58502                 region = this.getRegion(r);
58503                 if (region) {
58504                     // tried using nb[r], but it does not work..
58505                      
58506                     region.showPanel(abn[r]);
58507                    
58508                 }
58509             }
58510         }
58511         return ret;
58512         
58513     }
58514 });
58515
58516 /**
58517  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
58518  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
58519  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
58520  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
58521  * <pre><code>
58522 // shorthand
58523 var CP = Roo.ContentPanel;
58524
58525 var layout = Roo.BorderLayout.create({
58526     north: {
58527         initialSize: 25,
58528         titlebar: false,
58529         panels: [new CP("north", "North")]
58530     },
58531     west: {
58532         split:true,
58533         initialSize: 200,
58534         minSize: 175,
58535         maxSize: 400,
58536         titlebar: true,
58537         collapsible: true,
58538         panels: [new CP("west", {title: "West"})]
58539     },
58540     east: {
58541         split:true,
58542         initialSize: 202,
58543         minSize: 175,
58544         maxSize: 400,
58545         titlebar: true,
58546         collapsible: true,
58547         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
58548     },
58549     south: {
58550         split:true,
58551         initialSize: 100,
58552         minSize: 100,
58553         maxSize: 200,
58554         titlebar: true,
58555         collapsible: true,
58556         panels: [new CP("south", {title: "South", closable: true})]
58557     },
58558     center: {
58559         titlebar: true,
58560         autoScroll:true,
58561         resizeTabs: true,
58562         minTabWidth: 50,
58563         preferredTabWidth: 150,
58564         panels: [
58565             new CP("center1", {title: "Close Me", closable: true}),
58566             new CP("center2", {title: "Center Panel", closable: false})
58567         ]
58568     }
58569 }, document.body);
58570
58571 layout.getRegion("center").showPanel("center1");
58572 </code></pre>
58573  * @param config
58574  * @param targetEl
58575  */
58576 Roo.BorderLayout.create = function(config, targetEl){
58577     var layout = new Roo.BorderLayout(targetEl || document.body, config);
58578     layout.beginUpdate();
58579     var regions = Roo.BorderLayout.RegionFactory.validRegions;
58580     for(var j = 0, jlen = regions.length; j < jlen; j++){
58581         var lr = regions[j];
58582         if(layout.regions[lr] && config[lr].panels){
58583             var r = layout.regions[lr];
58584             var ps = config[lr].panels;
58585             layout.addTypedPanels(r, ps);
58586         }
58587     }
58588     layout.endUpdate();
58589     return layout;
58590 };
58591
58592 // private
58593 Roo.BorderLayout.RegionFactory = {
58594     // private
58595     validRegions : ["north","south","east","west","center"],
58596
58597     // private
58598     create : function(target, mgr, config){
58599         target = target.toLowerCase();
58600         if(config.lightweight || config.basic){
58601             return new Roo.BasicLayoutRegion(mgr, config, target);
58602         }
58603         switch(target){
58604             case "north":
58605                 return new Roo.NorthLayoutRegion(mgr, config);
58606             case "south":
58607                 return new Roo.SouthLayoutRegion(mgr, config);
58608             case "east":
58609                 return new Roo.EastLayoutRegion(mgr, config);
58610             case "west":
58611                 return new Roo.WestLayoutRegion(mgr, config);
58612             case "center":
58613                 return new Roo.CenterLayoutRegion(mgr, config);
58614         }
58615         throw 'Layout region "'+target+'" not supported.';
58616     }
58617 };/*
58618  * Based on:
58619  * Ext JS Library 1.1.1
58620  * Copyright(c) 2006-2007, Ext JS, LLC.
58621  *
58622  * Originally Released Under LGPL - original licence link has changed is not relivant.
58623  *
58624  * Fork - LGPL
58625  * <script type="text/javascript">
58626  */
58627  
58628 /**
58629  * @class Roo.BasicLayoutRegion
58630  * @extends Roo.util.Observable
58631  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
58632  * and does not have a titlebar, tabs or any other features. All it does is size and position 
58633  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
58634  */
58635 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
58636     this.mgr = mgr;
58637     this.position  = pos;
58638     this.events = {
58639         /**
58640          * @scope Roo.BasicLayoutRegion
58641          */
58642         
58643         /**
58644          * @event beforeremove
58645          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
58646          * @param {Roo.LayoutRegion} this
58647          * @param {Roo.ContentPanel} panel The panel
58648          * @param {Object} e The cancel event object
58649          */
58650         "beforeremove" : true,
58651         /**
58652          * @event invalidated
58653          * Fires when the layout for this region is changed.
58654          * @param {Roo.LayoutRegion} this
58655          */
58656         "invalidated" : true,
58657         /**
58658          * @event visibilitychange
58659          * Fires when this region is shown or hidden 
58660          * @param {Roo.LayoutRegion} this
58661          * @param {Boolean} visibility true or false
58662          */
58663         "visibilitychange" : true,
58664         /**
58665          * @event paneladded
58666          * Fires when a panel is added. 
58667          * @param {Roo.LayoutRegion} this
58668          * @param {Roo.ContentPanel} panel The panel
58669          */
58670         "paneladded" : true,
58671         /**
58672          * @event panelremoved
58673          * Fires when a panel is removed. 
58674          * @param {Roo.LayoutRegion} this
58675          * @param {Roo.ContentPanel} panel The panel
58676          */
58677         "panelremoved" : true,
58678         /**
58679          * @event beforecollapse
58680          * Fires when this region before collapse.
58681          * @param {Roo.LayoutRegion} this
58682          */
58683         "beforecollapse" : true,
58684         /**
58685          * @event collapsed
58686          * Fires when this region is collapsed.
58687          * @param {Roo.LayoutRegion} this
58688          */
58689         "collapsed" : true,
58690         /**
58691          * @event expanded
58692          * Fires when this region is expanded.
58693          * @param {Roo.LayoutRegion} this
58694          */
58695         "expanded" : true,
58696         /**
58697          * @event slideshow
58698          * Fires when this region is slid into view.
58699          * @param {Roo.LayoutRegion} this
58700          */
58701         "slideshow" : true,
58702         /**
58703          * @event slidehide
58704          * Fires when this region slides out of view. 
58705          * @param {Roo.LayoutRegion} this
58706          */
58707         "slidehide" : true,
58708         /**
58709          * @event panelactivated
58710          * Fires when a panel is activated. 
58711          * @param {Roo.LayoutRegion} this
58712          * @param {Roo.ContentPanel} panel The activated panel
58713          */
58714         "panelactivated" : true,
58715         /**
58716          * @event resized
58717          * Fires when the user resizes this region. 
58718          * @param {Roo.LayoutRegion} this
58719          * @param {Number} newSize The new size (width for east/west, height for north/south)
58720          */
58721         "resized" : true
58722     };
58723     /** A collection of panels in this region. @type Roo.util.MixedCollection */
58724     this.panels = new Roo.util.MixedCollection();
58725     this.panels.getKey = this.getPanelId.createDelegate(this);
58726     this.box = null;
58727     this.activePanel = null;
58728     // ensure listeners are added...
58729     
58730     if (config.listeners || config.events) {
58731         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
58732             listeners : config.listeners || {},
58733             events : config.events || {}
58734         });
58735     }
58736     
58737     if(skipConfig !== true){
58738         this.applyConfig(config);
58739     }
58740 };
58741
58742 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
58743     getPanelId : function(p){
58744         return p.getId();
58745     },
58746     
58747     applyConfig : function(config){
58748         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
58749         this.config = config;
58750         
58751     },
58752     
58753     /**
58754      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
58755      * the width, for horizontal (north, south) the height.
58756      * @param {Number} newSize The new width or height
58757      */
58758     resizeTo : function(newSize){
58759         var el = this.el ? this.el :
58760                  (this.activePanel ? this.activePanel.getEl() : null);
58761         if(el){
58762             switch(this.position){
58763                 case "east":
58764                 case "west":
58765                     el.setWidth(newSize);
58766                     this.fireEvent("resized", this, newSize);
58767                 break;
58768                 case "north":
58769                 case "south":
58770                     el.setHeight(newSize);
58771                     this.fireEvent("resized", this, newSize);
58772                 break;                
58773             }
58774         }
58775     },
58776     
58777     getBox : function(){
58778         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
58779     },
58780     
58781     getMargins : function(){
58782         return this.margins;
58783     },
58784     
58785     updateBox : function(box){
58786         this.box = box;
58787         var el = this.activePanel.getEl();
58788         el.dom.style.left = box.x + "px";
58789         el.dom.style.top = box.y + "px";
58790         this.activePanel.setSize(box.width, box.height);
58791     },
58792     
58793     /**
58794      * Returns the container element for this region.
58795      * @return {Roo.Element}
58796      */
58797     getEl : function(){
58798         return this.activePanel;
58799     },
58800     
58801     /**
58802      * Returns true if this region is currently visible.
58803      * @return {Boolean}
58804      */
58805     isVisible : function(){
58806         return this.activePanel ? true : false;
58807     },
58808     
58809     setActivePanel : function(panel){
58810         panel = this.getPanel(panel);
58811         if(this.activePanel && this.activePanel != panel){
58812             this.activePanel.setActiveState(false);
58813             this.activePanel.getEl().setLeftTop(-10000,-10000);
58814         }
58815         this.activePanel = panel;
58816         panel.setActiveState(true);
58817         if(this.box){
58818             panel.setSize(this.box.width, this.box.height);
58819         }
58820         this.fireEvent("panelactivated", this, panel);
58821         this.fireEvent("invalidated");
58822     },
58823     
58824     /**
58825      * Show the specified panel.
58826      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
58827      * @return {Roo.ContentPanel} The shown panel or null
58828      */
58829     showPanel : function(panel){
58830         if(panel = this.getPanel(panel)){
58831             this.setActivePanel(panel);
58832         }
58833         return panel;
58834     },
58835     
58836     /**
58837      * Get the active panel for this region.
58838      * @return {Roo.ContentPanel} The active panel or null
58839      */
58840     getActivePanel : function(){
58841         return this.activePanel;
58842     },
58843     
58844     /**
58845      * Add the passed ContentPanel(s)
58846      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
58847      * @return {Roo.ContentPanel} The panel added (if only one was added)
58848      */
58849     add : function(panel){
58850         if(arguments.length > 1){
58851             for(var i = 0, len = arguments.length; i < len; i++) {
58852                 this.add(arguments[i]);
58853             }
58854             return null;
58855         }
58856         if(this.hasPanel(panel)){
58857             this.showPanel(panel);
58858             return panel;
58859         }
58860         var el = panel.getEl();
58861         if(el.dom.parentNode != this.mgr.el.dom){
58862             this.mgr.el.dom.appendChild(el.dom);
58863         }
58864         if(panel.setRegion){
58865             panel.setRegion(this);
58866         }
58867         this.panels.add(panel);
58868         el.setStyle("position", "absolute");
58869         if(!panel.background){
58870             this.setActivePanel(panel);
58871             if(this.config.initialSize && this.panels.getCount()==1){
58872                 this.resizeTo(this.config.initialSize);
58873             }
58874         }
58875         this.fireEvent("paneladded", this, panel);
58876         return panel;
58877     },
58878     
58879     /**
58880      * Returns true if the panel is in this region.
58881      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
58882      * @return {Boolean}
58883      */
58884     hasPanel : function(panel){
58885         if(typeof panel == "object"){ // must be panel obj
58886             panel = panel.getId();
58887         }
58888         return this.getPanel(panel) ? true : false;
58889     },
58890     
58891     /**
58892      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
58893      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
58894      * @param {Boolean} preservePanel Overrides the config preservePanel option
58895      * @return {Roo.ContentPanel} The panel that was removed
58896      */
58897     remove : function(panel, preservePanel){
58898         panel = this.getPanel(panel);
58899         if(!panel){
58900             return null;
58901         }
58902         var e = {};
58903         this.fireEvent("beforeremove", this, panel, e);
58904         if(e.cancel === true){
58905             return null;
58906         }
58907         var panelId = panel.getId();
58908         this.panels.removeKey(panelId);
58909         return panel;
58910     },
58911     
58912     /**
58913      * Returns the panel specified or null if it's not in this region.
58914      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
58915      * @return {Roo.ContentPanel}
58916      */
58917     getPanel : function(id){
58918         if(typeof id == "object"){ // must be panel obj
58919             return id;
58920         }
58921         return this.panels.get(id);
58922     },
58923     
58924     /**
58925      * Returns this regions position (north/south/east/west/center).
58926      * @return {String} 
58927      */
58928     getPosition: function(){
58929         return this.position;    
58930     }
58931 });/*
58932  * Based on:
58933  * Ext JS Library 1.1.1
58934  * Copyright(c) 2006-2007, Ext JS, LLC.
58935  *
58936  * Originally Released Under LGPL - original licence link has changed is not relivant.
58937  *
58938  * Fork - LGPL
58939  * <script type="text/javascript">
58940  */
58941  
58942 /**
58943  * @class Roo.LayoutRegion
58944  * @extends Roo.BasicLayoutRegion
58945  * This class represents a region in a layout manager.
58946  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
58947  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
58948  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
58949  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
58950  * @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})
58951  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
58952  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
58953  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
58954  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
58955  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
58956  * @cfg {String}    title           The title for the region (overrides panel titles)
58957  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
58958  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
58959  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
58960  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
58961  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
58962  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
58963  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
58964  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
58965  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
58966  * @cfg {Boolean}   showPin         True to show a pin button
58967  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
58968  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
58969  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
58970  * @cfg {Number}    width           For East/West panels
58971  * @cfg {Number}    height          For North/South panels
58972  * @cfg {Boolean}   split           To show the splitter
58973  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
58974  */
58975 Roo.LayoutRegion = function(mgr, config, pos){
58976     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
58977     var dh = Roo.DomHelper;
58978     /** This region's container element 
58979     * @type Roo.Element */
58980     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
58981     /** This region's title element 
58982     * @type Roo.Element */
58983
58984     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
58985         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
58986         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
58987     ]}, true);
58988     this.titleEl.enableDisplayMode();
58989     /** This region's title text element 
58990     * @type HTMLElement */
58991     this.titleTextEl = this.titleEl.dom.firstChild;
58992     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
58993     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
58994     this.closeBtn.enableDisplayMode();
58995     this.closeBtn.on("click", this.closeClicked, this);
58996     this.closeBtn.hide();
58997
58998     this.createBody(config);
58999     this.visible = true;
59000     this.collapsed = false;
59001
59002     if(config.hideWhenEmpty){
59003         this.hide();
59004         this.on("paneladded", this.validateVisibility, this);
59005         this.on("panelremoved", this.validateVisibility, this);
59006     }
59007     this.applyConfig(config);
59008 };
59009
59010 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
59011
59012     createBody : function(){
59013         /** This region's body element 
59014         * @type Roo.Element */
59015         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
59016     },
59017
59018     applyConfig : function(c){
59019         if(c.collapsible && this.position != "center" && !this.collapsedEl){
59020             var dh = Roo.DomHelper;
59021             if(c.titlebar !== false){
59022                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
59023                 this.collapseBtn.on("click", this.collapse, this);
59024                 this.collapseBtn.enableDisplayMode();
59025
59026                 if(c.showPin === true || this.showPin){
59027                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
59028                     this.stickBtn.enableDisplayMode();
59029                     this.stickBtn.on("click", this.expand, this);
59030                     this.stickBtn.hide();
59031                 }
59032             }
59033             /** This region's collapsed element
59034             * @type Roo.Element */
59035             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
59036                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
59037             ]}, true);
59038             if(c.floatable !== false){
59039                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
59040                this.collapsedEl.on("click", this.collapseClick, this);
59041             }
59042
59043             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
59044                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
59045                    id: "message", unselectable: "on", style:{"float":"left"}});
59046                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
59047              }
59048             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
59049             this.expandBtn.on("click", this.expand, this);
59050         }
59051         if(this.collapseBtn){
59052             this.collapseBtn.setVisible(c.collapsible == true);
59053         }
59054         this.cmargins = c.cmargins || this.cmargins ||
59055                          (this.position == "west" || this.position == "east" ?
59056                              {top: 0, left: 2, right:2, bottom: 0} :
59057                              {top: 2, left: 0, right:0, bottom: 2});
59058         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59059         this.bottomTabs = c.tabPosition != "top";
59060         this.autoScroll = c.autoScroll || false;
59061         if(this.autoScroll){
59062             this.bodyEl.setStyle("overflow", "auto");
59063         }else{
59064             this.bodyEl.setStyle("overflow", "hidden");
59065         }
59066         //if(c.titlebar !== false){
59067             if((!c.titlebar && !c.title) || c.titlebar === false){
59068                 this.titleEl.hide();
59069             }else{
59070                 this.titleEl.show();
59071                 if(c.title){
59072                     this.titleTextEl.innerHTML = c.title;
59073                 }
59074             }
59075         //}
59076         this.duration = c.duration || .30;
59077         this.slideDuration = c.slideDuration || .45;
59078         this.config = c;
59079         if(c.collapsed){
59080             this.collapse(true);
59081         }
59082         if(c.hidden){
59083             this.hide();
59084         }
59085     },
59086     /**
59087      * Returns true if this region is currently visible.
59088      * @return {Boolean}
59089      */
59090     isVisible : function(){
59091         return this.visible;
59092     },
59093
59094     /**
59095      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
59096      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
59097      */
59098     setCollapsedTitle : function(title){
59099         title = title || "&#160;";
59100         if(this.collapsedTitleTextEl){
59101             this.collapsedTitleTextEl.innerHTML = title;
59102         }
59103     },
59104
59105     getBox : function(){
59106         var b;
59107         if(!this.collapsed){
59108             b = this.el.getBox(false, true);
59109         }else{
59110             b = this.collapsedEl.getBox(false, true);
59111         }
59112         return b;
59113     },
59114
59115     getMargins : function(){
59116         return this.collapsed ? this.cmargins : this.margins;
59117     },
59118
59119     highlight : function(){
59120         this.el.addClass("x-layout-panel-dragover");
59121     },
59122
59123     unhighlight : function(){
59124         this.el.removeClass("x-layout-panel-dragover");
59125     },
59126
59127     updateBox : function(box){
59128         this.box = box;
59129         if(!this.collapsed){
59130             this.el.dom.style.left = box.x + "px";
59131             this.el.dom.style.top = box.y + "px";
59132             this.updateBody(box.width, box.height);
59133         }else{
59134             this.collapsedEl.dom.style.left = box.x + "px";
59135             this.collapsedEl.dom.style.top = box.y + "px";
59136             this.collapsedEl.setSize(box.width, box.height);
59137         }
59138         if(this.tabs){
59139             this.tabs.autoSizeTabs();
59140         }
59141     },
59142
59143     updateBody : function(w, h){
59144         if(w !== null){
59145             this.el.setWidth(w);
59146             w -= this.el.getBorderWidth("rl");
59147             if(this.config.adjustments){
59148                 w += this.config.adjustments[0];
59149             }
59150         }
59151         if(h !== null){
59152             this.el.setHeight(h);
59153             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
59154             h -= this.el.getBorderWidth("tb");
59155             if(this.config.adjustments){
59156                 h += this.config.adjustments[1];
59157             }
59158             this.bodyEl.setHeight(h);
59159             if(this.tabs){
59160                 h = this.tabs.syncHeight(h);
59161             }
59162         }
59163         if(this.panelSize){
59164             w = w !== null ? w : this.panelSize.width;
59165             h = h !== null ? h : this.panelSize.height;
59166         }
59167         if(this.activePanel){
59168             var el = this.activePanel.getEl();
59169             w = w !== null ? w : el.getWidth();
59170             h = h !== null ? h : el.getHeight();
59171             this.panelSize = {width: w, height: h};
59172             this.activePanel.setSize(w, h);
59173         }
59174         if(Roo.isIE && this.tabs){
59175             this.tabs.el.repaint();
59176         }
59177     },
59178
59179     /**
59180      * Returns the container element for this region.
59181      * @return {Roo.Element}
59182      */
59183     getEl : function(){
59184         return this.el;
59185     },
59186
59187     /**
59188      * Hides this region.
59189      */
59190     hide : function(){
59191         if(!this.collapsed){
59192             this.el.dom.style.left = "-2000px";
59193             this.el.hide();
59194         }else{
59195             this.collapsedEl.dom.style.left = "-2000px";
59196             this.collapsedEl.hide();
59197         }
59198         this.visible = false;
59199         this.fireEvent("visibilitychange", this, false);
59200     },
59201
59202     /**
59203      * Shows this region if it was previously hidden.
59204      */
59205     show : function(){
59206         if(!this.collapsed){
59207             this.el.show();
59208         }else{
59209             this.collapsedEl.show();
59210         }
59211         this.visible = true;
59212         this.fireEvent("visibilitychange", this, true);
59213     },
59214
59215     closeClicked : function(){
59216         if(this.activePanel){
59217             this.remove(this.activePanel);
59218         }
59219     },
59220
59221     collapseClick : function(e){
59222         if(this.isSlid){
59223            e.stopPropagation();
59224            this.slideIn();
59225         }else{
59226            e.stopPropagation();
59227            this.slideOut();
59228         }
59229     },
59230
59231     /**
59232      * Collapses this region.
59233      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
59234      */
59235     collapse : function(skipAnim, skipCheck){
59236         if(this.collapsed) {
59237             return;
59238         }
59239         
59240         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
59241             
59242             this.collapsed = true;
59243             if(this.split){
59244                 this.split.el.hide();
59245             }
59246             if(this.config.animate && skipAnim !== true){
59247                 this.fireEvent("invalidated", this);
59248                 this.animateCollapse();
59249             }else{
59250                 this.el.setLocation(-20000,-20000);
59251                 this.el.hide();
59252                 this.collapsedEl.show();
59253                 this.fireEvent("collapsed", this);
59254                 this.fireEvent("invalidated", this);
59255             }
59256         }
59257         
59258     },
59259
59260     animateCollapse : function(){
59261         // overridden
59262     },
59263
59264     /**
59265      * Expands this region if it was previously collapsed.
59266      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
59267      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
59268      */
59269     expand : function(e, skipAnim){
59270         if(e) {
59271             e.stopPropagation();
59272         }
59273         if(!this.collapsed || this.el.hasActiveFx()) {
59274             return;
59275         }
59276         if(this.isSlid){
59277             this.afterSlideIn();
59278             skipAnim = true;
59279         }
59280         this.collapsed = false;
59281         if(this.config.animate && skipAnim !== true){
59282             this.animateExpand();
59283         }else{
59284             this.el.show();
59285             if(this.split){
59286                 this.split.el.show();
59287             }
59288             this.collapsedEl.setLocation(-2000,-2000);
59289             this.collapsedEl.hide();
59290             this.fireEvent("invalidated", this);
59291             this.fireEvent("expanded", this);
59292         }
59293     },
59294
59295     animateExpand : function(){
59296         // overridden
59297     },
59298
59299     initTabs : function()
59300     {
59301         this.bodyEl.setStyle("overflow", "hidden");
59302         var ts = new Roo.TabPanel(
59303                 this.bodyEl.dom,
59304                 {
59305                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
59306                     disableTooltips: this.config.disableTabTips,
59307                     toolbar : this.config.toolbar
59308                 }
59309         );
59310         if(this.config.hideTabs){
59311             ts.stripWrap.setDisplayed(false);
59312         }
59313         this.tabs = ts;
59314         ts.resizeTabs = this.config.resizeTabs === true;
59315         ts.minTabWidth = this.config.minTabWidth || 40;
59316         ts.maxTabWidth = this.config.maxTabWidth || 250;
59317         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
59318         ts.monitorResize = false;
59319         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59320         ts.bodyEl.addClass('x-layout-tabs-body');
59321         this.panels.each(this.initPanelAsTab, this);
59322     },
59323
59324     initPanelAsTab : function(panel){
59325         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
59326                     this.config.closeOnTab && panel.isClosable());
59327         if(panel.tabTip !== undefined){
59328             ti.setTooltip(panel.tabTip);
59329         }
59330         ti.on("activate", function(){
59331               this.setActivePanel(panel);
59332         }, this);
59333         if(this.config.closeOnTab){
59334             ti.on("beforeclose", function(t, e){
59335                 e.cancel = true;
59336                 this.remove(panel);
59337             }, this);
59338         }
59339         return ti;
59340     },
59341
59342     updatePanelTitle : function(panel, title){
59343         if(this.activePanel == panel){
59344             this.updateTitle(title);
59345         }
59346         if(this.tabs){
59347             var ti = this.tabs.getTab(panel.getEl().id);
59348             ti.setText(title);
59349             if(panel.tabTip !== undefined){
59350                 ti.setTooltip(panel.tabTip);
59351             }
59352         }
59353     },
59354
59355     updateTitle : function(title){
59356         if(this.titleTextEl && !this.config.title){
59357             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
59358         }
59359     },
59360
59361     setActivePanel : function(panel){
59362         panel = this.getPanel(panel);
59363         if(this.activePanel && this.activePanel != panel){
59364             this.activePanel.setActiveState(false);
59365         }
59366         this.activePanel = panel;
59367         panel.setActiveState(true);
59368         if(this.panelSize){
59369             panel.setSize(this.panelSize.width, this.panelSize.height);
59370         }
59371         if(this.closeBtn){
59372             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
59373         }
59374         this.updateTitle(panel.getTitle());
59375         if(this.tabs){
59376             this.fireEvent("invalidated", this);
59377         }
59378         this.fireEvent("panelactivated", this, panel);
59379     },
59380
59381     /**
59382      * Shows the specified panel.
59383      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
59384      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
59385      */
59386     showPanel : function(panel)
59387     {
59388         panel = this.getPanel(panel);
59389         if(panel){
59390             if(this.tabs){
59391                 var tab = this.tabs.getTab(panel.getEl().id);
59392                 if(tab.isHidden()){
59393                     this.tabs.unhideTab(tab.id);
59394                 }
59395                 tab.activate();
59396             }else{
59397                 this.setActivePanel(panel);
59398             }
59399         }
59400         return panel;
59401     },
59402
59403     /**
59404      * Get the active panel for this region.
59405      * @return {Roo.ContentPanel} The active panel or null
59406      */
59407     getActivePanel : function(){
59408         return this.activePanel;
59409     },
59410
59411     validateVisibility : function(){
59412         if(this.panels.getCount() < 1){
59413             this.updateTitle("&#160;");
59414             this.closeBtn.hide();
59415             this.hide();
59416         }else{
59417             if(!this.isVisible()){
59418                 this.show();
59419             }
59420         }
59421     },
59422
59423     /**
59424      * Adds the passed ContentPanel(s) to this region.
59425      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59426      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
59427      */
59428     add : function(panel){
59429         if(arguments.length > 1){
59430             for(var i = 0, len = arguments.length; i < len; i++) {
59431                 this.add(arguments[i]);
59432             }
59433             return null;
59434         }
59435         if(this.hasPanel(panel)){
59436             this.showPanel(panel);
59437             return panel;
59438         }
59439         panel.setRegion(this);
59440         this.panels.add(panel);
59441         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
59442             this.bodyEl.dom.appendChild(panel.getEl().dom);
59443             if(panel.background !== true){
59444                 this.setActivePanel(panel);
59445             }
59446             this.fireEvent("paneladded", this, panel);
59447             return panel;
59448         }
59449         if(!this.tabs){
59450             this.initTabs();
59451         }else{
59452             this.initPanelAsTab(panel);
59453         }
59454         if(panel.background !== true){
59455             this.tabs.activate(panel.getEl().id);
59456         }
59457         this.fireEvent("paneladded", this, panel);
59458         return panel;
59459     },
59460
59461     /**
59462      * Hides the tab for the specified panel.
59463      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59464      */
59465     hidePanel : function(panel){
59466         if(this.tabs && (panel = this.getPanel(panel))){
59467             this.tabs.hideTab(panel.getEl().id);
59468         }
59469     },
59470
59471     /**
59472      * Unhides the tab for a previously hidden panel.
59473      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59474      */
59475     unhidePanel : function(panel){
59476         if(this.tabs && (panel = this.getPanel(panel))){
59477             this.tabs.unhideTab(panel.getEl().id);
59478         }
59479     },
59480
59481     clearPanels : function(){
59482         while(this.panels.getCount() > 0){
59483              this.remove(this.panels.first());
59484         }
59485     },
59486
59487     /**
59488      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59489      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
59490      * @param {Boolean} preservePanel Overrides the config preservePanel option
59491      * @return {Roo.ContentPanel} The panel that was removed
59492      */
59493     remove : function(panel, preservePanel){
59494         panel = this.getPanel(panel);
59495         if(!panel){
59496             return null;
59497         }
59498         var e = {};
59499         this.fireEvent("beforeremove", this, panel, e);
59500         if(e.cancel === true){
59501             return null;
59502         }
59503         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
59504         var panelId = panel.getId();
59505         this.panels.removeKey(panelId);
59506         if(preservePanel){
59507             document.body.appendChild(panel.getEl().dom);
59508         }
59509         if(this.tabs){
59510             this.tabs.removeTab(panel.getEl().id);
59511         }else if (!preservePanel){
59512             this.bodyEl.dom.removeChild(panel.getEl().dom);
59513         }
59514         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
59515             var p = this.panels.first();
59516             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
59517             tempEl.appendChild(p.getEl().dom);
59518             this.bodyEl.update("");
59519             this.bodyEl.dom.appendChild(p.getEl().dom);
59520             tempEl = null;
59521             this.updateTitle(p.getTitle());
59522             this.tabs = null;
59523             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59524             this.setActivePanel(p);
59525         }
59526         panel.setRegion(null);
59527         if(this.activePanel == panel){
59528             this.activePanel = null;
59529         }
59530         if(this.config.autoDestroy !== false && preservePanel !== true){
59531             try{panel.destroy();}catch(e){}
59532         }
59533         this.fireEvent("panelremoved", this, panel);
59534         return panel;
59535     },
59536
59537     /**
59538      * Returns the TabPanel component used by this region
59539      * @return {Roo.TabPanel}
59540      */
59541     getTabs : function(){
59542         return this.tabs;
59543     },
59544
59545     createTool : function(parentEl, className){
59546         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
59547             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
59548         btn.addClassOnOver("x-layout-tools-button-over");
59549         return btn;
59550     }
59551 });/*
59552  * Based on:
59553  * Ext JS Library 1.1.1
59554  * Copyright(c) 2006-2007, Ext JS, LLC.
59555  *
59556  * Originally Released Under LGPL - original licence link has changed is not relivant.
59557  *
59558  * Fork - LGPL
59559  * <script type="text/javascript">
59560  */
59561  
59562
59563
59564 /**
59565  * @class Roo.SplitLayoutRegion
59566  * @extends Roo.LayoutRegion
59567  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
59568  */
59569 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
59570     this.cursor = cursor;
59571     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
59572 };
59573
59574 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
59575     splitTip : "Drag to resize.",
59576     collapsibleSplitTip : "Drag to resize. Double click to hide.",
59577     useSplitTips : false,
59578
59579     applyConfig : function(config){
59580         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
59581         if(config.split){
59582             if(!this.split){
59583                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
59584                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
59585                 /** The SplitBar for this region 
59586                 * @type Roo.SplitBar */
59587                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
59588                 this.split.on("moved", this.onSplitMove, this);
59589                 this.split.useShim = config.useShim === true;
59590                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
59591                 if(this.useSplitTips){
59592                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
59593                 }
59594                 if(config.collapsible){
59595                     this.split.el.on("dblclick", this.collapse,  this);
59596                 }
59597             }
59598             if(typeof config.minSize != "undefined"){
59599                 this.split.minSize = config.minSize;
59600             }
59601             if(typeof config.maxSize != "undefined"){
59602                 this.split.maxSize = config.maxSize;
59603             }
59604             if(config.hideWhenEmpty || config.hidden || config.collapsed){
59605                 this.hideSplitter();
59606             }
59607         }
59608     },
59609
59610     getHMaxSize : function(){
59611          var cmax = this.config.maxSize || 10000;
59612          var center = this.mgr.getRegion("center");
59613          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
59614     },
59615
59616     getVMaxSize : function(){
59617          var cmax = this.config.maxSize || 10000;
59618          var center = this.mgr.getRegion("center");
59619          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
59620     },
59621
59622     onSplitMove : function(split, newSize){
59623         this.fireEvent("resized", this, newSize);
59624     },
59625     
59626     /** 
59627      * Returns the {@link Roo.SplitBar} for this region.
59628      * @return {Roo.SplitBar}
59629      */
59630     getSplitBar : function(){
59631         return this.split;
59632     },
59633     
59634     hide : function(){
59635         this.hideSplitter();
59636         Roo.SplitLayoutRegion.superclass.hide.call(this);
59637     },
59638
59639     hideSplitter : function(){
59640         if(this.split){
59641             this.split.el.setLocation(-2000,-2000);
59642             this.split.el.hide();
59643         }
59644     },
59645
59646     show : function(){
59647         if(this.split){
59648             this.split.el.show();
59649         }
59650         Roo.SplitLayoutRegion.superclass.show.call(this);
59651     },
59652     
59653     beforeSlide: function(){
59654         if(Roo.isGecko){// firefox overflow auto bug workaround
59655             this.bodyEl.clip();
59656             if(this.tabs) {
59657                 this.tabs.bodyEl.clip();
59658             }
59659             if(this.activePanel){
59660                 this.activePanel.getEl().clip();
59661                 
59662                 if(this.activePanel.beforeSlide){
59663                     this.activePanel.beforeSlide();
59664                 }
59665             }
59666         }
59667     },
59668     
59669     afterSlide : function(){
59670         if(Roo.isGecko){// firefox overflow auto bug workaround
59671             this.bodyEl.unclip();
59672             if(this.tabs) {
59673                 this.tabs.bodyEl.unclip();
59674             }
59675             if(this.activePanel){
59676                 this.activePanel.getEl().unclip();
59677                 if(this.activePanel.afterSlide){
59678                     this.activePanel.afterSlide();
59679                 }
59680             }
59681         }
59682     },
59683
59684     initAutoHide : function(){
59685         if(this.autoHide !== false){
59686             if(!this.autoHideHd){
59687                 var st = new Roo.util.DelayedTask(this.slideIn, this);
59688                 this.autoHideHd = {
59689                     "mouseout": function(e){
59690                         if(!e.within(this.el, true)){
59691                             st.delay(500);
59692                         }
59693                     },
59694                     "mouseover" : function(e){
59695                         st.cancel();
59696                     },
59697                     scope : this
59698                 };
59699             }
59700             this.el.on(this.autoHideHd);
59701         }
59702     },
59703
59704     clearAutoHide : function(){
59705         if(this.autoHide !== false){
59706             this.el.un("mouseout", this.autoHideHd.mouseout);
59707             this.el.un("mouseover", this.autoHideHd.mouseover);
59708         }
59709     },
59710
59711     clearMonitor : function(){
59712         Roo.get(document).un("click", this.slideInIf, this);
59713     },
59714
59715     // these names are backwards but not changed for compat
59716     slideOut : function(){
59717         if(this.isSlid || this.el.hasActiveFx()){
59718             return;
59719         }
59720         this.isSlid = true;
59721         if(this.collapseBtn){
59722             this.collapseBtn.hide();
59723         }
59724         this.closeBtnState = this.closeBtn.getStyle('display');
59725         this.closeBtn.hide();
59726         if(this.stickBtn){
59727             this.stickBtn.show();
59728         }
59729         this.el.show();
59730         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
59731         this.beforeSlide();
59732         this.el.setStyle("z-index", 10001);
59733         this.el.slideIn(this.getSlideAnchor(), {
59734             callback: function(){
59735                 this.afterSlide();
59736                 this.initAutoHide();
59737                 Roo.get(document).on("click", this.slideInIf, this);
59738                 this.fireEvent("slideshow", this);
59739             },
59740             scope: this,
59741             block: true
59742         });
59743     },
59744
59745     afterSlideIn : function(){
59746         this.clearAutoHide();
59747         this.isSlid = false;
59748         this.clearMonitor();
59749         this.el.setStyle("z-index", "");
59750         if(this.collapseBtn){
59751             this.collapseBtn.show();
59752         }
59753         this.closeBtn.setStyle('display', this.closeBtnState);
59754         if(this.stickBtn){
59755             this.stickBtn.hide();
59756         }
59757         this.fireEvent("slidehide", this);
59758     },
59759
59760     slideIn : function(cb){
59761         if(!this.isSlid || this.el.hasActiveFx()){
59762             Roo.callback(cb);
59763             return;
59764         }
59765         this.isSlid = false;
59766         this.beforeSlide();
59767         this.el.slideOut(this.getSlideAnchor(), {
59768             callback: function(){
59769                 this.el.setLeftTop(-10000, -10000);
59770                 this.afterSlide();
59771                 this.afterSlideIn();
59772                 Roo.callback(cb);
59773             },
59774             scope: this,
59775             block: true
59776         });
59777     },
59778     
59779     slideInIf : function(e){
59780         if(!e.within(this.el)){
59781             this.slideIn();
59782         }
59783     },
59784
59785     animateCollapse : function(){
59786         this.beforeSlide();
59787         this.el.setStyle("z-index", 20000);
59788         var anchor = this.getSlideAnchor();
59789         this.el.slideOut(anchor, {
59790             callback : function(){
59791                 this.el.setStyle("z-index", "");
59792                 this.collapsedEl.slideIn(anchor, {duration:.3});
59793                 this.afterSlide();
59794                 this.el.setLocation(-10000,-10000);
59795                 this.el.hide();
59796                 this.fireEvent("collapsed", this);
59797             },
59798             scope: this,
59799             block: true
59800         });
59801     },
59802
59803     animateExpand : function(){
59804         this.beforeSlide();
59805         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
59806         this.el.setStyle("z-index", 20000);
59807         this.collapsedEl.hide({
59808             duration:.1
59809         });
59810         this.el.slideIn(this.getSlideAnchor(), {
59811             callback : function(){
59812                 this.el.setStyle("z-index", "");
59813                 this.afterSlide();
59814                 if(this.split){
59815                     this.split.el.show();
59816                 }
59817                 this.fireEvent("invalidated", this);
59818                 this.fireEvent("expanded", this);
59819             },
59820             scope: this,
59821             block: true
59822         });
59823     },
59824
59825     anchors : {
59826         "west" : "left",
59827         "east" : "right",
59828         "north" : "top",
59829         "south" : "bottom"
59830     },
59831
59832     sanchors : {
59833         "west" : "l",
59834         "east" : "r",
59835         "north" : "t",
59836         "south" : "b"
59837     },
59838
59839     canchors : {
59840         "west" : "tl-tr",
59841         "east" : "tr-tl",
59842         "north" : "tl-bl",
59843         "south" : "bl-tl"
59844     },
59845
59846     getAnchor : function(){
59847         return this.anchors[this.position];
59848     },
59849
59850     getCollapseAnchor : function(){
59851         return this.canchors[this.position];
59852     },
59853
59854     getSlideAnchor : function(){
59855         return this.sanchors[this.position];
59856     },
59857
59858     getAlignAdj : function(){
59859         var cm = this.cmargins;
59860         switch(this.position){
59861             case "west":
59862                 return [0, 0];
59863             break;
59864             case "east":
59865                 return [0, 0];
59866             break;
59867             case "north":
59868                 return [0, 0];
59869             break;
59870             case "south":
59871                 return [0, 0];
59872             break;
59873         }
59874     },
59875
59876     getExpandAdj : function(){
59877         var c = this.collapsedEl, cm = this.cmargins;
59878         switch(this.position){
59879             case "west":
59880                 return [-(cm.right+c.getWidth()+cm.left), 0];
59881             break;
59882             case "east":
59883                 return [cm.right+c.getWidth()+cm.left, 0];
59884             break;
59885             case "north":
59886                 return [0, -(cm.top+cm.bottom+c.getHeight())];
59887             break;
59888             case "south":
59889                 return [0, cm.top+cm.bottom+c.getHeight()];
59890             break;
59891         }
59892     }
59893 });/*
59894  * Based on:
59895  * Ext JS Library 1.1.1
59896  * Copyright(c) 2006-2007, Ext JS, LLC.
59897  *
59898  * Originally Released Under LGPL - original licence link has changed is not relivant.
59899  *
59900  * Fork - LGPL
59901  * <script type="text/javascript">
59902  */
59903 /*
59904  * These classes are private internal classes
59905  */
59906 Roo.CenterLayoutRegion = function(mgr, config){
59907     Roo.LayoutRegion.call(this, mgr, config, "center");
59908     this.visible = true;
59909     this.minWidth = config.minWidth || 20;
59910     this.minHeight = config.minHeight || 20;
59911 };
59912
59913 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
59914     hide : function(){
59915         // center panel can't be hidden
59916     },
59917     
59918     show : function(){
59919         // center panel can't be hidden
59920     },
59921     
59922     getMinWidth: function(){
59923         return this.minWidth;
59924     },
59925     
59926     getMinHeight: function(){
59927         return this.minHeight;
59928     }
59929 });
59930
59931
59932 Roo.NorthLayoutRegion = function(mgr, config){
59933     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
59934     if(this.split){
59935         this.split.placement = Roo.SplitBar.TOP;
59936         this.split.orientation = Roo.SplitBar.VERTICAL;
59937         this.split.el.addClass("x-layout-split-v");
59938     }
59939     var size = config.initialSize || config.height;
59940     if(typeof size != "undefined"){
59941         this.el.setHeight(size);
59942     }
59943 };
59944 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
59945     orientation: Roo.SplitBar.VERTICAL,
59946     getBox : function(){
59947         if(this.collapsed){
59948             return this.collapsedEl.getBox();
59949         }
59950         var box = this.el.getBox();
59951         if(this.split){
59952             box.height += this.split.el.getHeight();
59953         }
59954         return box;
59955     },
59956     
59957     updateBox : function(box){
59958         if(this.split && !this.collapsed){
59959             box.height -= this.split.el.getHeight();
59960             this.split.el.setLeft(box.x);
59961             this.split.el.setTop(box.y+box.height);
59962             this.split.el.setWidth(box.width);
59963         }
59964         if(this.collapsed){
59965             this.updateBody(box.width, null);
59966         }
59967         Roo.LayoutRegion.prototype.updateBox.call(this, box);
59968     }
59969 });
59970
59971 Roo.SouthLayoutRegion = function(mgr, config){
59972     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
59973     if(this.split){
59974         this.split.placement = Roo.SplitBar.BOTTOM;
59975         this.split.orientation = Roo.SplitBar.VERTICAL;
59976         this.split.el.addClass("x-layout-split-v");
59977     }
59978     var size = config.initialSize || config.height;
59979     if(typeof size != "undefined"){
59980         this.el.setHeight(size);
59981     }
59982 };
59983 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
59984     orientation: Roo.SplitBar.VERTICAL,
59985     getBox : function(){
59986         if(this.collapsed){
59987             return this.collapsedEl.getBox();
59988         }
59989         var box = this.el.getBox();
59990         if(this.split){
59991             var sh = this.split.el.getHeight();
59992             box.height += sh;
59993             box.y -= sh;
59994         }
59995         return box;
59996     },
59997     
59998     updateBox : function(box){
59999         if(this.split && !this.collapsed){
60000             var sh = this.split.el.getHeight();
60001             box.height -= sh;
60002             box.y += sh;
60003             this.split.el.setLeft(box.x);
60004             this.split.el.setTop(box.y-sh);
60005             this.split.el.setWidth(box.width);
60006         }
60007         if(this.collapsed){
60008             this.updateBody(box.width, null);
60009         }
60010         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60011     }
60012 });
60013
60014 Roo.EastLayoutRegion = function(mgr, config){
60015     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
60016     if(this.split){
60017         this.split.placement = Roo.SplitBar.RIGHT;
60018         this.split.orientation = Roo.SplitBar.HORIZONTAL;
60019         this.split.el.addClass("x-layout-split-h");
60020     }
60021     var size = config.initialSize || config.width;
60022     if(typeof size != "undefined"){
60023         this.el.setWidth(size);
60024     }
60025 };
60026 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
60027     orientation: Roo.SplitBar.HORIZONTAL,
60028     getBox : function(){
60029         if(this.collapsed){
60030             return this.collapsedEl.getBox();
60031         }
60032         var box = this.el.getBox();
60033         if(this.split){
60034             var sw = this.split.el.getWidth();
60035             box.width += sw;
60036             box.x -= sw;
60037         }
60038         return box;
60039     },
60040
60041     updateBox : function(box){
60042         if(this.split && !this.collapsed){
60043             var sw = this.split.el.getWidth();
60044             box.width -= sw;
60045             this.split.el.setLeft(box.x);
60046             this.split.el.setTop(box.y);
60047             this.split.el.setHeight(box.height);
60048             box.x += sw;
60049         }
60050         if(this.collapsed){
60051             this.updateBody(null, box.height);
60052         }
60053         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60054     }
60055 });
60056
60057 Roo.WestLayoutRegion = function(mgr, config){
60058     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
60059     if(this.split){
60060         this.split.placement = Roo.SplitBar.LEFT;
60061         this.split.orientation = Roo.SplitBar.HORIZONTAL;
60062         this.split.el.addClass("x-layout-split-h");
60063     }
60064     var size = config.initialSize || config.width;
60065     if(typeof size != "undefined"){
60066         this.el.setWidth(size);
60067     }
60068 };
60069 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
60070     orientation: Roo.SplitBar.HORIZONTAL,
60071     getBox : function(){
60072         if(this.collapsed){
60073             return this.collapsedEl.getBox();
60074         }
60075         var box = this.el.getBox();
60076         if(this.split){
60077             box.width += this.split.el.getWidth();
60078         }
60079         return box;
60080     },
60081     
60082     updateBox : function(box){
60083         if(this.split && !this.collapsed){
60084             var sw = this.split.el.getWidth();
60085             box.width -= sw;
60086             this.split.el.setLeft(box.x+box.width);
60087             this.split.el.setTop(box.y);
60088             this.split.el.setHeight(box.height);
60089         }
60090         if(this.collapsed){
60091             this.updateBody(null, box.height);
60092         }
60093         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60094     }
60095 });
60096 /*
60097  * Based on:
60098  * Ext JS Library 1.1.1
60099  * Copyright(c) 2006-2007, Ext JS, LLC.
60100  *
60101  * Originally Released Under LGPL - original licence link has changed is not relivant.
60102  *
60103  * Fork - LGPL
60104  * <script type="text/javascript">
60105  */
60106  
60107  
60108 /*
60109  * Private internal class for reading and applying state
60110  */
60111 Roo.LayoutStateManager = function(layout){
60112      // default empty state
60113      this.state = {
60114         north: {},
60115         south: {},
60116         east: {},
60117         west: {}       
60118     };
60119 };
60120
60121 Roo.LayoutStateManager.prototype = {
60122     init : function(layout, provider){
60123         this.provider = provider;
60124         var state = provider.get(layout.id+"-layout-state");
60125         if(state){
60126             var wasUpdating = layout.isUpdating();
60127             if(!wasUpdating){
60128                 layout.beginUpdate();
60129             }
60130             for(var key in state){
60131                 if(typeof state[key] != "function"){
60132                     var rstate = state[key];
60133                     var r = layout.getRegion(key);
60134                     if(r && rstate){
60135                         if(rstate.size){
60136                             r.resizeTo(rstate.size);
60137                         }
60138                         if(rstate.collapsed == true){
60139                             r.collapse(true);
60140                         }else{
60141                             r.expand(null, true);
60142                         }
60143                     }
60144                 }
60145             }
60146             if(!wasUpdating){
60147                 layout.endUpdate();
60148             }
60149             this.state = state; 
60150         }
60151         this.layout = layout;
60152         layout.on("regionresized", this.onRegionResized, this);
60153         layout.on("regioncollapsed", this.onRegionCollapsed, this);
60154         layout.on("regionexpanded", this.onRegionExpanded, this);
60155     },
60156     
60157     storeState : function(){
60158         this.provider.set(this.layout.id+"-layout-state", this.state);
60159     },
60160     
60161     onRegionResized : function(region, newSize){
60162         this.state[region.getPosition()].size = newSize;
60163         this.storeState();
60164     },
60165     
60166     onRegionCollapsed : function(region){
60167         this.state[region.getPosition()].collapsed = true;
60168         this.storeState();
60169     },
60170     
60171     onRegionExpanded : function(region){
60172         this.state[region.getPosition()].collapsed = false;
60173         this.storeState();
60174     }
60175 };/*
60176  * Based on:
60177  * Ext JS Library 1.1.1
60178  * Copyright(c) 2006-2007, Ext JS, LLC.
60179  *
60180  * Originally Released Under LGPL - original licence link has changed is not relivant.
60181  *
60182  * Fork - LGPL
60183  * <script type="text/javascript">
60184  */
60185 /**
60186  * @class Roo.ContentPanel
60187  * @extends Roo.util.Observable
60188  * @children Roo.form.Form Roo.JsonView Roo.View
60189  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60190  * A basic ContentPanel element.
60191  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
60192  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
60193  * @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
60194  * @cfg {Boolean}   closable      True if the panel can be closed/removed
60195  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
60196  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
60197  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
60198  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
60199  * @cfg {String} title          The title for this panel
60200  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
60201  * @cfg {String} url            Calls {@link #setUrl} with this value
60202  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
60203  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
60204  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
60205  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
60206  * @cfg {String}    style  Extra style to add to the content panel
60207  * @cfg {Roo.menu.Menu} menu  popup menu
60208
60209  * @constructor
60210  * Create a new ContentPanel.
60211  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
60212  * @param {String/Object} config A string to set only the title or a config object
60213  * @param {String} content (optional) Set the HTML content for this panel
60214  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
60215  */
60216 Roo.ContentPanel = function(el, config, content){
60217     
60218      
60219     /*
60220     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
60221         config = el;
60222         el = Roo.id();
60223     }
60224     if (config && config.parentLayout) { 
60225         el = config.parentLayout.el.createChild(); 
60226     }
60227     */
60228     if(el.autoCreate){ // xtype is available if this is called from factory
60229         config = el;
60230         el = Roo.id();
60231     }
60232     this.el = Roo.get(el);
60233     if(!this.el && config && config.autoCreate){
60234         if(typeof config.autoCreate == "object"){
60235             if(!config.autoCreate.id){
60236                 config.autoCreate.id = config.id||el;
60237             }
60238             this.el = Roo.DomHelper.append(document.body,
60239                         config.autoCreate, true);
60240         }else{
60241             this.el = Roo.DomHelper.append(document.body,
60242                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
60243         }
60244     }
60245     
60246     
60247     this.closable = false;
60248     this.loaded = false;
60249     this.active = false;
60250     if(typeof config == "string"){
60251         this.title = config;
60252     }else{
60253         Roo.apply(this, config);
60254     }
60255     
60256     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
60257         this.wrapEl = this.el.wrap();
60258         this.toolbar.container = this.el.insertSibling(false, 'before');
60259         this.toolbar = new Roo.Toolbar(this.toolbar);
60260     }
60261     
60262     // xtype created footer. - not sure if will work as we normally have to render first..
60263     if (this.footer && !this.footer.el && this.footer.xtype) {
60264         if (!this.wrapEl) {
60265             this.wrapEl = this.el.wrap();
60266         }
60267     
60268         this.footer.container = this.wrapEl.createChild();
60269          
60270         this.footer = Roo.factory(this.footer, Roo);
60271         
60272     }
60273     
60274     if(this.resizeEl){
60275         this.resizeEl = Roo.get(this.resizeEl, true);
60276     }else{
60277         this.resizeEl = this.el;
60278     }
60279     // handle view.xtype
60280     
60281  
60282     
60283     
60284     this.addEvents({
60285         /**
60286          * @event activate
60287          * Fires when this panel is activated. 
60288          * @param {Roo.ContentPanel} this
60289          */
60290         "activate" : true,
60291         /**
60292          * @event deactivate
60293          * Fires when this panel is activated. 
60294          * @param {Roo.ContentPanel} this
60295          */
60296         "deactivate" : true,
60297
60298         /**
60299          * @event resize
60300          * Fires when this panel is resized if fitToFrame is true.
60301          * @param {Roo.ContentPanel} this
60302          * @param {Number} width The width after any component adjustments
60303          * @param {Number} height The height after any component adjustments
60304          */
60305         "resize" : true,
60306         
60307          /**
60308          * @event render
60309          * Fires when this tab is created
60310          * @param {Roo.ContentPanel} this
60311          */
60312         "render" : true
60313          
60314         
60315     });
60316     
60317
60318     
60319     
60320     if(this.autoScroll){
60321         this.resizeEl.setStyle("overflow", "auto");
60322     } else {
60323         // fix randome scrolling
60324         this.el.on('scroll', function() {
60325             Roo.log('fix random scolling');
60326             this.scrollTo('top',0); 
60327         });
60328     }
60329     content = content || this.content;
60330     if(content){
60331         this.setContent(content);
60332     }
60333     if(config && config.url){
60334         this.setUrl(this.url, this.params, this.loadOnce);
60335     }
60336     
60337     
60338     
60339     Roo.ContentPanel.superclass.constructor.call(this);
60340     
60341     if (this.view && typeof(this.view.xtype) != 'undefined') {
60342         this.view.el = this.el.appendChild(document.createElement("div"));
60343         this.view = Roo.factory(this.view); 
60344         this.view.render  &&  this.view.render(false, '');  
60345     }
60346     
60347     
60348     this.fireEvent('render', this);
60349 };
60350
60351 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
60352     tabTip:'',
60353     setRegion : function(region){
60354         this.region = region;
60355         if(region){
60356            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
60357         }else{
60358            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
60359         } 
60360     },
60361     
60362     /**
60363      * Returns the toolbar for this Panel if one was configured. 
60364      * @return {Roo.Toolbar} 
60365      */
60366     getToolbar : function(){
60367         return this.toolbar;
60368     },
60369     
60370     setActiveState : function(active){
60371         this.active = active;
60372         if(!active){
60373             this.fireEvent("deactivate", this);
60374         }else{
60375             this.fireEvent("activate", this);
60376         }
60377     },
60378     /**
60379      * Updates this panel's element
60380      * @param {String} content The new content
60381      * @param {Boolean} loadScripts (optional) true to look for and process scripts
60382     */
60383     setContent : function(content, loadScripts){
60384         this.el.update(content, loadScripts);
60385     },
60386
60387     ignoreResize : function(w, h){
60388         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
60389             return true;
60390         }else{
60391             this.lastSize = {width: w, height: h};
60392             return false;
60393         }
60394     },
60395     /**
60396      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
60397      * @return {Roo.UpdateManager} The UpdateManager
60398      */
60399     getUpdateManager : function(){
60400         return this.el.getUpdateManager();
60401     },
60402      /**
60403      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
60404      * @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:
60405 <pre><code>
60406 panel.load({
60407     url: "your-url.php",
60408     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
60409     callback: yourFunction,
60410     scope: yourObject, //(optional scope)
60411     discardUrl: false,
60412     nocache: false,
60413     text: "Loading...",
60414     timeout: 30,
60415     scripts: false
60416 });
60417 </code></pre>
60418      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
60419      * 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.
60420      * @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}
60421      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
60422      * @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.
60423      * @return {Roo.ContentPanel} this
60424      */
60425     load : function(){
60426         var um = this.el.getUpdateManager();
60427         um.update.apply(um, arguments);
60428         return this;
60429     },
60430
60431
60432     /**
60433      * 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.
60434      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
60435      * @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)
60436      * @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)
60437      * @return {Roo.UpdateManager} The UpdateManager
60438      */
60439     setUrl : function(url, params, loadOnce){
60440         if(this.refreshDelegate){
60441             this.removeListener("activate", this.refreshDelegate);
60442         }
60443         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
60444         this.on("activate", this.refreshDelegate);
60445         return this.el.getUpdateManager();
60446     },
60447     
60448     _handleRefresh : function(url, params, loadOnce){
60449         if(!loadOnce || !this.loaded){
60450             var updater = this.el.getUpdateManager();
60451             updater.update(url, params, this._setLoaded.createDelegate(this));
60452         }
60453     },
60454     
60455     _setLoaded : function(){
60456         this.loaded = true;
60457     }, 
60458     
60459     /**
60460      * Returns this panel's id
60461      * @return {String} 
60462      */
60463     getId : function(){
60464         return this.el.id;
60465     },
60466     
60467     /** 
60468      * Returns this panel's element - used by regiosn to add.
60469      * @return {Roo.Element} 
60470      */
60471     getEl : function(){
60472         return this.wrapEl || this.el;
60473     },
60474     
60475     adjustForComponents : function(width, height)
60476     {
60477         //Roo.log('adjustForComponents ');
60478         if(this.resizeEl != this.el){
60479             width -= this.el.getFrameWidth('lr');
60480             height -= this.el.getFrameWidth('tb');
60481         }
60482         if(this.toolbar){
60483             var te = this.toolbar.getEl();
60484             height -= te.getHeight();
60485             te.setWidth(width);
60486         }
60487         if(this.footer){
60488             var te = this.footer.getEl();
60489             //Roo.log("footer:" + te.getHeight());
60490             
60491             height -= te.getHeight();
60492             te.setWidth(width);
60493         }
60494         
60495         
60496         if(this.adjustments){
60497             width += this.adjustments[0];
60498             height += this.adjustments[1];
60499         }
60500         return {"width": width, "height": height};
60501     },
60502     
60503     setSize : function(width, height){
60504         if(this.fitToFrame && !this.ignoreResize(width, height)){
60505             if(this.fitContainer && this.resizeEl != this.el){
60506                 this.el.setSize(width, height);
60507             }
60508             var size = this.adjustForComponents(width, height);
60509             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
60510             this.fireEvent('resize', this, size.width, size.height);
60511         }
60512     },
60513     
60514     /**
60515      * Returns this panel's title
60516      * @return {String} 
60517      */
60518     getTitle : function(){
60519         return this.title;
60520     },
60521     
60522     /**
60523      * Set this panel's title
60524      * @param {String} title
60525      */
60526     setTitle : function(title){
60527         this.title = title;
60528         if(this.region){
60529             this.region.updatePanelTitle(this, title);
60530         }
60531     },
60532     
60533     /**
60534      * Returns true is this panel was configured to be closable
60535      * @return {Boolean} 
60536      */
60537     isClosable : function(){
60538         return this.closable;
60539     },
60540     
60541     beforeSlide : function(){
60542         this.el.clip();
60543         this.resizeEl.clip();
60544     },
60545     
60546     afterSlide : function(){
60547         this.el.unclip();
60548         this.resizeEl.unclip();
60549     },
60550     
60551     /**
60552      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
60553      *   Will fail silently if the {@link #setUrl} method has not been called.
60554      *   This does not activate the panel, just updates its content.
60555      */
60556     refresh : function(){
60557         if(this.refreshDelegate){
60558            this.loaded = false;
60559            this.refreshDelegate();
60560         }
60561     },
60562     
60563     /**
60564      * Destroys this panel
60565      */
60566     destroy : function(){
60567         this.el.removeAllListeners();
60568         var tempEl = document.createElement("span");
60569         tempEl.appendChild(this.el.dom);
60570         tempEl.innerHTML = "";
60571         this.el.remove();
60572         this.el = null;
60573     },
60574     
60575     /**
60576      * form - if the content panel contains a form - this is a reference to it.
60577      * @type {Roo.form.Form}
60578      */
60579     form : false,
60580     /**
60581      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
60582      *    This contains a reference to it.
60583      * @type {Roo.View}
60584      */
60585     view : false,
60586     
60587       /**
60588      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
60589      * <pre><code>
60590
60591 layout.addxtype({
60592        xtype : 'Form',
60593        items: [ .... ]
60594    }
60595 );
60596
60597 </code></pre>
60598      * @param {Object} cfg Xtype definition of item to add.
60599      */
60600     
60601     addxtype : function(cfg) {
60602         // add form..
60603         if (cfg.xtype.match(/^Form$/)) {
60604             
60605             var el;
60606             //if (this.footer) {
60607             //    el = this.footer.container.insertSibling(false, 'before');
60608             //} else {
60609                 el = this.el.createChild();
60610             //}
60611
60612             this.form = new  Roo.form.Form(cfg);
60613             
60614             
60615             if ( this.form.allItems.length) {
60616                 this.form.render(el.dom);
60617             }
60618             return this.form;
60619         }
60620         // should only have one of theses..
60621         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
60622             // views.. should not be just added - used named prop 'view''
60623             
60624             cfg.el = this.el.appendChild(document.createElement("div"));
60625             // factory?
60626             
60627             var ret = new Roo.factory(cfg);
60628              
60629              ret.render && ret.render(false, ''); // render blank..
60630             this.view = ret;
60631             return ret;
60632         }
60633         return false;
60634     }
60635 });
60636
60637
60638
60639
60640
60641
60642
60643
60644
60645
60646
60647
60648 /**
60649  * @class Roo.GridPanel
60650  * @extends Roo.ContentPanel
60651  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60652  * @constructor
60653  * Create a new GridPanel.
60654  * @cfg {Roo.grid.Grid} grid The grid for this panel
60655  */
60656 Roo.GridPanel = function(grid, config){
60657     
60658     // universal ctor...
60659     if (typeof(grid.grid) != 'undefined') {
60660         config = grid;
60661         grid = config.grid;
60662     }
60663     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
60664         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
60665         
60666     this.wrapper.dom.appendChild(grid.getGridEl().dom);
60667     
60668     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
60669     
60670     if(this.toolbar){
60671         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
60672     }
60673     // xtype created footer. - not sure if will work as we normally have to render first..
60674     if (this.footer && !this.footer.el && this.footer.xtype) {
60675         
60676         this.footer.container = this.grid.getView().getFooterPanel(true);
60677         this.footer.dataSource = this.grid.dataSource;
60678         this.footer = Roo.factory(this.footer, Roo);
60679         
60680     }
60681     
60682     grid.monitorWindowResize = false; // turn off autosizing
60683     grid.autoHeight = false;
60684     grid.autoWidth = false;
60685     this.grid = grid;
60686     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
60687 };
60688
60689 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
60690     getId : function(){
60691         return this.grid.id;
60692     },
60693     
60694     /**
60695      * Returns the grid for this panel
60696      * @return {Roo.grid.Grid} 
60697      */
60698     getGrid : function(){
60699         return this.grid;    
60700     },
60701     
60702     setSize : function(width, height){
60703         if(!this.ignoreResize(width, height)){
60704             var grid = this.grid;
60705             var size = this.adjustForComponents(width, height);
60706             grid.getGridEl().setSize(size.width, size.height);
60707             grid.autoSize();
60708         }
60709     },
60710     
60711     beforeSlide : function(){
60712         this.grid.getView().scroller.clip();
60713     },
60714     
60715     afterSlide : function(){
60716         this.grid.getView().scroller.unclip();
60717     },
60718     
60719     destroy : function(){
60720         this.grid.destroy();
60721         delete this.grid;
60722         Roo.GridPanel.superclass.destroy.call(this); 
60723     }
60724 });
60725
60726
60727 /**
60728  * @class Roo.NestedLayoutPanel
60729  * @extends Roo.ContentPanel
60730  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60731  * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
60732  *
60733  * 
60734  * @constructor
60735  * Create a new NestedLayoutPanel.
60736  * 
60737  * 
60738  * @param {Roo.BorderLayout} layout [required] The layout for this panel
60739  * @param {String/Object} config A string to set only the title or a config object
60740  */
60741 Roo.NestedLayoutPanel = function(layout, config)
60742 {
60743     // construct with only one argument..
60744     /* FIXME - implement nicer consturctors
60745     if (layout.layout) {
60746         config = layout;
60747         layout = config.layout;
60748         delete config.layout;
60749     }
60750     if (layout.xtype && !layout.getEl) {
60751         // then layout needs constructing..
60752         layout = Roo.factory(layout, Roo);
60753     }
60754     */
60755     
60756     
60757     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
60758     
60759     layout.monitorWindowResize = false; // turn off autosizing
60760     this.layout = layout;
60761     this.layout.getEl().addClass("x-layout-nested-layout");
60762     
60763     
60764     
60765     
60766 };
60767
60768 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
60769
60770     layout : false,
60771
60772     setSize : function(width, height){
60773         if(!this.ignoreResize(width, height)){
60774             var size = this.adjustForComponents(width, height);
60775             var el = this.layout.getEl();
60776             el.setSize(size.width, size.height);
60777             var touch = el.dom.offsetWidth;
60778             this.layout.layout();
60779             // ie requires a double layout on the first pass
60780             if(Roo.isIE && !this.initialized){
60781                 this.initialized = true;
60782                 this.layout.layout();
60783             }
60784         }
60785     },
60786     
60787     // activate all subpanels if not currently active..
60788     
60789     setActiveState : function(active){
60790         this.active = active;
60791         if(!active){
60792             this.fireEvent("deactivate", this);
60793             return;
60794         }
60795         
60796         this.fireEvent("activate", this);
60797         // not sure if this should happen before or after..
60798         if (!this.layout) {
60799             return; // should not happen..
60800         }
60801         var reg = false;
60802         for (var r in this.layout.regions) {
60803             reg = this.layout.getRegion(r);
60804             if (reg.getActivePanel()) {
60805                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
60806                 reg.setActivePanel(reg.getActivePanel());
60807                 continue;
60808             }
60809             if (!reg.panels.length) {
60810                 continue;
60811             }
60812             reg.showPanel(reg.getPanel(0));
60813         }
60814         
60815         
60816         
60817         
60818     },
60819     
60820     /**
60821      * Returns the nested BorderLayout for this panel
60822      * @return {Roo.BorderLayout}
60823      */
60824     getLayout : function(){
60825         return this.layout;
60826     },
60827     
60828      /**
60829      * Adds a xtype elements to the layout of the nested panel
60830      * <pre><code>
60831
60832 panel.addxtype({
60833        xtype : 'ContentPanel',
60834        region: 'west',
60835        items: [ .... ]
60836    }
60837 );
60838
60839 panel.addxtype({
60840         xtype : 'NestedLayoutPanel',
60841         region: 'west',
60842         layout: {
60843            center: { },
60844            west: { }   
60845         },
60846         items : [ ... list of content panels or nested layout panels.. ]
60847    }
60848 );
60849 </code></pre>
60850      * @param {Object} cfg Xtype definition of item to add.
60851      */
60852     addxtype : function(cfg) {
60853         return this.layout.addxtype(cfg);
60854     
60855     }
60856 });
60857
60858 Roo.ScrollPanel = function(el, config, content){
60859     config = config || {};
60860     config.fitToFrame = true;
60861     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
60862     
60863     this.el.dom.style.overflow = "hidden";
60864     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
60865     this.el.removeClass("x-layout-inactive-content");
60866     this.el.on("mousewheel", this.onWheel, this);
60867
60868     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
60869     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
60870     up.unselectable(); down.unselectable();
60871     up.on("click", this.scrollUp, this);
60872     down.on("click", this.scrollDown, this);
60873     up.addClassOnOver("x-scroller-btn-over");
60874     down.addClassOnOver("x-scroller-btn-over");
60875     up.addClassOnClick("x-scroller-btn-click");
60876     down.addClassOnClick("x-scroller-btn-click");
60877     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
60878
60879     this.resizeEl = this.el;
60880     this.el = wrap; this.up = up; this.down = down;
60881 };
60882
60883 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
60884     increment : 100,
60885     wheelIncrement : 5,
60886     scrollUp : function(){
60887         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
60888     },
60889
60890     scrollDown : function(){
60891         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
60892     },
60893
60894     afterScroll : function(){
60895         var el = this.resizeEl;
60896         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
60897         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
60898         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
60899     },
60900
60901     setSize : function(){
60902         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
60903         this.afterScroll();
60904     },
60905
60906     onWheel : function(e){
60907         var d = e.getWheelDelta();
60908         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
60909         this.afterScroll();
60910         e.stopEvent();
60911     },
60912
60913     setContent : function(content, loadScripts){
60914         this.resizeEl.update(content, loadScripts);
60915     }
60916
60917 });
60918
60919
60920
60921 /**
60922  * @class Roo.TreePanel
60923  * @extends Roo.ContentPanel
60924  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60925  * Treepanel component
60926  * 
60927  * @constructor
60928  * Create a new TreePanel. - defaults to fit/scoll contents.
60929  * @param {String/Object} config A string to set only the panel's title, or a config object
60930  */
60931 Roo.TreePanel = function(config){
60932     var el = config.el;
60933     var tree = config.tree;
60934     delete config.tree; 
60935     delete config.el; // hopefull!
60936     
60937     // wrapper for IE7 strict & safari scroll issue
60938     
60939     var treeEl = el.createChild();
60940     config.resizeEl = treeEl;
60941     
60942     
60943     
60944     Roo.TreePanel.superclass.constructor.call(this, el, config);
60945  
60946  
60947     this.tree = new Roo.tree.TreePanel(treeEl , tree);
60948     //console.log(tree);
60949     this.on('activate', function()
60950     {
60951         if (this.tree.rendered) {
60952             return;
60953         }
60954         //console.log('render tree');
60955         this.tree.render();
60956     });
60957     // this should not be needed.. - it's actually the 'el' that resizes?
60958     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
60959     
60960     //this.on('resize',  function (cp, w, h) {
60961     //        this.tree.innerCt.setWidth(w);
60962     //        this.tree.innerCt.setHeight(h);
60963     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
60964     //});
60965
60966         
60967     
60968 };
60969
60970 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
60971     fitToFrame : true,
60972     autoScroll : true,
60973     /*
60974      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
60975      */
60976     tree : false
60977
60978 });
60979 /*
60980  * Based on:
60981  * Ext JS Library 1.1.1
60982  * Copyright(c) 2006-2007, Ext JS, LLC.
60983  *
60984  * Originally Released Under LGPL - original licence link has changed is not relivant.
60985  *
60986  * Fork - LGPL
60987  * <script type="text/javascript">
60988  */
60989  
60990
60991 /**
60992  * @class Roo.ReaderLayout
60993  * @extends Roo.BorderLayout
60994  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
60995  * center region containing two nested regions (a top one for a list view and one for item preview below),
60996  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
60997  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
60998  * expedites the setup of the overall layout and regions for this common application style.
60999  * Example:
61000  <pre><code>
61001 var reader = new Roo.ReaderLayout();
61002 var CP = Roo.ContentPanel;  // shortcut for adding
61003
61004 reader.beginUpdate();
61005 reader.add("north", new CP("north", "North"));
61006 reader.add("west", new CP("west", {title: "West"}));
61007 reader.add("east", new CP("east", {title: "East"}));
61008
61009 reader.regions.listView.add(new CP("listView", "List"));
61010 reader.regions.preview.add(new CP("preview", "Preview"));
61011 reader.endUpdate();
61012 </code></pre>
61013 * @constructor
61014 * Create a new ReaderLayout
61015 * @param {Object} config Configuration options
61016 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
61017 * document.body if omitted)
61018 */
61019 Roo.ReaderLayout = function(config, renderTo){
61020     var c = config || {size:{}};
61021     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
61022         north: c.north !== false ? Roo.apply({
61023             split:false,
61024             initialSize: 32,
61025             titlebar: false
61026         }, c.north) : false,
61027         west: c.west !== false ? Roo.apply({
61028             split:true,
61029             initialSize: 200,
61030             minSize: 175,
61031             maxSize: 400,
61032             titlebar: true,
61033             collapsible: true,
61034             animate: true,
61035             margins:{left:5,right:0,bottom:5,top:5},
61036             cmargins:{left:5,right:5,bottom:5,top:5}
61037         }, c.west) : false,
61038         east: c.east !== false ? Roo.apply({
61039             split:true,
61040             initialSize: 200,
61041             minSize: 175,
61042             maxSize: 400,
61043             titlebar: true,
61044             collapsible: true,
61045             animate: true,
61046             margins:{left:0,right:5,bottom:5,top:5},
61047             cmargins:{left:5,right:5,bottom:5,top:5}
61048         }, c.east) : false,
61049         center: Roo.apply({
61050             tabPosition: 'top',
61051             autoScroll:false,
61052             closeOnTab: true,
61053             titlebar:false,
61054             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
61055         }, c.center)
61056     });
61057
61058     this.el.addClass('x-reader');
61059
61060     this.beginUpdate();
61061
61062     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
61063         south: c.preview !== false ? Roo.apply({
61064             split:true,
61065             initialSize: 200,
61066             minSize: 100,
61067             autoScroll:true,
61068             collapsible:true,
61069             titlebar: true,
61070             cmargins:{top:5,left:0, right:0, bottom:0}
61071         }, c.preview) : false,
61072         center: Roo.apply({
61073             autoScroll:false,
61074             titlebar:false,
61075             minHeight:200
61076         }, c.listView)
61077     });
61078     this.add('center', new Roo.NestedLayoutPanel(inner,
61079             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
61080
61081     this.endUpdate();
61082
61083     this.regions.preview = inner.getRegion('south');
61084     this.regions.listView = inner.getRegion('center');
61085 };
61086
61087 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
61088  * Based on:
61089  * Ext JS Library 1.1.1
61090  * Copyright(c) 2006-2007, Ext JS, LLC.
61091  *
61092  * Originally Released Under LGPL - original licence link has changed is not relivant.
61093  *
61094  * Fork - LGPL
61095  * <script type="text/javascript">
61096  */
61097  
61098 /**
61099  * @class Roo.grid.Grid
61100  * @extends Roo.util.Observable
61101  * This class represents the primary interface of a component based grid control.
61102  * <br><br>Usage:<pre><code>
61103  var grid = new Roo.grid.Grid("my-container-id", {
61104      ds: myDataStore,
61105      cm: myColModel,
61106      selModel: mySelectionModel,
61107      autoSizeColumns: true,
61108      monitorWindowResize: false,
61109      trackMouseOver: true
61110  });
61111  // set any options
61112  grid.render();
61113  * </code></pre>
61114  * <b>Common Problems:</b><br/>
61115  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
61116  * element will correct this<br/>
61117  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
61118  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
61119  * are unpredictable.<br/>
61120  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
61121  * grid to calculate dimensions/offsets.<br/>
61122   * @constructor
61123  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61124  * The container MUST have some type of size defined for the grid to fill. The container will be
61125  * automatically set to position relative if it isn't already.
61126  * @param {Object} config A config object that sets properties on this grid.
61127  */
61128 Roo.grid.Grid = function(container, config){
61129         // initialize the container
61130         this.container = Roo.get(container);
61131         this.container.update("");
61132         this.container.setStyle("overflow", "hidden");
61133     this.container.addClass('x-grid-container');
61134
61135     this.id = this.container.id;
61136
61137     Roo.apply(this, config);
61138     // check and correct shorthanded configs
61139     if(this.ds){
61140         this.dataSource = this.ds;
61141         delete this.ds;
61142     }
61143     if(this.cm){
61144         this.colModel = this.cm;
61145         delete this.cm;
61146     }
61147     if(this.sm){
61148         this.selModel = this.sm;
61149         delete this.sm;
61150     }
61151
61152     if (this.selModel) {
61153         this.selModel = Roo.factory(this.selModel, Roo.grid);
61154         this.sm = this.selModel;
61155         this.sm.xmodule = this.xmodule || false;
61156     }
61157     if (typeof(this.colModel.config) == 'undefined') {
61158         this.colModel = new Roo.grid.ColumnModel(this.colModel);
61159         this.cm = this.colModel;
61160         this.cm.xmodule = this.xmodule || false;
61161     }
61162     if (this.dataSource) {
61163         this.dataSource= Roo.factory(this.dataSource, Roo.data);
61164         this.ds = this.dataSource;
61165         this.ds.xmodule = this.xmodule || false;
61166          
61167     }
61168     
61169     
61170     
61171     if(this.width){
61172         this.container.setWidth(this.width);
61173     }
61174
61175     if(this.height){
61176         this.container.setHeight(this.height);
61177     }
61178     /** @private */
61179         this.addEvents({
61180         // raw events
61181         /**
61182          * @event click
61183          * The raw click event for the entire grid.
61184          * @param {Roo.EventObject} e
61185          */
61186         "click" : true,
61187         /**
61188          * @event dblclick
61189          * The raw dblclick event for the entire grid.
61190          * @param {Roo.EventObject} e
61191          */
61192         "dblclick" : true,
61193         /**
61194          * @event contextmenu
61195          * The raw contextmenu event for the entire grid.
61196          * @param {Roo.EventObject} e
61197          */
61198         "contextmenu" : true,
61199         /**
61200          * @event mousedown
61201          * The raw mousedown event for the entire grid.
61202          * @param {Roo.EventObject} e
61203          */
61204         "mousedown" : true,
61205         /**
61206          * @event mouseup
61207          * The raw mouseup event for the entire grid.
61208          * @param {Roo.EventObject} e
61209          */
61210         "mouseup" : true,
61211         /**
61212          * @event mouseover
61213          * The raw mouseover event for the entire grid.
61214          * @param {Roo.EventObject} e
61215          */
61216         "mouseover" : true,
61217         /**
61218          * @event mouseout
61219          * The raw mouseout event for the entire grid.
61220          * @param {Roo.EventObject} e
61221          */
61222         "mouseout" : true,
61223         /**
61224          * @event keypress
61225          * The raw keypress event for the entire grid.
61226          * @param {Roo.EventObject} e
61227          */
61228         "keypress" : true,
61229         /**
61230          * @event keydown
61231          * The raw keydown event for the entire grid.
61232          * @param {Roo.EventObject} e
61233          */
61234         "keydown" : true,
61235
61236         // custom events
61237
61238         /**
61239          * @event cellclick
61240          * Fires when a cell is clicked
61241          * @param {Grid} this
61242          * @param {Number} rowIndex
61243          * @param {Number} columnIndex
61244          * @param {Roo.EventObject} e
61245          */
61246         "cellclick" : true,
61247         /**
61248          * @event celldblclick
61249          * Fires when a cell is double clicked
61250          * @param {Grid} this
61251          * @param {Number} rowIndex
61252          * @param {Number} columnIndex
61253          * @param {Roo.EventObject} e
61254          */
61255         "celldblclick" : true,
61256         /**
61257          * @event rowclick
61258          * Fires when a row is clicked
61259          * @param {Grid} this
61260          * @param {Number} rowIndex
61261          * @param {Roo.EventObject} e
61262          */
61263         "rowclick" : true,
61264         /**
61265          * @event rowdblclick
61266          * Fires when a row is double clicked
61267          * @param {Grid} this
61268          * @param {Number} rowIndex
61269          * @param {Roo.EventObject} e
61270          */
61271         "rowdblclick" : true,
61272         /**
61273          * @event headerclick
61274          * Fires when a header is clicked
61275          * @param {Grid} this
61276          * @param {Number} columnIndex
61277          * @param {Roo.EventObject} e
61278          */
61279         "headerclick" : true,
61280         /**
61281          * @event headerdblclick
61282          * Fires when a header cell is double clicked
61283          * @param {Grid} this
61284          * @param {Number} columnIndex
61285          * @param {Roo.EventObject} e
61286          */
61287         "headerdblclick" : true,
61288         /**
61289          * @event rowcontextmenu
61290          * Fires when a row is right clicked
61291          * @param {Grid} this
61292          * @param {Number} rowIndex
61293          * @param {Roo.EventObject} e
61294          */
61295         "rowcontextmenu" : true,
61296         /**
61297          * @event cellcontextmenu
61298          * Fires when a cell is right clicked
61299          * @param {Grid} this
61300          * @param {Number} rowIndex
61301          * @param {Number} cellIndex
61302          * @param {Roo.EventObject} e
61303          */
61304          "cellcontextmenu" : true,
61305         /**
61306          * @event headercontextmenu
61307          * Fires when a header is right clicked
61308          * @param {Grid} this
61309          * @param {Number} columnIndex
61310          * @param {Roo.EventObject} e
61311          */
61312         "headercontextmenu" : true,
61313         /**
61314          * @event bodyscroll
61315          * Fires when the body element is scrolled
61316          * @param {Number} scrollLeft
61317          * @param {Number} scrollTop
61318          */
61319         "bodyscroll" : true,
61320         /**
61321          * @event columnresize
61322          * Fires when the user resizes a column
61323          * @param {Number} columnIndex
61324          * @param {Number} newSize
61325          */
61326         "columnresize" : true,
61327         /**
61328          * @event columnmove
61329          * Fires when the user moves a column
61330          * @param {Number} oldIndex
61331          * @param {Number} newIndex
61332          */
61333         "columnmove" : true,
61334         /**
61335          * @event startdrag
61336          * Fires when row(s) start being dragged
61337          * @param {Grid} this
61338          * @param {Roo.GridDD} dd The drag drop object
61339          * @param {event} e The raw browser event
61340          */
61341         "startdrag" : true,
61342         /**
61343          * @event enddrag
61344          * Fires when a drag operation is complete
61345          * @param {Grid} this
61346          * @param {Roo.GridDD} dd The drag drop object
61347          * @param {event} e The raw browser event
61348          */
61349         "enddrag" : true,
61350         /**
61351          * @event dragdrop
61352          * Fires when dragged row(s) are dropped on a valid DD target
61353          * @param {Grid} this
61354          * @param {Roo.GridDD} dd The drag drop object
61355          * @param {String} targetId The target drag drop object
61356          * @param {event} e The raw browser event
61357          */
61358         "dragdrop" : true,
61359         /**
61360          * @event dragover
61361          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61362          * @param {Grid} this
61363          * @param {Roo.GridDD} dd The drag drop object
61364          * @param {String} targetId The target drag drop object
61365          * @param {event} e The raw browser event
61366          */
61367         "dragover" : true,
61368         /**
61369          * @event dragenter
61370          *  Fires when the dragged row(s) first cross another DD target while being dragged
61371          * @param {Grid} this
61372          * @param {Roo.GridDD} dd The drag drop object
61373          * @param {String} targetId The target drag drop object
61374          * @param {event} e The raw browser event
61375          */
61376         "dragenter" : true,
61377         /**
61378          * @event dragout
61379          * Fires when the dragged row(s) leave another DD target while being dragged
61380          * @param {Grid} this
61381          * @param {Roo.GridDD} dd The drag drop object
61382          * @param {String} targetId The target drag drop object
61383          * @param {event} e The raw browser event
61384          */
61385         "dragout" : true,
61386         /**
61387          * @event rowclass
61388          * Fires when a row is rendered, so you can change add a style to it.
61389          * @param {GridView} gridview   The grid view
61390          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
61391          */
61392         'rowclass' : true,
61393
61394         /**
61395          * @event render
61396          * Fires when the grid is rendered
61397          * @param {Grid} grid
61398          */
61399         'render' : true
61400     });
61401
61402     Roo.grid.Grid.superclass.constructor.call(this);
61403 };
61404 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
61405     
61406     /**
61407          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
61408          */
61409         /**
61410          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
61411          */
61412         /**
61413          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
61414          */
61415         /**
61416          * @cfg {Roo.grid.Store} ds The data store for the grid
61417          */
61418         /**
61419          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
61420          */
61421         /**
61422      * @cfg {String} ddGroup - drag drop group.
61423      */
61424       /**
61425      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
61426      */
61427
61428     /**
61429      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
61430      */
61431     minColumnWidth : 25,
61432
61433     /**
61434      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
61435      * <b>on initial render.</b> It is more efficient to explicitly size the columns
61436      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
61437      */
61438     autoSizeColumns : false,
61439
61440     /**
61441      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
61442      */
61443     autoSizeHeaders : true,
61444
61445     /**
61446      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
61447      */
61448     monitorWindowResize : true,
61449
61450     /**
61451      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
61452      * rows measured to get a columns size. Default is 0 (all rows).
61453      */
61454     maxRowsToMeasure : 0,
61455
61456     /**
61457      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
61458      */
61459     trackMouseOver : true,
61460
61461     /**
61462     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
61463     */
61464       /**
61465     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
61466     */
61467     
61468     /**
61469     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
61470     */
61471     enableDragDrop : false,
61472     
61473     /**
61474     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
61475     */
61476     enableColumnMove : true,
61477     
61478     /**
61479     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
61480     */
61481     enableColumnHide : true,
61482     
61483     /**
61484     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
61485     */
61486     enableRowHeightSync : false,
61487     
61488     /**
61489     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
61490     */
61491     stripeRows : true,
61492     
61493     /**
61494     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
61495     */
61496     autoHeight : false,
61497
61498     /**
61499      * @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.
61500      */
61501     autoExpandColumn : false,
61502
61503     /**
61504     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
61505     * Default is 50.
61506     */
61507     autoExpandMin : 50,
61508
61509     /**
61510     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
61511     */
61512     autoExpandMax : 1000,
61513
61514     /**
61515     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
61516     */
61517     view : null,
61518
61519     /**
61520     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
61521     */
61522     loadMask : false,
61523     /**
61524     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
61525     */
61526     dropTarget: false,
61527      /**
61528     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
61529     */ 
61530     sortColMenu : false,
61531     
61532     // private
61533     rendered : false,
61534
61535     /**
61536     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
61537     * of a fixed width. Default is false.
61538     */
61539     /**
61540     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
61541     */
61542     
61543     
61544     /**
61545     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
61546     * %0 is replaced with the number of selected rows.
61547     */
61548     ddText : "{0} selected row{1}",
61549     
61550     
61551     /**
61552      * Called once after all setup has been completed and the grid is ready to be rendered.
61553      * @return {Roo.grid.Grid} this
61554      */
61555     render : function()
61556     {
61557         var c = this.container;
61558         // try to detect autoHeight/width mode
61559         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
61560             this.autoHeight = true;
61561         }
61562         var view = this.getView();
61563         view.init(this);
61564
61565         c.on("click", this.onClick, this);
61566         c.on("dblclick", this.onDblClick, this);
61567         c.on("contextmenu", this.onContextMenu, this);
61568         c.on("keydown", this.onKeyDown, this);
61569         if (Roo.isTouch) {
61570             c.on("touchstart", this.onTouchStart, this);
61571         }
61572
61573         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
61574
61575         this.getSelectionModel().init(this);
61576
61577         view.render();
61578
61579         if(this.loadMask){
61580             this.loadMask = new Roo.LoadMask(this.container,
61581                     Roo.apply({store:this.dataSource}, this.loadMask));
61582         }
61583         
61584         
61585         if (this.toolbar && this.toolbar.xtype) {
61586             this.toolbar.container = this.getView().getHeaderPanel(true);
61587             this.toolbar = new Roo.Toolbar(this.toolbar);
61588         }
61589         if (this.footer && this.footer.xtype) {
61590             this.footer.dataSource = this.getDataSource();
61591             this.footer.container = this.getView().getFooterPanel(true);
61592             this.footer = Roo.factory(this.footer, Roo);
61593         }
61594         if (this.dropTarget && this.dropTarget.xtype) {
61595             delete this.dropTarget.xtype;
61596             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
61597         }
61598         
61599         
61600         this.rendered = true;
61601         this.fireEvent('render', this);
61602         return this;
61603     },
61604
61605     /**
61606      * Reconfigures the grid to use a different Store and Column Model.
61607      * The View will be bound to the new objects and refreshed.
61608      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
61609      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
61610      */
61611     reconfigure : function(dataSource, colModel){
61612         if(this.loadMask){
61613             this.loadMask.destroy();
61614             this.loadMask = new Roo.LoadMask(this.container,
61615                     Roo.apply({store:dataSource}, this.loadMask));
61616         }
61617         this.view.bind(dataSource, colModel);
61618         this.dataSource = dataSource;
61619         this.colModel = colModel;
61620         this.view.refresh(true);
61621     },
61622     /**
61623      * addColumns
61624      * Add's a column, default at the end..
61625      
61626      * @param {int} position to add (default end)
61627      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
61628      */
61629     addColumns : function(pos, ar)
61630     {
61631         
61632         for (var i =0;i< ar.length;i++) {
61633             var cfg = ar[i];
61634             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
61635             this.cm.lookup[cfg.id] = cfg;
61636         }
61637         
61638         
61639         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
61640             pos = this.cm.config.length; //this.cm.config.push(cfg);
61641         } 
61642         pos = Math.max(0,pos);
61643         ar.unshift(0);
61644         ar.unshift(pos);
61645         this.cm.config.splice.apply(this.cm.config, ar);
61646         
61647         
61648         
61649         this.view.generateRules(this.cm);
61650         this.view.refresh(true);
61651         
61652     },
61653     
61654     
61655     
61656     
61657     // private
61658     onKeyDown : function(e){
61659         this.fireEvent("keydown", e);
61660     },
61661
61662     /**
61663      * Destroy this grid.
61664      * @param {Boolean} removeEl True to remove the element
61665      */
61666     destroy : function(removeEl, keepListeners){
61667         if(this.loadMask){
61668             this.loadMask.destroy();
61669         }
61670         var c = this.container;
61671         c.removeAllListeners();
61672         this.view.destroy();
61673         this.colModel.purgeListeners();
61674         if(!keepListeners){
61675             this.purgeListeners();
61676         }
61677         c.update("");
61678         if(removeEl === true){
61679             c.remove();
61680         }
61681     },
61682
61683     // private
61684     processEvent : function(name, e){
61685         // does this fire select???
61686         //Roo.log('grid:processEvent '  + name);
61687         
61688         if (name != 'touchstart' ) {
61689             this.fireEvent(name, e);    
61690         }
61691         
61692         var t = e.getTarget();
61693         var v = this.view;
61694         var header = v.findHeaderIndex(t);
61695         if(header !== false){
61696             var ename = name == 'touchstart' ? 'click' : name;
61697              
61698             this.fireEvent("header" + ename, this, header, e);
61699         }else{
61700             var row = v.findRowIndex(t);
61701             var cell = v.findCellIndex(t);
61702             if (name == 'touchstart') {
61703                 // first touch is always a click.
61704                 // hopefull this happens after selection is updated.?
61705                 name = false;
61706                 
61707                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
61708                     var cs = this.selModel.getSelectedCell();
61709                     if (row == cs[0] && cell == cs[1]){
61710                         name = 'dblclick';
61711                     }
61712                 }
61713                 if (typeof(this.selModel.getSelections) != 'undefined') {
61714                     var cs = this.selModel.getSelections();
61715                     var ds = this.dataSource;
61716                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
61717                         name = 'dblclick';
61718                     }
61719                 }
61720                 if (!name) {
61721                     return;
61722                 }
61723             }
61724             
61725             
61726             if(row !== false){
61727                 this.fireEvent("row" + name, this, row, e);
61728                 if(cell !== false){
61729                     this.fireEvent("cell" + name, this, row, cell, e);
61730                 }
61731             }
61732         }
61733     },
61734
61735     // private
61736     onClick : function(e){
61737         this.processEvent("click", e);
61738     },
61739    // private
61740     onTouchStart : function(e){
61741         this.processEvent("touchstart", e);
61742     },
61743
61744     // private
61745     onContextMenu : function(e, t){
61746         this.processEvent("contextmenu", e);
61747     },
61748
61749     // private
61750     onDblClick : function(e){
61751         this.processEvent("dblclick", e);
61752     },
61753
61754     // private
61755     walkCells : function(row, col, step, fn, scope){
61756         var cm = this.colModel, clen = cm.getColumnCount();
61757         var ds = this.dataSource, rlen = ds.getCount(), first = true;
61758         if(step < 0){
61759             if(col < 0){
61760                 row--;
61761                 first = false;
61762             }
61763             while(row >= 0){
61764                 if(!first){
61765                     col = clen-1;
61766                 }
61767                 first = false;
61768                 while(col >= 0){
61769                     if(fn.call(scope || this, row, col, cm) === true){
61770                         return [row, col];
61771                     }
61772                     col--;
61773                 }
61774                 row--;
61775             }
61776         } else {
61777             if(col >= clen){
61778                 row++;
61779                 first = false;
61780             }
61781             while(row < rlen){
61782                 if(!first){
61783                     col = 0;
61784                 }
61785                 first = false;
61786                 while(col < clen){
61787                     if(fn.call(scope || this, row, col, cm) === true){
61788                         return [row, col];
61789                     }
61790                     col++;
61791                 }
61792                 row++;
61793             }
61794         }
61795         return null;
61796     },
61797
61798     // private
61799     getSelections : function(){
61800         return this.selModel.getSelections();
61801     },
61802
61803     /**
61804      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
61805      * but if manual update is required this method will initiate it.
61806      */
61807     autoSize : function(){
61808         if(this.rendered){
61809             this.view.layout();
61810             if(this.view.adjustForScroll){
61811                 this.view.adjustForScroll();
61812             }
61813         }
61814     },
61815
61816     /**
61817      * Returns the grid's underlying element.
61818      * @return {Element} The element
61819      */
61820     getGridEl : function(){
61821         return this.container;
61822     },
61823
61824     // private for compatibility, overridden by editor grid
61825     stopEditing : function(){},
61826
61827     /**
61828      * Returns the grid's SelectionModel.
61829      * @return {SelectionModel}
61830      */
61831     getSelectionModel : function(){
61832         if(!this.selModel){
61833             this.selModel = new Roo.grid.RowSelectionModel();
61834         }
61835         return this.selModel;
61836     },
61837
61838     /**
61839      * Returns the grid's DataSource.
61840      * @return {DataSource}
61841      */
61842     getDataSource : function(){
61843         return this.dataSource;
61844     },
61845
61846     /**
61847      * Returns the grid's ColumnModel.
61848      * @return {ColumnModel}
61849      */
61850     getColumnModel : function(){
61851         return this.colModel;
61852     },
61853
61854     /**
61855      * Returns the grid's GridView object.
61856      * @return {GridView}
61857      */
61858     getView : function(){
61859         if(!this.view){
61860             this.view = new Roo.grid.GridView(this.viewConfig);
61861             this.relayEvents(this.view, [
61862                 "beforerowremoved", "beforerowsinserted",
61863                 "beforerefresh", "rowremoved",
61864                 "rowsinserted", "rowupdated" ,"refresh"
61865             ]);
61866         }
61867         return this.view;
61868     },
61869     /**
61870      * Called to get grid's drag proxy text, by default returns this.ddText.
61871      * Override this to put something different in the dragged text.
61872      * @return {String}
61873      */
61874     getDragDropText : function(){
61875         var count = this.selModel.getCount();
61876         return String.format(this.ddText, count, count == 1 ? '' : 's');
61877     }
61878 });
61879 /*
61880  * Based on:
61881  * Ext JS Library 1.1.1
61882  * Copyright(c) 2006-2007, Ext JS, LLC.
61883  *
61884  * Originally Released Under LGPL - original licence link has changed is not relivant.
61885  *
61886  * Fork - LGPL
61887  * <script type="text/javascript">
61888  */
61889  /**
61890  * @class Roo.grid.AbstractGridView
61891  * @extends Roo.util.Observable
61892  * @abstract
61893  * Abstract base class for grid Views
61894  * @constructor
61895  */
61896 Roo.grid.AbstractGridView = function(){
61897         this.grid = null;
61898         
61899         this.events = {
61900             "beforerowremoved" : true,
61901             "beforerowsinserted" : true,
61902             "beforerefresh" : true,
61903             "rowremoved" : true,
61904             "rowsinserted" : true,
61905             "rowupdated" : true,
61906             "refresh" : true
61907         };
61908     Roo.grid.AbstractGridView.superclass.constructor.call(this);
61909 };
61910
61911 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
61912     rowClass : "x-grid-row",
61913     cellClass : "x-grid-cell",
61914     tdClass : "x-grid-td",
61915     hdClass : "x-grid-hd",
61916     splitClass : "x-grid-hd-split",
61917     
61918     init: function(grid){
61919         this.grid = grid;
61920                 var cid = this.grid.getGridEl().id;
61921         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
61922         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
61923         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
61924         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
61925         },
61926         
61927     getColumnRenderers : function(){
61928         var renderers = [];
61929         var cm = this.grid.colModel;
61930         var colCount = cm.getColumnCount();
61931         for(var i = 0; i < colCount; i++){
61932             renderers[i] = cm.getRenderer(i);
61933         }
61934         return renderers;
61935     },
61936     
61937     getColumnIds : function(){
61938         var ids = [];
61939         var cm = this.grid.colModel;
61940         var colCount = cm.getColumnCount();
61941         for(var i = 0; i < colCount; i++){
61942             ids[i] = cm.getColumnId(i);
61943         }
61944         return ids;
61945     },
61946     
61947     getDataIndexes : function(){
61948         if(!this.indexMap){
61949             this.indexMap = this.buildIndexMap();
61950         }
61951         return this.indexMap.colToData;
61952     },
61953     
61954     getColumnIndexByDataIndex : function(dataIndex){
61955         if(!this.indexMap){
61956             this.indexMap = this.buildIndexMap();
61957         }
61958         return this.indexMap.dataToCol[dataIndex];
61959     },
61960     
61961     /**
61962      * Set a css style for a column dynamically. 
61963      * @param {Number} colIndex The index of the column
61964      * @param {String} name The css property name
61965      * @param {String} value The css value
61966      */
61967     setCSSStyle : function(colIndex, name, value){
61968         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
61969         Roo.util.CSS.updateRule(selector, name, value);
61970     },
61971     
61972     generateRules : function(cm){
61973         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
61974         Roo.util.CSS.removeStyleSheet(rulesId);
61975         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
61976             var cid = cm.getColumnId(i);
61977             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
61978                          this.tdSelector, cid, " {\n}\n",
61979                          this.hdSelector, cid, " {\n}\n",
61980                          this.splitSelector, cid, " {\n}\n");
61981         }
61982         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
61983     }
61984 });/*
61985  * Based on:
61986  * Ext JS Library 1.1.1
61987  * Copyright(c) 2006-2007, Ext JS, LLC.
61988  *
61989  * Originally Released Under LGPL - original licence link has changed is not relivant.
61990  *
61991  * Fork - LGPL
61992  * <script type="text/javascript">
61993  */
61994
61995 // private
61996 // This is a support class used internally by the Grid components
61997 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
61998     this.grid = grid;
61999     this.view = grid.getView();
62000     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62001     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
62002     if(hd2){
62003         this.setHandleElId(Roo.id(hd));
62004         this.setOuterHandleElId(Roo.id(hd2));
62005     }
62006     this.scroll = false;
62007 };
62008 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
62009     maxDragWidth: 120,
62010     getDragData : function(e){
62011         var t = Roo.lib.Event.getTarget(e);
62012         var h = this.view.findHeaderCell(t);
62013         if(h){
62014             return {ddel: h.firstChild, header:h};
62015         }
62016         return false;
62017     },
62018
62019     onInitDrag : function(e){
62020         this.view.headersDisabled = true;
62021         var clone = this.dragData.ddel.cloneNode(true);
62022         clone.id = Roo.id();
62023         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
62024         this.proxy.update(clone);
62025         return true;
62026     },
62027
62028     afterValidDrop : function(){
62029         var v = this.view;
62030         setTimeout(function(){
62031             v.headersDisabled = false;
62032         }, 50);
62033     },
62034
62035     afterInvalidDrop : function(){
62036         var v = this.view;
62037         setTimeout(function(){
62038             v.headersDisabled = false;
62039         }, 50);
62040     }
62041 });
62042 /*
62043  * Based on:
62044  * Ext JS Library 1.1.1
62045  * Copyright(c) 2006-2007, Ext JS, LLC.
62046  *
62047  * Originally Released Under LGPL - original licence link has changed is not relivant.
62048  *
62049  * Fork - LGPL
62050  * <script type="text/javascript">
62051  */
62052 // private
62053 // This is a support class used internally by the Grid components
62054 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
62055     this.grid = grid;
62056     this.view = grid.getView();
62057     // split the proxies so they don't interfere with mouse events
62058     this.proxyTop = Roo.DomHelper.append(document.body, {
62059         cls:"col-move-top", html:"&#160;"
62060     }, true);
62061     this.proxyBottom = Roo.DomHelper.append(document.body, {
62062         cls:"col-move-bottom", html:"&#160;"
62063     }, true);
62064     this.proxyTop.hide = this.proxyBottom.hide = function(){
62065         this.setLeftTop(-100,-100);
62066         this.setStyle("visibility", "hidden");
62067     };
62068     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62069     // temporarily disabled
62070     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
62071     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
62072 };
62073 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
62074     proxyOffsets : [-4, -9],
62075     fly: Roo.Element.fly,
62076
62077     getTargetFromEvent : function(e){
62078         var t = Roo.lib.Event.getTarget(e);
62079         var cindex = this.view.findCellIndex(t);
62080         if(cindex !== false){
62081             return this.view.getHeaderCell(cindex);
62082         }
62083         return null;
62084     },
62085
62086     nextVisible : function(h){
62087         var v = this.view, cm = this.grid.colModel;
62088         h = h.nextSibling;
62089         while(h){
62090             if(!cm.isHidden(v.getCellIndex(h))){
62091                 return h;
62092             }
62093             h = h.nextSibling;
62094         }
62095         return null;
62096     },
62097
62098     prevVisible : function(h){
62099         var v = this.view, cm = this.grid.colModel;
62100         h = h.prevSibling;
62101         while(h){
62102             if(!cm.isHidden(v.getCellIndex(h))){
62103                 return h;
62104             }
62105             h = h.prevSibling;
62106         }
62107         return null;
62108     },
62109
62110     positionIndicator : function(h, n, e){
62111         var x = Roo.lib.Event.getPageX(e);
62112         var r = Roo.lib.Dom.getRegion(n.firstChild);
62113         var px, pt, py = r.top + this.proxyOffsets[1];
62114         if((r.right - x) <= (r.right-r.left)/2){
62115             px = r.right+this.view.borderWidth;
62116             pt = "after";
62117         }else{
62118             px = r.left;
62119             pt = "before";
62120         }
62121         var oldIndex = this.view.getCellIndex(h);
62122         var newIndex = this.view.getCellIndex(n);
62123
62124         if(this.grid.colModel.isFixed(newIndex)){
62125             return false;
62126         }
62127
62128         var locked = this.grid.colModel.isLocked(newIndex);
62129
62130         if(pt == "after"){
62131             newIndex++;
62132         }
62133         if(oldIndex < newIndex){
62134             newIndex--;
62135         }
62136         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
62137             return false;
62138         }
62139         px +=  this.proxyOffsets[0];
62140         this.proxyTop.setLeftTop(px, py);
62141         this.proxyTop.show();
62142         if(!this.bottomOffset){
62143             this.bottomOffset = this.view.mainHd.getHeight();
62144         }
62145         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
62146         this.proxyBottom.show();
62147         return pt;
62148     },
62149
62150     onNodeEnter : function(n, dd, e, data){
62151         if(data.header != n){
62152             this.positionIndicator(data.header, n, e);
62153         }
62154     },
62155
62156     onNodeOver : function(n, dd, e, data){
62157         var result = false;
62158         if(data.header != n){
62159             result = this.positionIndicator(data.header, n, e);
62160         }
62161         if(!result){
62162             this.proxyTop.hide();
62163             this.proxyBottom.hide();
62164         }
62165         return result ? this.dropAllowed : this.dropNotAllowed;
62166     },
62167
62168     onNodeOut : function(n, dd, e, data){
62169         this.proxyTop.hide();
62170         this.proxyBottom.hide();
62171     },
62172
62173     onNodeDrop : function(n, dd, e, data){
62174         var h = data.header;
62175         if(h != n){
62176             var cm = this.grid.colModel;
62177             var x = Roo.lib.Event.getPageX(e);
62178             var r = Roo.lib.Dom.getRegion(n.firstChild);
62179             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
62180             var oldIndex = this.view.getCellIndex(h);
62181             var newIndex = this.view.getCellIndex(n);
62182             var locked = cm.isLocked(newIndex);
62183             if(pt == "after"){
62184                 newIndex++;
62185             }
62186             if(oldIndex < newIndex){
62187                 newIndex--;
62188             }
62189             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
62190                 return false;
62191             }
62192             cm.setLocked(oldIndex, locked, true);
62193             cm.moveColumn(oldIndex, newIndex);
62194             this.grid.fireEvent("columnmove", oldIndex, newIndex);
62195             return true;
62196         }
62197         return false;
62198     }
62199 });
62200 /*
62201  * Based on:
62202  * Ext JS Library 1.1.1
62203  * Copyright(c) 2006-2007, Ext JS, LLC.
62204  *
62205  * Originally Released Under LGPL - original licence link has changed is not relivant.
62206  *
62207  * Fork - LGPL
62208  * <script type="text/javascript">
62209  */
62210   
62211 /**
62212  * @class Roo.grid.GridView
62213  * @extends Roo.util.Observable
62214  *
62215  * @constructor
62216  * @param {Object} config
62217  */
62218 Roo.grid.GridView = function(config){
62219     Roo.grid.GridView.superclass.constructor.call(this);
62220     this.el = null;
62221
62222     Roo.apply(this, config);
62223 };
62224
62225 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
62226
62227     unselectable :  'unselectable="on"',
62228     unselectableCls :  'x-unselectable',
62229     
62230     
62231     rowClass : "x-grid-row",
62232
62233     cellClass : "x-grid-col",
62234
62235     tdClass : "x-grid-td",
62236
62237     hdClass : "x-grid-hd",
62238
62239     splitClass : "x-grid-split",
62240
62241     sortClasses : ["sort-asc", "sort-desc"],
62242
62243     enableMoveAnim : false,
62244
62245     hlColor: "C3DAF9",
62246
62247     dh : Roo.DomHelper,
62248
62249     fly : Roo.Element.fly,
62250
62251     css : Roo.util.CSS,
62252
62253     borderWidth: 1,
62254
62255     splitOffset: 3,
62256
62257     scrollIncrement : 22,
62258
62259     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
62260
62261     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
62262
62263     bind : function(ds, cm){
62264         if(this.ds){
62265             this.ds.un("load", this.onLoad, this);
62266             this.ds.un("datachanged", this.onDataChange, this);
62267             this.ds.un("add", this.onAdd, this);
62268             this.ds.un("remove", this.onRemove, this);
62269             this.ds.un("update", this.onUpdate, this);
62270             this.ds.un("clear", this.onClear, this);
62271         }
62272         if(ds){
62273             ds.on("load", this.onLoad, this);
62274             ds.on("datachanged", this.onDataChange, this);
62275             ds.on("add", this.onAdd, this);
62276             ds.on("remove", this.onRemove, this);
62277             ds.on("update", this.onUpdate, this);
62278             ds.on("clear", this.onClear, this);
62279         }
62280         this.ds = ds;
62281
62282         if(this.cm){
62283             this.cm.un("widthchange", this.onColWidthChange, this);
62284             this.cm.un("headerchange", this.onHeaderChange, this);
62285             this.cm.un("hiddenchange", this.onHiddenChange, this);
62286             this.cm.un("columnmoved", this.onColumnMove, this);
62287             this.cm.un("columnlockchange", this.onColumnLock, this);
62288         }
62289         if(cm){
62290             this.generateRules(cm);
62291             cm.on("widthchange", this.onColWidthChange, this);
62292             cm.on("headerchange", this.onHeaderChange, this);
62293             cm.on("hiddenchange", this.onHiddenChange, this);
62294             cm.on("columnmoved", this.onColumnMove, this);
62295             cm.on("columnlockchange", this.onColumnLock, this);
62296         }
62297         this.cm = cm;
62298     },
62299
62300     init: function(grid){
62301         Roo.grid.GridView.superclass.init.call(this, grid);
62302
62303         this.bind(grid.dataSource, grid.colModel);
62304
62305         grid.on("headerclick", this.handleHeaderClick, this);
62306
62307         if(grid.trackMouseOver){
62308             grid.on("mouseover", this.onRowOver, this);
62309             grid.on("mouseout", this.onRowOut, this);
62310         }
62311         grid.cancelTextSelection = function(){};
62312         this.gridId = grid.id;
62313
62314         var tpls = this.templates || {};
62315
62316         if(!tpls.master){
62317             tpls.master = new Roo.Template(
62318                '<div class="x-grid" hidefocus="true">',
62319                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
62320                   '<div class="x-grid-topbar"></div>',
62321                   '<div class="x-grid-scroller"><div></div></div>',
62322                   '<div class="x-grid-locked">',
62323                       '<div class="x-grid-header">{lockedHeader}</div>',
62324                       '<div class="x-grid-body">{lockedBody}</div>',
62325                   "</div>",
62326                   '<div class="x-grid-viewport">',
62327                       '<div class="x-grid-header">{header}</div>',
62328                       '<div class="x-grid-body">{body}</div>',
62329                   "</div>",
62330                   '<div class="x-grid-bottombar"></div>',
62331                  
62332                   '<div class="x-grid-resize-proxy">&#160;</div>',
62333                "</div>"
62334             );
62335             tpls.master.disableformats = true;
62336         }
62337
62338         if(!tpls.header){
62339             tpls.header = new Roo.Template(
62340                '<table border="0" cellspacing="0" cellpadding="0">',
62341                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
62342                "</table>{splits}"
62343             );
62344             tpls.header.disableformats = true;
62345         }
62346         tpls.header.compile();
62347
62348         if(!tpls.hcell){
62349             tpls.hcell = new Roo.Template(
62350                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
62351                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
62352                 "</div></td>"
62353              );
62354              tpls.hcell.disableFormats = true;
62355         }
62356         tpls.hcell.compile();
62357
62358         if(!tpls.hsplit){
62359             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
62360                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
62361             tpls.hsplit.disableFormats = true;
62362         }
62363         tpls.hsplit.compile();
62364
62365         if(!tpls.body){
62366             tpls.body = new Roo.Template(
62367                '<table border="0" cellspacing="0" cellpadding="0">',
62368                "<tbody>{rows}</tbody>",
62369                "</table>"
62370             );
62371             tpls.body.disableFormats = true;
62372         }
62373         tpls.body.compile();
62374
62375         if(!tpls.row){
62376             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
62377             tpls.row.disableFormats = true;
62378         }
62379         tpls.row.compile();
62380
62381         if(!tpls.cell){
62382             tpls.cell = new Roo.Template(
62383                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
62384                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
62385                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
62386                 "</td>"
62387             );
62388             tpls.cell.disableFormats = true;
62389         }
62390         tpls.cell.compile();
62391
62392         this.templates = tpls;
62393     },
62394
62395     // remap these for backwards compat
62396     onColWidthChange : function(){
62397         this.updateColumns.apply(this, arguments);
62398     },
62399     onHeaderChange : function(){
62400         this.updateHeaders.apply(this, arguments);
62401     }, 
62402     onHiddenChange : function(){
62403         this.handleHiddenChange.apply(this, arguments);
62404     },
62405     onColumnMove : function(){
62406         this.handleColumnMove.apply(this, arguments);
62407     },
62408     onColumnLock : function(){
62409         this.handleLockChange.apply(this, arguments);
62410     },
62411
62412     onDataChange : function(){
62413         this.refresh();
62414         this.updateHeaderSortState();
62415     },
62416
62417     onClear : function(){
62418         this.refresh();
62419     },
62420
62421     onUpdate : function(ds, record){
62422         this.refreshRow(record);
62423     },
62424
62425     refreshRow : function(record){
62426         var ds = this.ds, index;
62427         if(typeof record == 'number'){
62428             index = record;
62429             record = ds.getAt(index);
62430         }else{
62431             index = ds.indexOf(record);
62432         }
62433         this.insertRows(ds, index, index, true);
62434         this.onRemove(ds, record, index+1, true);
62435         this.syncRowHeights(index, index);
62436         this.layout();
62437         this.fireEvent("rowupdated", this, index, record);
62438     },
62439
62440     onAdd : function(ds, records, index){
62441         this.insertRows(ds, index, index + (records.length-1));
62442     },
62443
62444     onRemove : function(ds, record, index, isUpdate){
62445         if(isUpdate !== true){
62446             this.fireEvent("beforerowremoved", this, index, record);
62447         }
62448         var bt = this.getBodyTable(), lt = this.getLockedTable();
62449         if(bt.rows[index]){
62450             bt.firstChild.removeChild(bt.rows[index]);
62451         }
62452         if(lt.rows[index]){
62453             lt.firstChild.removeChild(lt.rows[index]);
62454         }
62455         if(isUpdate !== true){
62456             this.stripeRows(index);
62457             this.syncRowHeights(index, index);
62458             this.layout();
62459             this.fireEvent("rowremoved", this, index, record);
62460         }
62461     },
62462
62463     onLoad : function(){
62464         this.scrollToTop();
62465     },
62466
62467     /**
62468      * Scrolls the grid to the top
62469      */
62470     scrollToTop : function(){
62471         if(this.scroller){
62472             this.scroller.dom.scrollTop = 0;
62473             this.syncScroll();
62474         }
62475     },
62476
62477     /**
62478      * Gets a panel in the header of the grid that can be used for toolbars etc.
62479      * After modifying the contents of this panel a call to grid.autoSize() may be
62480      * required to register any changes in size.
62481      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
62482      * @return Roo.Element
62483      */
62484     getHeaderPanel : function(doShow){
62485         if(doShow){
62486             this.headerPanel.show();
62487         }
62488         return this.headerPanel;
62489     },
62490
62491     /**
62492      * Gets a panel in the footer of the grid that can be used for toolbars etc.
62493      * After modifying the contents of this panel a call to grid.autoSize() may be
62494      * required to register any changes in size.
62495      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
62496      * @return Roo.Element
62497      */
62498     getFooterPanel : function(doShow){
62499         if(doShow){
62500             this.footerPanel.show();
62501         }
62502         return this.footerPanel;
62503     },
62504
62505     initElements : function(){
62506         var E = Roo.Element;
62507         var el = this.grid.getGridEl().dom.firstChild;
62508         var cs = el.childNodes;
62509
62510         this.el = new E(el);
62511         
62512          this.focusEl = new E(el.firstChild);
62513         this.focusEl.swallowEvent("click", true);
62514         
62515         this.headerPanel = new E(cs[1]);
62516         this.headerPanel.enableDisplayMode("block");
62517
62518         this.scroller = new E(cs[2]);
62519         this.scrollSizer = new E(this.scroller.dom.firstChild);
62520
62521         this.lockedWrap = new E(cs[3]);
62522         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
62523         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
62524
62525         this.mainWrap = new E(cs[4]);
62526         this.mainHd = new E(this.mainWrap.dom.firstChild);
62527         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
62528
62529         this.footerPanel = new E(cs[5]);
62530         this.footerPanel.enableDisplayMode("block");
62531
62532         this.resizeProxy = new E(cs[6]);
62533
62534         this.headerSelector = String.format(
62535            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
62536            this.lockedHd.id, this.mainHd.id
62537         );
62538
62539         this.splitterSelector = String.format(
62540            '#{0} div.x-grid-split, #{1} div.x-grid-split',
62541            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
62542         );
62543     },
62544     idToCssName : function(s)
62545     {
62546         return s.replace(/[^a-z0-9]+/ig, '-');
62547     },
62548
62549     getHeaderCell : function(index){
62550         return Roo.DomQuery.select(this.headerSelector)[index];
62551     },
62552
62553     getHeaderCellMeasure : function(index){
62554         return this.getHeaderCell(index).firstChild;
62555     },
62556
62557     getHeaderCellText : function(index){
62558         return this.getHeaderCell(index).firstChild.firstChild;
62559     },
62560
62561     getLockedTable : function(){
62562         return this.lockedBody.dom.firstChild;
62563     },
62564
62565     getBodyTable : function(){
62566         return this.mainBody.dom.firstChild;
62567     },
62568
62569     getLockedRow : function(index){
62570         return this.getLockedTable().rows[index];
62571     },
62572
62573     getRow : function(index){
62574         return this.getBodyTable().rows[index];
62575     },
62576
62577     getRowComposite : function(index){
62578         if(!this.rowEl){
62579             this.rowEl = new Roo.CompositeElementLite();
62580         }
62581         var els = [], lrow, mrow;
62582         if(lrow = this.getLockedRow(index)){
62583             els.push(lrow);
62584         }
62585         if(mrow = this.getRow(index)){
62586             els.push(mrow);
62587         }
62588         this.rowEl.elements = els;
62589         return this.rowEl;
62590     },
62591     /**
62592      * Gets the 'td' of the cell
62593      * 
62594      * @param {Integer} rowIndex row to select
62595      * @param {Integer} colIndex column to select
62596      * 
62597      * @return {Object} 
62598      */
62599     getCell : function(rowIndex, colIndex){
62600         var locked = this.cm.getLockedCount();
62601         var source;
62602         if(colIndex < locked){
62603             source = this.lockedBody.dom.firstChild;
62604         }else{
62605             source = this.mainBody.dom.firstChild;
62606             colIndex -= locked;
62607         }
62608         return source.rows[rowIndex].childNodes[colIndex];
62609     },
62610
62611     getCellText : function(rowIndex, colIndex){
62612         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
62613     },
62614
62615     getCellBox : function(cell){
62616         var b = this.fly(cell).getBox();
62617         if(Roo.isOpera){ // opera fails to report the Y
62618             b.y = cell.offsetTop + this.mainBody.getY();
62619         }
62620         return b;
62621     },
62622
62623     getCellIndex : function(cell){
62624         var id = String(cell.className).match(this.cellRE);
62625         if(id){
62626             return parseInt(id[1], 10);
62627         }
62628         return 0;
62629     },
62630
62631     findHeaderIndex : function(n){
62632         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
62633         return r ? this.getCellIndex(r) : false;
62634     },
62635
62636     findHeaderCell : function(n){
62637         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
62638         return r ? r : false;
62639     },
62640
62641     findRowIndex : function(n){
62642         if(!n){
62643             return false;
62644         }
62645         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
62646         return r ? r.rowIndex : false;
62647     },
62648
62649     findCellIndex : function(node){
62650         var stop = this.el.dom;
62651         while(node && node != stop){
62652             if(this.findRE.test(node.className)){
62653                 return this.getCellIndex(node);
62654             }
62655             node = node.parentNode;
62656         }
62657         return false;
62658     },
62659
62660     getColumnId : function(index){
62661         return this.cm.getColumnId(index);
62662     },
62663
62664     getSplitters : function()
62665     {
62666         if(this.splitterSelector){
62667            return Roo.DomQuery.select(this.splitterSelector);
62668         }else{
62669             return null;
62670       }
62671     },
62672
62673     getSplitter : function(index){
62674         return this.getSplitters()[index];
62675     },
62676
62677     onRowOver : function(e, t){
62678         var row;
62679         if((row = this.findRowIndex(t)) !== false){
62680             this.getRowComposite(row).addClass("x-grid-row-over");
62681         }
62682     },
62683
62684     onRowOut : function(e, t){
62685         var row;
62686         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
62687             this.getRowComposite(row).removeClass("x-grid-row-over");
62688         }
62689     },
62690
62691     renderHeaders : function(){
62692         var cm = this.cm;
62693         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
62694         var cb = [], lb = [], sb = [], lsb = [], p = {};
62695         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62696             p.cellId = "x-grid-hd-0-" + i;
62697             p.splitId = "x-grid-csplit-0-" + i;
62698             p.id = cm.getColumnId(i);
62699             p.value = cm.getColumnHeader(i) || "";
62700             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
62701             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
62702             if(!cm.isLocked(i)){
62703                 cb[cb.length] = ct.apply(p);
62704                 sb[sb.length] = st.apply(p);
62705             }else{
62706                 lb[lb.length] = ct.apply(p);
62707                 lsb[lsb.length] = st.apply(p);
62708             }
62709         }
62710         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
62711                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
62712     },
62713
62714     updateHeaders : function(){
62715         var html = this.renderHeaders();
62716         this.lockedHd.update(html[0]);
62717         this.mainHd.update(html[1]);
62718     },
62719
62720     /**
62721      * Focuses the specified row.
62722      * @param {Number} row The row index
62723      */
62724     focusRow : function(row)
62725     {
62726         //Roo.log('GridView.focusRow');
62727         var x = this.scroller.dom.scrollLeft;
62728         this.focusCell(row, 0, false);
62729         this.scroller.dom.scrollLeft = x;
62730     },
62731
62732     /**
62733      * Focuses the specified cell.
62734      * @param {Number} row The row index
62735      * @param {Number} col The column index
62736      * @param {Boolean} hscroll false to disable horizontal scrolling
62737      */
62738     focusCell : function(row, col, hscroll)
62739     {
62740         //Roo.log('GridView.focusCell');
62741         var el = this.ensureVisible(row, col, hscroll);
62742         this.focusEl.alignTo(el, "tl-tl");
62743         if(Roo.isGecko){
62744             this.focusEl.focus();
62745         }else{
62746             this.focusEl.focus.defer(1, this.focusEl);
62747         }
62748     },
62749
62750     /**
62751      * Scrolls the specified cell into view
62752      * @param {Number} row The row index
62753      * @param {Number} col The column index
62754      * @param {Boolean} hscroll false to disable horizontal scrolling
62755      */
62756     ensureVisible : function(row, col, hscroll)
62757     {
62758         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
62759         //return null; //disable for testing.
62760         if(typeof row != "number"){
62761             row = row.rowIndex;
62762         }
62763         if(row < 0 && row >= this.ds.getCount()){
62764             return  null;
62765         }
62766         col = (col !== undefined ? col : 0);
62767         var cm = this.grid.colModel;
62768         while(cm.isHidden(col)){
62769             col++;
62770         }
62771
62772         var el = this.getCell(row, col);
62773         if(!el){
62774             return null;
62775         }
62776         var c = this.scroller.dom;
62777
62778         var ctop = parseInt(el.offsetTop, 10);
62779         var cleft = parseInt(el.offsetLeft, 10);
62780         var cbot = ctop + el.offsetHeight;
62781         var cright = cleft + el.offsetWidth;
62782         
62783         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
62784         var stop = parseInt(c.scrollTop, 10);
62785         var sleft = parseInt(c.scrollLeft, 10);
62786         var sbot = stop + ch;
62787         var sright = sleft + c.clientWidth;
62788         /*
62789         Roo.log('GridView.ensureVisible:' +
62790                 ' ctop:' + ctop +
62791                 ' c.clientHeight:' + c.clientHeight +
62792                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
62793                 ' stop:' + stop +
62794                 ' cbot:' + cbot +
62795                 ' sbot:' + sbot +
62796                 ' ch:' + ch  
62797                 );
62798         */
62799         if(ctop < stop){
62800             c.scrollTop = ctop;
62801             //Roo.log("set scrolltop to ctop DISABLE?");
62802         }else if(cbot > sbot){
62803             //Roo.log("set scrolltop to cbot-ch");
62804             c.scrollTop = cbot-ch;
62805         }
62806         
62807         if(hscroll !== false){
62808             if(cleft < sleft){
62809                 c.scrollLeft = cleft;
62810             }else if(cright > sright){
62811                 c.scrollLeft = cright-c.clientWidth;
62812             }
62813         }
62814          
62815         return el;
62816     },
62817
62818     updateColumns : function(){
62819         this.grid.stopEditing();
62820         var cm = this.grid.colModel, colIds = this.getColumnIds();
62821         //var totalWidth = cm.getTotalWidth();
62822         var pos = 0;
62823         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62824             //if(cm.isHidden(i)) continue;
62825             var w = cm.getColumnWidth(i);
62826             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
62827             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
62828         }
62829         this.updateSplitters();
62830     },
62831
62832     generateRules : function(cm){
62833         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
62834         Roo.util.CSS.removeStyleSheet(rulesId);
62835         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62836             var cid = cm.getColumnId(i);
62837             var align = '';
62838             if(cm.config[i].align){
62839                 align = 'text-align:'+cm.config[i].align+';';
62840             }
62841             var hidden = '';
62842             if(cm.isHidden(i)){
62843                 hidden = 'display:none;';
62844             }
62845             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
62846             ruleBuf.push(
62847                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
62848                     this.hdSelector, cid, " {\n", align, width, "}\n",
62849                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
62850                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
62851         }
62852         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
62853     },
62854
62855     updateSplitters : function(){
62856         var cm = this.cm, s = this.getSplitters();
62857         if(s){ // splitters not created yet
62858             var pos = 0, locked = true;
62859             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62860                 if(cm.isHidden(i)) {
62861                     continue;
62862                 }
62863                 var w = cm.getColumnWidth(i); // make sure it's a number
62864                 if(!cm.isLocked(i) && locked){
62865                     pos = 0;
62866                     locked = false;
62867                 }
62868                 pos += w;
62869                 s[i].style.left = (pos-this.splitOffset) + "px";
62870             }
62871         }
62872     },
62873
62874     handleHiddenChange : function(colModel, colIndex, hidden){
62875         if(hidden){
62876             this.hideColumn(colIndex);
62877         }else{
62878             this.unhideColumn(colIndex);
62879         }
62880     },
62881
62882     hideColumn : function(colIndex){
62883         var cid = this.getColumnId(colIndex);
62884         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
62885         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
62886         if(Roo.isSafari){
62887             this.updateHeaders();
62888         }
62889         this.updateSplitters();
62890         this.layout();
62891     },
62892
62893     unhideColumn : function(colIndex){
62894         var cid = this.getColumnId(colIndex);
62895         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
62896         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
62897
62898         if(Roo.isSafari){
62899             this.updateHeaders();
62900         }
62901         this.updateSplitters();
62902         this.layout();
62903     },
62904
62905     insertRows : function(dm, firstRow, lastRow, isUpdate){
62906         if(firstRow == 0 && lastRow == dm.getCount()-1){
62907             this.refresh();
62908         }else{
62909             if(!isUpdate){
62910                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
62911             }
62912             var s = this.getScrollState();
62913             var markup = this.renderRows(firstRow, lastRow);
62914             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
62915             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
62916             this.restoreScroll(s);
62917             if(!isUpdate){
62918                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
62919                 this.syncRowHeights(firstRow, lastRow);
62920                 this.stripeRows(firstRow);
62921                 this.layout();
62922             }
62923         }
62924     },
62925
62926     bufferRows : function(markup, target, index){
62927         var before = null, trows = target.rows, tbody = target.tBodies[0];
62928         if(index < trows.length){
62929             before = trows[index];
62930         }
62931         var b = document.createElement("div");
62932         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
62933         var rows = b.firstChild.rows;
62934         for(var i = 0, len = rows.length; i < len; i++){
62935             if(before){
62936                 tbody.insertBefore(rows[0], before);
62937             }else{
62938                 tbody.appendChild(rows[0]);
62939             }
62940         }
62941         b.innerHTML = "";
62942         b = null;
62943     },
62944
62945     deleteRows : function(dm, firstRow, lastRow){
62946         if(dm.getRowCount()<1){
62947             this.fireEvent("beforerefresh", this);
62948             this.mainBody.update("");
62949             this.lockedBody.update("");
62950             this.fireEvent("refresh", this);
62951         }else{
62952             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
62953             var bt = this.getBodyTable();
62954             var tbody = bt.firstChild;
62955             var rows = bt.rows;
62956             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
62957                 tbody.removeChild(rows[firstRow]);
62958             }
62959             this.stripeRows(firstRow);
62960             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
62961         }
62962     },
62963
62964     updateRows : function(dataSource, firstRow, lastRow){
62965         var s = this.getScrollState();
62966         this.refresh();
62967         this.restoreScroll(s);
62968     },
62969
62970     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
62971         if(!noRefresh){
62972            this.refresh();
62973         }
62974         this.updateHeaderSortState();
62975     },
62976
62977     getScrollState : function(){
62978         
62979         var sb = this.scroller.dom;
62980         return {left: sb.scrollLeft, top: sb.scrollTop};
62981     },
62982
62983     stripeRows : function(startRow){
62984         if(!this.grid.stripeRows || this.ds.getCount() < 1){
62985             return;
62986         }
62987         startRow = startRow || 0;
62988         var rows = this.getBodyTable().rows;
62989         var lrows = this.getLockedTable().rows;
62990         var cls = ' x-grid-row-alt ';
62991         for(var i = startRow, len = rows.length; i < len; i++){
62992             var row = rows[i], lrow = lrows[i];
62993             var isAlt = ((i+1) % 2 == 0);
62994             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
62995             if(isAlt == hasAlt){
62996                 continue;
62997             }
62998             if(isAlt){
62999                 row.className += " x-grid-row-alt";
63000             }else{
63001                 row.className = row.className.replace("x-grid-row-alt", "");
63002             }
63003             if(lrow){
63004                 lrow.className = row.className;
63005             }
63006         }
63007     },
63008
63009     restoreScroll : function(state){
63010         //Roo.log('GridView.restoreScroll');
63011         var sb = this.scroller.dom;
63012         sb.scrollLeft = state.left;
63013         sb.scrollTop = state.top;
63014         this.syncScroll();
63015     },
63016
63017     syncScroll : function(){
63018         //Roo.log('GridView.syncScroll');
63019         var sb = this.scroller.dom;
63020         var sh = this.mainHd.dom;
63021         var bs = this.mainBody.dom;
63022         var lv = this.lockedBody.dom;
63023         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
63024         lv.scrollTop = bs.scrollTop = sb.scrollTop;
63025     },
63026
63027     handleScroll : function(e){
63028         this.syncScroll();
63029         var sb = this.scroller.dom;
63030         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
63031         e.stopEvent();
63032     },
63033
63034     handleWheel : function(e){
63035         var d = e.getWheelDelta();
63036         this.scroller.dom.scrollTop -= d*22;
63037         // set this here to prevent jumpy scrolling on large tables
63038         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
63039         e.stopEvent();
63040     },
63041
63042     renderRows : function(startRow, endRow){
63043         // pull in all the crap needed to render rows
63044         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
63045         var colCount = cm.getColumnCount();
63046
63047         if(ds.getCount() < 1){
63048             return ["", ""];
63049         }
63050
63051         // build a map for all the columns
63052         var cs = [];
63053         for(var i = 0; i < colCount; i++){
63054             var name = cm.getDataIndex(i);
63055             cs[i] = {
63056                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
63057                 renderer : cm.getRenderer(i),
63058                 id : cm.getColumnId(i),
63059                 locked : cm.isLocked(i),
63060                 has_editor : cm.isCellEditable(i)
63061             };
63062         }
63063
63064         startRow = startRow || 0;
63065         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
63066
63067         // records to render
63068         var rs = ds.getRange(startRow, endRow);
63069
63070         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
63071     },
63072
63073     // As much as I hate to duplicate code, this was branched because FireFox really hates
63074     // [].join("") on strings. The performance difference was substantial enough to
63075     // branch this function
63076     doRender : Roo.isGecko ?
63077             function(cs, rs, ds, startRow, colCount, stripe){
63078                 var ts = this.templates, ct = ts.cell, rt = ts.row;
63079                 // buffers
63080                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63081                 
63082                 var hasListener = this.grid.hasListener('rowclass');
63083                 var rowcfg = {};
63084                 for(var j = 0, len = rs.length; j < len; j++){
63085                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
63086                     for(var i = 0; i < colCount; i++){
63087                         c = cs[i];
63088                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63089                         p.id = c.id;
63090                         p.css = p.attr = "";
63091                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63092                         if(p.value == undefined || p.value === "") {
63093                             p.value = "&#160;";
63094                         }
63095                         if(c.has_editor){
63096                             p.css += ' x-grid-editable-cell';
63097                         }
63098                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
63099                             p.css +=  ' x-grid-dirty-cell';
63100                         }
63101                         var markup = ct.apply(p);
63102                         if(!c.locked){
63103                             cb+= markup;
63104                         }else{
63105                             lcb+= markup;
63106                         }
63107                     }
63108                     var alt = [];
63109                     if(stripe && ((rowIndex+1) % 2 == 0)){
63110                         alt.push("x-grid-row-alt")
63111                     }
63112                     if(r.dirty){
63113                         alt.push(  " x-grid-dirty-row");
63114                     }
63115                     rp.cells = lcb;
63116                     if(this.getRowClass){
63117                         alt.push(this.getRowClass(r, rowIndex));
63118                     }
63119                     if (hasListener) {
63120                         rowcfg = {
63121                              
63122                             record: r,
63123                             rowIndex : rowIndex,
63124                             rowClass : ''
63125                         };
63126                         this.grid.fireEvent('rowclass', this, rowcfg);
63127                         alt.push(rowcfg.rowClass);
63128                     }
63129                     rp.alt = alt.join(" ");
63130                     lbuf+= rt.apply(rp);
63131                     rp.cells = cb;
63132                     buf+=  rt.apply(rp);
63133                 }
63134                 return [lbuf, buf];
63135             } :
63136             function(cs, rs, ds, startRow, colCount, stripe){
63137                 var ts = this.templates, ct = ts.cell, rt = ts.row;
63138                 // buffers
63139                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63140                 var hasListener = this.grid.hasListener('rowclass');
63141  
63142                 var rowcfg = {};
63143                 for(var j = 0, len = rs.length; j < len; j++){
63144                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
63145                     for(var i = 0; i < colCount; i++){
63146                         c = cs[i];
63147                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63148                         p.id = c.id;
63149                         p.css = p.attr = "";
63150                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63151                         if(p.value == undefined || p.value === "") {
63152                             p.value = "&#160;";
63153                         }
63154                         //Roo.log(c);
63155                          if(c.has_editor){
63156                             p.css += ' x-grid-editable-cell';
63157                         }
63158                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
63159                             p.css += ' x-grid-dirty-cell' 
63160                         }
63161                         
63162                         var markup = ct.apply(p);
63163                         if(!c.locked){
63164                             cb[cb.length] = markup;
63165                         }else{
63166                             lcb[lcb.length] = markup;
63167                         }
63168                     }
63169                     var alt = [];
63170                     if(stripe && ((rowIndex+1) % 2 == 0)){
63171                         alt.push( "x-grid-row-alt");
63172                     }
63173                     if(r.dirty){
63174                         alt.push(" x-grid-dirty-row");
63175                     }
63176                     rp.cells = lcb;
63177                     if(this.getRowClass){
63178                         alt.push( this.getRowClass(r, rowIndex));
63179                     }
63180                     if (hasListener) {
63181                         rowcfg = {
63182                              
63183                             record: r,
63184                             rowIndex : rowIndex,
63185                             rowClass : ''
63186                         };
63187                         this.grid.fireEvent('rowclass', this, rowcfg);
63188                         alt.push(rowcfg.rowClass);
63189                     }
63190                     
63191                     rp.alt = alt.join(" ");
63192                     rp.cells = lcb.join("");
63193                     lbuf[lbuf.length] = rt.apply(rp);
63194                     rp.cells = cb.join("");
63195                     buf[buf.length] =  rt.apply(rp);
63196                 }
63197                 return [lbuf.join(""), buf.join("")];
63198             },
63199
63200     renderBody : function(){
63201         var markup = this.renderRows();
63202         var bt = this.templates.body;
63203         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
63204     },
63205
63206     /**
63207      * Refreshes the grid
63208      * @param {Boolean} headersToo
63209      */
63210     refresh : function(headersToo){
63211         this.fireEvent("beforerefresh", this);
63212         this.grid.stopEditing();
63213         var result = this.renderBody();
63214         this.lockedBody.update(result[0]);
63215         this.mainBody.update(result[1]);
63216         if(headersToo === true){
63217             this.updateHeaders();
63218             this.updateColumns();
63219             this.updateSplitters();
63220             this.updateHeaderSortState();
63221         }
63222         this.syncRowHeights();
63223         this.layout();
63224         this.fireEvent("refresh", this);
63225     },
63226
63227     handleColumnMove : function(cm, oldIndex, newIndex){
63228         this.indexMap = null;
63229         var s = this.getScrollState();
63230         this.refresh(true);
63231         this.restoreScroll(s);
63232         this.afterMove(newIndex);
63233     },
63234
63235     afterMove : function(colIndex){
63236         if(this.enableMoveAnim && Roo.enableFx){
63237             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
63238         }
63239         // if multisort - fix sortOrder, and reload..
63240         if (this.grid.dataSource.multiSort) {
63241             // the we can call sort again..
63242             var dm = this.grid.dataSource;
63243             var cm = this.grid.colModel;
63244             var so = [];
63245             for(var i = 0; i < cm.config.length; i++ ) {
63246                 
63247                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
63248                     continue; // dont' bother, it's not in sort list or being set.
63249                 }
63250                 
63251                 so.push(cm.config[i].dataIndex);
63252             };
63253             dm.sortOrder = so;
63254             dm.load(dm.lastOptions);
63255             
63256             
63257         }
63258         
63259     },
63260
63261     updateCell : function(dm, rowIndex, dataIndex){
63262         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
63263         if(typeof colIndex == "undefined"){ // not present in grid
63264             return;
63265         }
63266         var cm = this.grid.colModel;
63267         var cell = this.getCell(rowIndex, colIndex);
63268         var cellText = this.getCellText(rowIndex, colIndex);
63269
63270         var p = {
63271             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
63272             id : cm.getColumnId(colIndex),
63273             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
63274         };
63275         var renderer = cm.getRenderer(colIndex);
63276         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
63277         if(typeof val == "undefined" || val === "") {
63278             val = "&#160;";
63279         }
63280         cellText.innerHTML = val;
63281         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
63282         this.syncRowHeights(rowIndex, rowIndex);
63283     },
63284
63285     calcColumnWidth : function(colIndex, maxRowsToMeasure){
63286         var maxWidth = 0;
63287         if(this.grid.autoSizeHeaders){
63288             var h = this.getHeaderCellMeasure(colIndex);
63289             maxWidth = Math.max(maxWidth, h.scrollWidth);
63290         }
63291         var tb, index;
63292         if(this.cm.isLocked(colIndex)){
63293             tb = this.getLockedTable();
63294             index = colIndex;
63295         }else{
63296             tb = this.getBodyTable();
63297             index = colIndex - this.cm.getLockedCount();
63298         }
63299         if(tb && tb.rows){
63300             var rows = tb.rows;
63301             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
63302             for(var i = 0; i < stopIndex; i++){
63303                 var cell = rows[i].childNodes[index].firstChild;
63304                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
63305             }
63306         }
63307         return maxWidth + /*margin for error in IE*/ 5;
63308     },
63309     /**
63310      * Autofit a column to its content.
63311      * @param {Number} colIndex
63312      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
63313      */
63314      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
63315          if(this.cm.isHidden(colIndex)){
63316              return; // can't calc a hidden column
63317          }
63318         if(forceMinSize){
63319             var cid = this.cm.getColumnId(colIndex);
63320             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
63321            if(this.grid.autoSizeHeaders){
63322                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
63323            }
63324         }
63325         var newWidth = this.calcColumnWidth(colIndex);
63326         this.cm.setColumnWidth(colIndex,
63327             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
63328         if(!suppressEvent){
63329             this.grid.fireEvent("columnresize", colIndex, newWidth);
63330         }
63331     },
63332
63333     /**
63334      * Autofits all columns to their content and then expands to fit any extra space in the grid
63335      */
63336      autoSizeColumns : function(){
63337         var cm = this.grid.colModel;
63338         var colCount = cm.getColumnCount();
63339         for(var i = 0; i < colCount; i++){
63340             this.autoSizeColumn(i, true, true);
63341         }
63342         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
63343             this.fitColumns();
63344         }else{
63345             this.updateColumns();
63346             this.layout();
63347         }
63348     },
63349
63350     /**
63351      * Autofits all columns to the grid's width proportionate with their current size
63352      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
63353      */
63354     fitColumns : function(reserveScrollSpace){
63355         var cm = this.grid.colModel;
63356         var colCount = cm.getColumnCount();
63357         var cols = [];
63358         var width = 0;
63359         var i, w;
63360         for (i = 0; i < colCount; i++){
63361             if(!cm.isHidden(i) && !cm.isFixed(i)){
63362                 w = cm.getColumnWidth(i);
63363                 cols.push(i);
63364                 cols.push(w);
63365                 width += w;
63366             }
63367         }
63368         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
63369         if(reserveScrollSpace){
63370             avail -= 17;
63371         }
63372         var frac = (avail - cm.getTotalWidth())/width;
63373         while (cols.length){
63374             w = cols.pop();
63375             i = cols.pop();
63376             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
63377         }
63378         this.updateColumns();
63379         this.layout();
63380     },
63381
63382     onRowSelect : function(rowIndex){
63383         var row = this.getRowComposite(rowIndex);
63384         row.addClass("x-grid-row-selected");
63385     },
63386
63387     onRowDeselect : function(rowIndex){
63388         var row = this.getRowComposite(rowIndex);
63389         row.removeClass("x-grid-row-selected");
63390     },
63391
63392     onCellSelect : function(row, col){
63393         var cell = this.getCell(row, col);
63394         if(cell){
63395             Roo.fly(cell).addClass("x-grid-cell-selected");
63396         }
63397     },
63398
63399     onCellDeselect : function(row, col){
63400         var cell = this.getCell(row, col);
63401         if(cell){
63402             Roo.fly(cell).removeClass("x-grid-cell-selected");
63403         }
63404     },
63405
63406     updateHeaderSortState : function(){
63407         
63408         // sort state can be single { field: xxx, direction : yyy}
63409         // or   { xxx=>ASC , yyy : DESC ..... }
63410         
63411         var mstate = {};
63412         if (!this.ds.multiSort) { 
63413             var state = this.ds.getSortState();
63414             if(!state){
63415                 return;
63416             }
63417             mstate[state.field] = state.direction;
63418             // FIXME... - this is not used here.. but might be elsewhere..
63419             this.sortState = state;
63420             
63421         } else {
63422             mstate = this.ds.sortToggle;
63423         }
63424         //remove existing sort classes..
63425         
63426         var sc = this.sortClasses;
63427         var hds = this.el.select(this.headerSelector).removeClass(sc);
63428         
63429         for(var f in mstate) {
63430         
63431             var sortColumn = this.cm.findColumnIndex(f);
63432             
63433             if(sortColumn != -1){
63434                 var sortDir = mstate[f];        
63435                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
63436             }
63437         }
63438         
63439          
63440         
63441     },
63442
63443
63444     handleHeaderClick : function(g, index,e){
63445         
63446         Roo.log("header click");
63447         
63448         if (Roo.isTouch) {
63449             // touch events on header are handled by context
63450             this.handleHdCtx(g,index,e);
63451             return;
63452         }
63453         
63454         
63455         if(this.headersDisabled){
63456             return;
63457         }
63458         var dm = g.dataSource, cm = g.colModel;
63459         if(!cm.isSortable(index)){
63460             return;
63461         }
63462         g.stopEditing();
63463         
63464         if (dm.multiSort) {
63465             // update the sortOrder
63466             var so = [];
63467             for(var i = 0; i < cm.config.length; i++ ) {
63468                 
63469                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
63470                     continue; // dont' bother, it's not in sort list or being set.
63471                 }
63472                 
63473                 so.push(cm.config[i].dataIndex);
63474             };
63475             dm.sortOrder = so;
63476         }
63477         
63478         
63479         dm.sort(cm.getDataIndex(index));
63480     },
63481
63482
63483     destroy : function(){
63484         if(this.colMenu){
63485             this.colMenu.removeAll();
63486             Roo.menu.MenuMgr.unregister(this.colMenu);
63487             this.colMenu.getEl().remove();
63488             delete this.colMenu;
63489         }
63490         if(this.hmenu){
63491             this.hmenu.removeAll();
63492             Roo.menu.MenuMgr.unregister(this.hmenu);
63493             this.hmenu.getEl().remove();
63494             delete this.hmenu;
63495         }
63496         if(this.grid.enableColumnMove){
63497             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63498             if(dds){
63499                 for(var dd in dds){
63500                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
63501                         var elid = dds[dd].dragElId;
63502                         dds[dd].unreg();
63503                         Roo.get(elid).remove();
63504                     } else if(dds[dd].config.isTarget){
63505                         dds[dd].proxyTop.remove();
63506                         dds[dd].proxyBottom.remove();
63507                         dds[dd].unreg();
63508                     }
63509                     if(Roo.dd.DDM.locationCache[dd]){
63510                         delete Roo.dd.DDM.locationCache[dd];
63511                     }
63512                 }
63513                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
63514             }
63515         }
63516         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
63517         this.bind(null, null);
63518         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
63519     },
63520
63521     handleLockChange : function(){
63522         this.refresh(true);
63523     },
63524
63525     onDenyColumnLock : function(){
63526
63527     },
63528
63529     onDenyColumnHide : function(){
63530
63531     },
63532
63533     handleHdMenuClick : function(item){
63534         var index = this.hdCtxIndex;
63535         var cm = this.cm, ds = this.ds;
63536         switch(item.id){
63537             case "asc":
63538                 ds.sort(cm.getDataIndex(index), "ASC");
63539                 break;
63540             case "desc":
63541                 ds.sort(cm.getDataIndex(index), "DESC");
63542                 break;
63543             case "lock":
63544                 var lc = cm.getLockedCount();
63545                 if(cm.getColumnCount(true) <= lc+1){
63546                     this.onDenyColumnLock();
63547                     return;
63548                 }
63549                 if(lc != index){
63550                     cm.setLocked(index, true, true);
63551                     cm.moveColumn(index, lc);
63552                     this.grid.fireEvent("columnmove", index, lc);
63553                 }else{
63554                     cm.setLocked(index, true);
63555                 }
63556             break;
63557             case "unlock":
63558                 var lc = cm.getLockedCount();
63559                 if((lc-1) != index){
63560                     cm.setLocked(index, false, true);
63561                     cm.moveColumn(index, lc-1);
63562                     this.grid.fireEvent("columnmove", index, lc-1);
63563                 }else{
63564                     cm.setLocked(index, false);
63565                 }
63566             break;
63567             case 'wider': // used to expand cols on touch..
63568             case 'narrow':
63569                 var cw = cm.getColumnWidth(index);
63570                 cw += (item.id == 'wider' ? 1 : -1) * 50;
63571                 cw = Math.max(0, cw);
63572                 cw = Math.min(cw,4000);
63573                 cm.setColumnWidth(index, cw);
63574                 break;
63575                 
63576             default:
63577                 index = cm.getIndexById(item.id.substr(4));
63578                 if(index != -1){
63579                     if(item.checked && cm.getColumnCount(true) <= 1){
63580                         this.onDenyColumnHide();
63581                         return false;
63582                     }
63583                     cm.setHidden(index, item.checked);
63584                 }
63585         }
63586         return true;
63587     },
63588
63589     beforeColMenuShow : function(){
63590         var cm = this.cm,  colCount = cm.getColumnCount();
63591         this.colMenu.removeAll();
63592         
63593         var items = [];
63594         for(var i = 0; i < colCount; i++){
63595             items.push({
63596                 id: "col-"+cm.getColumnId(i),
63597                 text: cm.getColumnHeader(i),
63598                 checked: !cm.isHidden(i),
63599                 hideOnClick:false
63600             });
63601         }
63602         
63603         if (this.grid.sortColMenu) {
63604             items.sort(function(a,b) {
63605                 if (a.text == b.text) {
63606                     return 0;
63607                 }
63608                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
63609             });
63610         }
63611         
63612         for(var i = 0; i < colCount; i++){
63613             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
63614         }
63615     },
63616
63617     handleHdCtx : function(g, index, e){
63618         e.stopEvent();
63619         var hd = this.getHeaderCell(index);
63620         this.hdCtxIndex = index;
63621         var ms = this.hmenu.items, cm = this.cm;
63622         ms.get("asc").setDisabled(!cm.isSortable(index));
63623         ms.get("desc").setDisabled(!cm.isSortable(index));
63624         if(this.grid.enableColLock !== false){
63625             ms.get("lock").setDisabled(cm.isLocked(index));
63626             ms.get("unlock").setDisabled(!cm.isLocked(index));
63627         }
63628         this.hmenu.show(hd, "tl-bl");
63629     },
63630
63631     handleHdOver : function(e){
63632         var hd = this.findHeaderCell(e.getTarget());
63633         if(hd && !this.headersDisabled){
63634             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
63635                this.fly(hd).addClass("x-grid-hd-over");
63636             }
63637         }
63638     },
63639
63640     handleHdOut : function(e){
63641         var hd = this.findHeaderCell(e.getTarget());
63642         if(hd){
63643             this.fly(hd).removeClass("x-grid-hd-over");
63644         }
63645     },
63646
63647     handleSplitDblClick : function(e, t){
63648         var i = this.getCellIndex(t);
63649         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
63650             this.autoSizeColumn(i, true);
63651             this.layout();
63652         }
63653     },
63654
63655     render : function(){
63656
63657         var cm = this.cm;
63658         var colCount = cm.getColumnCount();
63659
63660         if(this.grid.monitorWindowResize === true){
63661             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
63662         }
63663         var header = this.renderHeaders();
63664         var body = this.templates.body.apply({rows:""});
63665         var html = this.templates.master.apply({
63666             lockedBody: body,
63667             body: body,
63668             lockedHeader: header[0],
63669             header: header[1]
63670         });
63671
63672         //this.updateColumns();
63673
63674         this.grid.getGridEl().dom.innerHTML = html;
63675
63676         this.initElements();
63677         
63678         // a kludge to fix the random scolling effect in webkit
63679         this.el.on("scroll", function() {
63680             this.el.dom.scrollTop=0; // hopefully not recursive..
63681         },this);
63682
63683         this.scroller.on("scroll", this.handleScroll, this);
63684         this.lockedBody.on("mousewheel", this.handleWheel, this);
63685         this.mainBody.on("mousewheel", this.handleWheel, this);
63686
63687         this.mainHd.on("mouseover", this.handleHdOver, this);
63688         this.mainHd.on("mouseout", this.handleHdOut, this);
63689         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
63690                 {delegate: "."+this.splitClass});
63691
63692         this.lockedHd.on("mouseover", this.handleHdOver, this);
63693         this.lockedHd.on("mouseout", this.handleHdOut, this);
63694         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
63695                 {delegate: "."+this.splitClass});
63696
63697         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
63698             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63699         }
63700
63701         this.updateSplitters();
63702
63703         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
63704             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63705             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
63706         }
63707
63708         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
63709             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
63710             this.hmenu.add(
63711                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
63712                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
63713             );
63714             if(this.grid.enableColLock !== false){
63715                 this.hmenu.add('-',
63716                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
63717                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
63718                 );
63719             }
63720             if (Roo.isTouch) {
63721                  this.hmenu.add('-',
63722                     {id:"wider", text: this.columnsWiderText},
63723                     {id:"narrow", text: this.columnsNarrowText }
63724                 );
63725                 
63726                  
63727             }
63728             
63729             if(this.grid.enableColumnHide !== false){
63730
63731                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
63732                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
63733                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
63734
63735                 this.hmenu.add('-',
63736                     {id:"columns", text: this.columnsText, menu: this.colMenu}
63737                 );
63738             }
63739             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
63740
63741             this.grid.on("headercontextmenu", this.handleHdCtx, this);
63742         }
63743
63744         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
63745             this.dd = new Roo.grid.GridDragZone(this.grid, {
63746                 ddGroup : this.grid.ddGroup || 'GridDD'
63747             });
63748             
63749         }
63750
63751         /*
63752         for(var i = 0; i < colCount; i++){
63753             if(cm.isHidden(i)){
63754                 this.hideColumn(i);
63755             }
63756             if(cm.config[i].align){
63757                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
63758                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
63759             }
63760         }*/
63761         
63762         this.updateHeaderSortState();
63763
63764         this.beforeInitialResize();
63765         this.layout(true);
63766
63767         // two part rendering gives faster view to the user
63768         this.renderPhase2.defer(1, this);
63769     },
63770
63771     renderPhase2 : function(){
63772         // render the rows now
63773         this.refresh();
63774         if(this.grid.autoSizeColumns){
63775             this.autoSizeColumns();
63776         }
63777     },
63778
63779     beforeInitialResize : function(){
63780
63781     },
63782
63783     onColumnSplitterMoved : function(i, w){
63784         this.userResized = true;
63785         var cm = this.grid.colModel;
63786         cm.setColumnWidth(i, w, true);
63787         var cid = cm.getColumnId(i);
63788         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
63789         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
63790         this.updateSplitters();
63791         this.layout();
63792         this.grid.fireEvent("columnresize", i, w);
63793     },
63794
63795     syncRowHeights : function(startIndex, endIndex){
63796         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
63797             startIndex = startIndex || 0;
63798             var mrows = this.getBodyTable().rows;
63799             var lrows = this.getLockedTable().rows;
63800             var len = mrows.length-1;
63801             endIndex = Math.min(endIndex || len, len);
63802             for(var i = startIndex; i <= endIndex; i++){
63803                 var m = mrows[i], l = lrows[i];
63804                 var h = Math.max(m.offsetHeight, l.offsetHeight);
63805                 m.style.height = l.style.height = h + "px";
63806             }
63807         }
63808     },
63809
63810     layout : function(initialRender, is2ndPass)
63811     {
63812         var g = this.grid;
63813         var auto = g.autoHeight;
63814         var scrollOffset = 16;
63815         var c = g.getGridEl(), cm = this.cm,
63816                 expandCol = g.autoExpandColumn,
63817                 gv = this;
63818         //c.beginMeasure();
63819
63820         if(!c.dom.offsetWidth){ // display:none?
63821             if(initialRender){
63822                 this.lockedWrap.show();
63823                 this.mainWrap.show();
63824             }
63825             return;
63826         }
63827
63828         var hasLock = this.cm.isLocked(0);
63829
63830         var tbh = this.headerPanel.getHeight();
63831         var bbh = this.footerPanel.getHeight();
63832
63833         if(auto){
63834             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
63835             var newHeight = ch + c.getBorderWidth("tb");
63836             if(g.maxHeight){
63837                 newHeight = Math.min(g.maxHeight, newHeight);
63838             }
63839             c.setHeight(newHeight);
63840         }
63841
63842         if(g.autoWidth){
63843             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
63844         }
63845
63846         var s = this.scroller;
63847
63848         var csize = c.getSize(true);
63849
63850         this.el.setSize(csize.width, csize.height);
63851
63852         this.headerPanel.setWidth(csize.width);
63853         this.footerPanel.setWidth(csize.width);
63854
63855         var hdHeight = this.mainHd.getHeight();
63856         var vw = csize.width;
63857         var vh = csize.height - (tbh + bbh);
63858
63859         s.setSize(vw, vh);
63860
63861         var bt = this.getBodyTable();
63862         
63863         if(cm.getLockedCount() == cm.config.length){
63864             bt = this.getLockedTable();
63865         }
63866         
63867         var ltWidth = hasLock ?
63868                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
63869
63870         var scrollHeight = bt.offsetHeight;
63871         var scrollWidth = ltWidth + bt.offsetWidth;
63872         var vscroll = false, hscroll = false;
63873
63874         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
63875
63876         var lw = this.lockedWrap, mw = this.mainWrap;
63877         var lb = this.lockedBody, mb = this.mainBody;
63878
63879         setTimeout(function(){
63880             var t = s.dom.offsetTop;
63881             var w = s.dom.clientWidth,
63882                 h = s.dom.clientHeight;
63883
63884             lw.setTop(t);
63885             lw.setSize(ltWidth, h);
63886
63887             mw.setLeftTop(ltWidth, t);
63888             mw.setSize(w-ltWidth, h);
63889
63890             lb.setHeight(h-hdHeight);
63891             mb.setHeight(h-hdHeight);
63892
63893             if(is2ndPass !== true && !gv.userResized && expandCol){
63894                 // high speed resize without full column calculation
63895                 
63896                 var ci = cm.getIndexById(expandCol);
63897                 if (ci < 0) {
63898                     ci = cm.findColumnIndex(expandCol);
63899                 }
63900                 ci = Math.max(0, ci); // make sure it's got at least the first col.
63901                 var expandId = cm.getColumnId(ci);
63902                 var  tw = cm.getTotalWidth(false);
63903                 var currentWidth = cm.getColumnWidth(ci);
63904                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
63905                 if(currentWidth != cw){
63906                     cm.setColumnWidth(ci, cw, true);
63907                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
63908                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
63909                     gv.updateSplitters();
63910                     gv.layout(false, true);
63911                 }
63912             }
63913
63914             if(initialRender){
63915                 lw.show();
63916                 mw.show();
63917             }
63918             //c.endMeasure();
63919         }, 10);
63920     },
63921
63922     onWindowResize : function(){
63923         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
63924             return;
63925         }
63926         this.layout();
63927     },
63928
63929     appendFooter : function(parentEl){
63930         return null;
63931     },
63932
63933     sortAscText : "Sort Ascending",
63934     sortDescText : "Sort Descending",
63935     lockText : "Lock Column",
63936     unlockText : "Unlock Column",
63937     columnsText : "Columns",
63938  
63939     columnsWiderText : "Wider",
63940     columnsNarrowText : "Thinner"
63941 });
63942
63943
63944 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
63945     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
63946     this.proxy.el.addClass('x-grid3-col-dd');
63947 };
63948
63949 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
63950     handleMouseDown : function(e){
63951
63952     },
63953
63954     callHandleMouseDown : function(e){
63955         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
63956     }
63957 });
63958 /*
63959  * Based on:
63960  * Ext JS Library 1.1.1
63961  * Copyright(c) 2006-2007, Ext JS, LLC.
63962  *
63963  * Originally Released Under LGPL - original licence link has changed is not relivant.
63964  *
63965  * Fork - LGPL
63966  * <script type="text/javascript">
63967  */
63968  /**
63969  * @extends Roo.dd.DDProxy
63970  * @class Roo.grid.SplitDragZone
63971  * Support for Column Header resizing
63972  * @constructor
63973  * @param {Object} config
63974  */
63975 // private
63976 // This is a support class used internally by the Grid components
63977 Roo.grid.SplitDragZone = function(grid, hd, hd2){
63978     this.grid = grid;
63979     this.view = grid.getView();
63980     this.proxy = this.view.resizeProxy;
63981     Roo.grid.SplitDragZone.superclass.constructor.call(
63982         this,
63983         hd, // ID
63984         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
63985         {  // CONFIG
63986             dragElId : Roo.id(this.proxy.dom),
63987             resizeFrame:false
63988         }
63989     );
63990     
63991     this.setHandleElId(Roo.id(hd));
63992     if (hd2 !== false) {
63993         this.setOuterHandleElId(Roo.id(hd2));
63994     }
63995     
63996     this.scroll = false;
63997 };
63998 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
63999     fly: Roo.Element.fly,
64000
64001     b4StartDrag : function(x, y){
64002         this.view.headersDisabled = true;
64003         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
64004                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
64005         );
64006         this.proxy.setHeight(h);
64007         
64008         // for old system colWidth really stored the actual width?
64009         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
64010         // which in reality did not work.. - it worked only for fixed sizes
64011         // for resizable we need to use actual sizes.
64012         var w = this.cm.getColumnWidth(this.cellIndex);
64013         if (!this.view.mainWrap) {
64014             // bootstrap.
64015             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
64016         }
64017         
64018         
64019         
64020         // this was w-this.grid.minColumnWidth;
64021         // doesnt really make sense? - w = thie curren width or the rendered one?
64022         var minw = Math.max(w-this.grid.minColumnWidth, 0);
64023         this.resetConstraints();
64024         this.setXConstraint(minw, 1000);
64025         this.setYConstraint(0, 0);
64026         this.minX = x - minw;
64027         this.maxX = x + 1000;
64028         this.startPos = x;
64029         if (!this.view.mainWrap) { // this is Bootstrap code..
64030             this.getDragEl().style.display='block';
64031         }
64032         
64033         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
64034     },
64035
64036
64037     handleMouseDown : function(e){
64038         ev = Roo.EventObject.setEvent(e);
64039         var t = this.fly(ev.getTarget());
64040         if(t.hasClass("x-grid-split")){
64041             this.cellIndex = this.view.getCellIndex(t.dom);
64042             this.split = t.dom;
64043             this.cm = this.grid.colModel;
64044             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
64045                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
64046             }
64047         }
64048     },
64049
64050     endDrag : function(e){
64051         this.view.headersDisabled = false;
64052         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
64053         var diff = endX - this.startPos;
64054         // 
64055         var w = this.cm.getColumnWidth(this.cellIndex);
64056         if (!this.view.mainWrap) {
64057             w = 0;
64058         }
64059         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
64060     },
64061
64062     autoOffset : function(){
64063         this.setDelta(0,0);
64064     }
64065 });/*
64066  * Based on:
64067  * Ext JS Library 1.1.1
64068  * Copyright(c) 2006-2007, Ext JS, LLC.
64069  *
64070  * Originally Released Under LGPL - original licence link has changed is not relivant.
64071  *
64072  * Fork - LGPL
64073  * <script type="text/javascript">
64074  */
64075  
64076 // private
64077 // This is a support class used internally by the Grid components
64078 Roo.grid.GridDragZone = function(grid, config){
64079     this.view = grid.getView();
64080     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
64081     if(this.view.lockedBody){
64082         this.setHandleElId(Roo.id(this.view.mainBody.dom));
64083         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
64084     }
64085     this.scroll = false;
64086     this.grid = grid;
64087     this.ddel = document.createElement('div');
64088     this.ddel.className = 'x-grid-dd-wrap';
64089 };
64090
64091 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
64092     ddGroup : "GridDD",
64093
64094     getDragData : function(e){
64095         var t = Roo.lib.Event.getTarget(e);
64096         var rowIndex = this.view.findRowIndex(t);
64097         var sm = this.grid.selModel;
64098             
64099         //Roo.log(rowIndex);
64100         
64101         if (sm.getSelectedCell) {
64102             // cell selection..
64103             if (!sm.getSelectedCell()) {
64104                 return false;
64105             }
64106             if (rowIndex != sm.getSelectedCell()[0]) {
64107                 return false;
64108             }
64109         
64110         }
64111         if (sm.getSelections && sm.getSelections().length < 1) {
64112             return false;
64113         }
64114         
64115         
64116         // before it used to all dragging of unseleted... - now we dont do that.
64117         if(rowIndex !== false){
64118             
64119             // if editorgrid.. 
64120             
64121             
64122             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
64123                
64124             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
64125               //  
64126             //}
64127             if (e.hasModifier()){
64128                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
64129             }
64130             
64131             Roo.log("getDragData");
64132             
64133             return {
64134                 grid: this.grid,
64135                 ddel: this.ddel,
64136                 rowIndex: rowIndex,
64137                 selections: sm.getSelections ? sm.getSelections() : (
64138                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
64139             };
64140         }
64141         return false;
64142     },
64143     
64144     
64145     onInitDrag : function(e){
64146         var data = this.dragData;
64147         this.ddel.innerHTML = this.grid.getDragDropText();
64148         this.proxy.update(this.ddel);
64149         // fire start drag?
64150     },
64151
64152     afterRepair : function(){
64153         this.dragging = false;
64154     },
64155
64156     getRepairXY : function(e, data){
64157         return false;
64158     },
64159
64160     onEndDrag : function(data, e){
64161         // fire end drag?
64162     },
64163
64164     onValidDrop : function(dd, e, id){
64165         // fire drag drop?
64166         this.hideProxy();
64167     },
64168
64169     beforeInvalidDrop : function(e, id){
64170
64171     }
64172 });/*
64173  * Based on:
64174  * Ext JS Library 1.1.1
64175  * Copyright(c) 2006-2007, Ext JS, LLC.
64176  *
64177  * Originally Released Under LGPL - original licence link has changed is not relivant.
64178  *
64179  * Fork - LGPL
64180  * <script type="text/javascript">
64181  */
64182  
64183
64184 /**
64185  * @class Roo.grid.ColumnModel
64186  * @extends Roo.util.Observable
64187  * This is the default implementation of a ColumnModel used by the Grid. It defines
64188  * the columns in the grid.
64189  * <br>Usage:<br>
64190  <pre><code>
64191  var colModel = new Roo.grid.ColumnModel([
64192         {header: "Ticker", width: 60, sortable: true, locked: true},
64193         {header: "Company Name", width: 150, sortable: true},
64194         {header: "Market Cap.", width: 100, sortable: true},
64195         {header: "$ Sales", width: 100, sortable: true, renderer: money},
64196         {header: "Employees", width: 100, sortable: true, resizable: false}
64197  ]);
64198  </code></pre>
64199  * <p>
64200  
64201  * The config options listed for this class are options which may appear in each
64202  * individual column definition.
64203  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
64204  * @constructor
64205  * @param {Object} config An Array of column config objects. See this class's
64206  * config objects for details.
64207 */
64208 Roo.grid.ColumnModel = function(config){
64209         /**
64210      * The config passed into the constructor
64211      */
64212     this.config = []; //config;
64213     this.lookup = {};
64214
64215     // if no id, create one
64216     // if the column does not have a dataIndex mapping,
64217     // map it to the order it is in the config
64218     for(var i = 0, len = config.length; i < len; i++){
64219         this.addColumn(config[i]);
64220         
64221     }
64222
64223     /**
64224      * The width of columns which have no width specified (defaults to 100)
64225      * @type Number
64226      */
64227     this.defaultWidth = 100;
64228
64229     /**
64230      * Default sortable of columns which have no sortable specified (defaults to false)
64231      * @type Boolean
64232      */
64233     this.defaultSortable = false;
64234
64235     this.addEvents({
64236         /**
64237              * @event widthchange
64238              * Fires when the width of a column changes.
64239              * @param {ColumnModel} this
64240              * @param {Number} columnIndex The column index
64241              * @param {Number} newWidth The new width
64242              */
64243             "widthchange": true,
64244         /**
64245              * @event headerchange
64246              * Fires when the text of a header changes.
64247              * @param {ColumnModel} this
64248              * @param {Number} columnIndex The column index
64249              * @param {Number} newText The new header text
64250              */
64251             "headerchange": true,
64252         /**
64253              * @event hiddenchange
64254              * Fires when a column is hidden or "unhidden".
64255              * @param {ColumnModel} this
64256              * @param {Number} columnIndex The column index
64257              * @param {Boolean} hidden true if hidden, false otherwise
64258              */
64259             "hiddenchange": true,
64260             /**
64261          * @event columnmoved
64262          * Fires when a column is moved.
64263          * @param {ColumnModel} this
64264          * @param {Number} oldIndex
64265          * @param {Number} newIndex
64266          */
64267         "columnmoved" : true,
64268         /**
64269          * @event columlockchange
64270          * Fires when a column's locked state is changed
64271          * @param {ColumnModel} this
64272          * @param {Number} colIndex
64273          * @param {Boolean} locked true if locked
64274          */
64275         "columnlockchange" : true
64276     });
64277     Roo.grid.ColumnModel.superclass.constructor.call(this);
64278 };
64279 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
64280     /**
64281      * @cfg {String} header The header text to display in the Grid view.
64282      */
64283         /**
64284      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
64285      */
64286         /**
64287      * @cfg {String} smHeader Header at Bootsrap Small width
64288      */
64289         /**
64290      * @cfg {String} mdHeader Header at Bootsrap Medium width
64291      */
64292         /**
64293      * @cfg {String} lgHeader Header at Bootsrap Large width
64294      */
64295         /**
64296      * @cfg {String} xlHeader Header at Bootsrap extra Large width
64297      */
64298     /**
64299      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
64300      * {@link Roo.data.Record} definition from which to draw the column's value. If not
64301      * specified, the column's index is used as an index into the Record's data Array.
64302      */
64303     /**
64304      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
64305      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
64306      */
64307     /**
64308      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
64309      * Defaults to the value of the {@link #defaultSortable} property.
64310      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
64311      */
64312     /**
64313      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
64314      */
64315     /**
64316      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
64317      */
64318     /**
64319      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
64320      */
64321     /**
64322      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
64323      */
64324     /**
64325      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
64326      * given the cell's data value. See {@link #setRenderer}. If not specified, the
64327      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
64328      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
64329      */
64330        /**
64331      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
64332      */
64333     /**
64334      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
64335      */
64336     /**
64337      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
64338      */
64339     /**
64340      * @cfg {String} cursor (Optional)
64341      */
64342     /**
64343      * @cfg {String} tooltip (Optional)
64344      */
64345     /**
64346      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
64347      */
64348     /**
64349      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
64350      */
64351     /**
64352      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
64353      */
64354     /**
64355      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
64356      */
64357         /**
64358      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
64359      */
64360     /**
64361      * Returns the id of the column at the specified index.
64362      * @param {Number} index The column index
64363      * @return {String} the id
64364      */
64365     getColumnId : function(index){
64366         return this.config[index].id;
64367     },
64368
64369     /**
64370      * Returns the column for a specified id.
64371      * @param {String} id The column id
64372      * @return {Object} the column
64373      */
64374     getColumnById : function(id){
64375         return this.lookup[id];
64376     },
64377
64378     
64379     /**
64380      * Returns the column Object for a specified dataIndex.
64381      * @param {String} dataIndex The column dataIndex
64382      * @return {Object|Boolean} the column or false if not found
64383      */
64384     getColumnByDataIndex: function(dataIndex){
64385         var index = this.findColumnIndex(dataIndex);
64386         return index > -1 ? this.config[index] : false;
64387     },
64388     
64389     /**
64390      * Returns the index for a specified column id.
64391      * @param {String} id The column id
64392      * @return {Number} the index, or -1 if not found
64393      */
64394     getIndexById : function(id){
64395         for(var i = 0, len = this.config.length; i < len; i++){
64396             if(this.config[i].id == id){
64397                 return i;
64398             }
64399         }
64400         return -1;
64401     },
64402     
64403     /**
64404      * Returns the index for a specified column dataIndex.
64405      * @param {String} dataIndex The column dataIndex
64406      * @return {Number} the index, or -1 if not found
64407      */
64408     
64409     findColumnIndex : function(dataIndex){
64410         for(var i = 0, len = this.config.length; i < len; i++){
64411             if(this.config[i].dataIndex == dataIndex){
64412                 return i;
64413             }
64414         }
64415         return -1;
64416     },
64417     
64418     
64419     moveColumn : function(oldIndex, newIndex){
64420         var c = this.config[oldIndex];
64421         this.config.splice(oldIndex, 1);
64422         this.config.splice(newIndex, 0, c);
64423         this.dataMap = null;
64424         this.fireEvent("columnmoved", this, oldIndex, newIndex);
64425     },
64426
64427     isLocked : function(colIndex){
64428         return this.config[colIndex].locked === true;
64429     },
64430
64431     setLocked : function(colIndex, value, suppressEvent){
64432         if(this.isLocked(colIndex) == value){
64433             return;
64434         }
64435         this.config[colIndex].locked = value;
64436         if(!suppressEvent){
64437             this.fireEvent("columnlockchange", this, colIndex, value);
64438         }
64439     },
64440
64441     getTotalLockedWidth : function(){
64442         var totalWidth = 0;
64443         for(var i = 0; i < this.config.length; i++){
64444             if(this.isLocked(i) && !this.isHidden(i)){
64445                 this.totalWidth += this.getColumnWidth(i);
64446             }
64447         }
64448         return totalWidth;
64449     },
64450
64451     getLockedCount : function(){
64452         for(var i = 0, len = this.config.length; i < len; i++){
64453             if(!this.isLocked(i)){
64454                 return i;
64455             }
64456         }
64457         
64458         return this.config.length;
64459     },
64460
64461     /**
64462      * Returns the number of columns.
64463      * @return {Number}
64464      */
64465     getColumnCount : function(visibleOnly){
64466         if(visibleOnly === true){
64467             var c = 0;
64468             for(var i = 0, len = this.config.length; i < len; i++){
64469                 if(!this.isHidden(i)){
64470                     c++;
64471                 }
64472             }
64473             return c;
64474         }
64475         return this.config.length;
64476     },
64477
64478     /**
64479      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
64480      * @param {Function} fn
64481      * @param {Object} scope (optional)
64482      * @return {Array} result
64483      */
64484     getColumnsBy : function(fn, scope){
64485         var r = [];
64486         for(var i = 0, len = this.config.length; i < len; i++){
64487             var c = this.config[i];
64488             if(fn.call(scope||this, c, i) === true){
64489                 r[r.length] = c;
64490             }
64491         }
64492         return r;
64493     },
64494
64495     /**
64496      * Returns true if the specified column is sortable.
64497      * @param {Number} col The column index
64498      * @return {Boolean}
64499      */
64500     isSortable : function(col){
64501         if(typeof this.config[col].sortable == "undefined"){
64502             return this.defaultSortable;
64503         }
64504         return this.config[col].sortable;
64505     },
64506
64507     /**
64508      * Returns the rendering (formatting) function defined for the column.
64509      * @param {Number} col The column index.
64510      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
64511      */
64512     getRenderer : function(col){
64513         if(!this.config[col].renderer){
64514             return Roo.grid.ColumnModel.defaultRenderer;
64515         }
64516         return this.config[col].renderer;
64517     },
64518
64519     /**
64520      * Sets the rendering (formatting) function for a column.
64521      * @param {Number} col The column index
64522      * @param {Function} fn The function to use to process the cell's raw data
64523      * to return HTML markup for the grid view. The render function is called with
64524      * the following parameters:<ul>
64525      * <li>Data value.</li>
64526      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
64527      * <li>css A CSS style string to apply to the table cell.</li>
64528      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
64529      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
64530      * <li>Row index</li>
64531      * <li>Column index</li>
64532      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
64533      */
64534     setRenderer : function(col, fn){
64535         this.config[col].renderer = fn;
64536     },
64537
64538     /**
64539      * Returns the width for the specified column.
64540      * @param {Number} col The column index
64541      * @param (optional) {String} gridSize bootstrap width size.
64542      * @return {Number}
64543      */
64544     getColumnWidth : function(col, gridSize)
64545         {
64546                 var cfg = this.config[col];
64547                 
64548                 if (typeof(gridSize) == 'undefined') {
64549                         return cfg.width * 1 || this.defaultWidth;
64550                 }
64551                 if (gridSize === false) { // if we set it..
64552                         return cfg.width || false;
64553                 }
64554                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
64555                 
64556                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
64557                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
64558                                 continue;
64559                         }
64560                         return cfg[ sizes[i] ];
64561                 }
64562                 return 1;
64563                 
64564     },
64565
64566     /**
64567      * Sets the width for a column.
64568      * @param {Number} col The column index
64569      * @param {Number} width The new width
64570      */
64571     setColumnWidth : function(col, width, suppressEvent){
64572         this.config[col].width = width;
64573         this.totalWidth = null;
64574         if(!suppressEvent){
64575              this.fireEvent("widthchange", this, col, width);
64576         }
64577     },
64578
64579     /**
64580      * Returns the total width of all columns.
64581      * @param {Boolean} includeHidden True to include hidden column widths
64582      * @return {Number}
64583      */
64584     getTotalWidth : function(includeHidden){
64585         if(!this.totalWidth){
64586             this.totalWidth = 0;
64587             for(var i = 0, len = this.config.length; i < len; i++){
64588                 if(includeHidden || !this.isHidden(i)){
64589                     this.totalWidth += this.getColumnWidth(i);
64590                 }
64591             }
64592         }
64593         return this.totalWidth;
64594     },
64595
64596     /**
64597      * Returns the header for the specified column.
64598      * @param {Number} col The column index
64599      * @return {String}
64600      */
64601     getColumnHeader : function(col){
64602         return this.config[col].header;
64603     },
64604
64605     /**
64606      * Sets the header for a column.
64607      * @param {Number} col The column index
64608      * @param {String} header The new header
64609      */
64610     setColumnHeader : function(col, header){
64611         this.config[col].header = header;
64612         this.fireEvent("headerchange", this, col, header);
64613     },
64614
64615     /**
64616      * Returns the tooltip for the specified column.
64617      * @param {Number} col The column index
64618      * @return {String}
64619      */
64620     getColumnTooltip : function(col){
64621             return this.config[col].tooltip;
64622     },
64623     /**
64624      * Sets the tooltip for a column.
64625      * @param {Number} col The column index
64626      * @param {String} tooltip The new tooltip
64627      */
64628     setColumnTooltip : function(col, tooltip){
64629             this.config[col].tooltip = tooltip;
64630     },
64631
64632     /**
64633      * Returns the dataIndex for the specified column.
64634      * @param {Number} col The column index
64635      * @return {Number}
64636      */
64637     getDataIndex : function(col){
64638         return this.config[col].dataIndex;
64639     },
64640
64641     /**
64642      * Sets the dataIndex for a column.
64643      * @param {Number} col The column index
64644      * @param {Number} dataIndex The new dataIndex
64645      */
64646     setDataIndex : function(col, dataIndex){
64647         this.config[col].dataIndex = dataIndex;
64648     },
64649
64650     
64651     
64652     /**
64653      * Returns true if the cell is editable.
64654      * @param {Number} colIndex The column index
64655      * @param {Number} rowIndex The row index - this is nto actually used..?
64656      * @return {Boolean}
64657      */
64658     isCellEditable : function(colIndex, rowIndex){
64659         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
64660     },
64661
64662     /**
64663      * Returns the editor defined for the cell/column.
64664      * return false or null to disable editing.
64665      * @param {Number} colIndex The column index
64666      * @param {Number} rowIndex The row index
64667      * @return {Object}
64668      */
64669     getCellEditor : function(colIndex, rowIndex){
64670         return this.config[colIndex].editor;
64671     },
64672
64673     /**
64674      * Sets if a column is editable.
64675      * @param {Number} col The column index
64676      * @param {Boolean} editable True if the column is editable
64677      */
64678     setEditable : function(col, editable){
64679         this.config[col].editable = editable;
64680     },
64681
64682
64683     /**
64684      * Returns true if the column is hidden.
64685      * @param {Number} colIndex The column index
64686      * @return {Boolean}
64687      */
64688     isHidden : function(colIndex){
64689         return this.config[colIndex].hidden;
64690     },
64691
64692
64693     /**
64694      * Returns true if the column width cannot be changed
64695      */
64696     isFixed : function(colIndex){
64697         return this.config[colIndex].fixed;
64698     },
64699
64700     /**
64701      * Returns true if the column can be resized
64702      * @return {Boolean}
64703      */
64704     isResizable : function(colIndex){
64705         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
64706     },
64707     /**
64708      * Sets if a column is hidden.
64709      * @param {Number} colIndex The column index
64710      * @param {Boolean} hidden True if the column is hidden
64711      */
64712     setHidden : function(colIndex, hidden){
64713         this.config[colIndex].hidden = hidden;
64714         this.totalWidth = null;
64715         this.fireEvent("hiddenchange", this, colIndex, hidden);
64716     },
64717
64718     /**
64719      * Sets the editor for a column.
64720      * @param {Number} col The column index
64721      * @param {Object} editor The editor object
64722      */
64723     setEditor : function(col, editor){
64724         this.config[col].editor = editor;
64725     },
64726     /**
64727      * Add a column (experimental...) - defaults to adding to the end..
64728      * @param {Object} config 
64729     */
64730     addColumn : function(c)
64731     {
64732     
64733         var i = this.config.length;
64734         this.config[i] = c;
64735         
64736         if(typeof c.dataIndex == "undefined"){
64737             c.dataIndex = i;
64738         }
64739         if(typeof c.renderer == "string"){
64740             c.renderer = Roo.util.Format[c.renderer];
64741         }
64742         if(typeof c.id == "undefined"){
64743             c.id = Roo.id();
64744         }
64745         if(c.editor && c.editor.xtype){
64746             c.editor  = Roo.factory(c.editor, Roo.grid);
64747         }
64748         if(c.editor && c.editor.isFormField){
64749             c.editor = new Roo.grid.GridEditor(c.editor);
64750         }
64751         this.lookup[c.id] = c;
64752     }
64753     
64754 });
64755
64756 Roo.grid.ColumnModel.defaultRenderer = function(value)
64757 {
64758     if(typeof value == "object") {
64759         return value;
64760     }
64761         if(typeof value == "string" && value.length < 1){
64762             return "&#160;";
64763         }
64764     
64765         return String.format("{0}", value);
64766 };
64767
64768 // Alias for backwards compatibility
64769 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
64770 /*
64771  * Based on:
64772  * Ext JS Library 1.1.1
64773  * Copyright(c) 2006-2007, Ext JS, LLC.
64774  *
64775  * Originally Released Under LGPL - original licence link has changed is not relivant.
64776  *
64777  * Fork - LGPL
64778  * <script type="text/javascript">
64779  */
64780
64781 /**
64782  * @class Roo.grid.AbstractSelectionModel
64783  * @extends Roo.util.Observable
64784  * @abstract
64785  * Abstract base class for grid SelectionModels.  It provides the interface that should be
64786  * implemented by descendant classes.  This class should not be directly instantiated.
64787  * @constructor
64788  */
64789 Roo.grid.AbstractSelectionModel = function(){
64790     this.locked = false;
64791     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
64792 };
64793
64794 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
64795     /** @ignore Called by the grid automatically. Do not call directly. */
64796     init : function(grid){
64797         this.grid = grid;
64798         this.initEvents();
64799     },
64800
64801     /**
64802      * Locks the selections.
64803      */
64804     lock : function(){
64805         this.locked = true;
64806     },
64807
64808     /**
64809      * Unlocks the selections.
64810      */
64811     unlock : function(){
64812         this.locked = false;
64813     },
64814
64815     /**
64816      * Returns true if the selections are locked.
64817      * @return {Boolean}
64818      */
64819     isLocked : function(){
64820         return this.locked;
64821     }
64822 });/*
64823  * Based on:
64824  * Ext JS Library 1.1.1
64825  * Copyright(c) 2006-2007, Ext JS, LLC.
64826  *
64827  * Originally Released Under LGPL - original licence link has changed is not relivant.
64828  *
64829  * Fork - LGPL
64830  * <script type="text/javascript">
64831  */
64832 /**
64833  * @extends Roo.grid.AbstractSelectionModel
64834  * @class Roo.grid.RowSelectionModel
64835  * The default SelectionModel used by {@link Roo.grid.Grid}.
64836  * It supports multiple selections and keyboard selection/navigation. 
64837  * @constructor
64838  * @param {Object} config
64839  */
64840 Roo.grid.RowSelectionModel = function(config){
64841     Roo.apply(this, config);
64842     this.selections = new Roo.util.MixedCollection(false, function(o){
64843         return o.id;
64844     });
64845
64846     this.last = false;
64847     this.lastActive = false;
64848
64849     this.addEvents({
64850         /**
64851         * @event selectionchange
64852         * Fires when the selection changes
64853         * @param {SelectionModel} this
64854         */
64855        "selectionchange" : true,
64856        /**
64857         * @event afterselectionchange
64858         * Fires after the selection changes (eg. by key press or clicking)
64859         * @param {SelectionModel} this
64860         */
64861        "afterselectionchange" : true,
64862        /**
64863         * @event beforerowselect
64864         * Fires when a row is selected being selected, return false to cancel.
64865         * @param {SelectionModel} this
64866         * @param {Number} rowIndex The selected index
64867         * @param {Boolean} keepExisting False if other selections will be cleared
64868         */
64869        "beforerowselect" : true,
64870        /**
64871         * @event rowselect
64872         * Fires when a row is selected.
64873         * @param {SelectionModel} this
64874         * @param {Number} rowIndex The selected index
64875         * @param {Roo.data.Record} r The record
64876         */
64877        "rowselect" : true,
64878        /**
64879         * @event rowdeselect
64880         * Fires when a row is deselected.
64881         * @param {SelectionModel} this
64882         * @param {Number} rowIndex The selected index
64883         */
64884         "rowdeselect" : true
64885     });
64886     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
64887     this.locked = false;
64888 };
64889
64890 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
64891     /**
64892      * @cfg {Boolean} singleSelect
64893      * True to allow selection of only one row at a time (defaults to false)
64894      */
64895     singleSelect : false,
64896
64897     // private
64898     initEvents : function(){
64899
64900         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
64901             this.grid.on("mousedown", this.handleMouseDown, this);
64902         }else{ // allow click to work like normal
64903             this.grid.on("rowclick", this.handleDragableRowClick, this);
64904         }
64905         // bootstrap does not have a view..
64906         var view = this.grid.view ? this.grid.view : this.grid;
64907         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
64908             "up" : function(e){
64909                 if(!e.shiftKey){
64910                     this.selectPrevious(e.shiftKey);
64911                 }else if(this.last !== false && this.lastActive !== false){
64912                     var last = this.last;
64913                     this.selectRange(this.last,  this.lastActive-1);
64914                     view.focusRow(this.lastActive);
64915                     if(last !== false){
64916                         this.last = last;
64917                     }
64918                 }else{
64919                     this.selectFirstRow();
64920                 }
64921                 this.fireEvent("afterselectionchange", this);
64922             },
64923             "down" : function(e){
64924                 if(!e.shiftKey){
64925                     this.selectNext(e.shiftKey);
64926                 }else if(this.last !== false && this.lastActive !== false){
64927                     var last = this.last;
64928                     this.selectRange(this.last,  this.lastActive+1);
64929                     view.focusRow(this.lastActive);
64930                     if(last !== false){
64931                         this.last = last;
64932                     }
64933                 }else{
64934                     this.selectFirstRow();
64935                 }
64936                 this.fireEvent("afterselectionchange", this);
64937             },
64938             scope: this
64939         });
64940
64941          
64942         view.on("refresh", this.onRefresh, this);
64943         view.on("rowupdated", this.onRowUpdated, this);
64944         view.on("rowremoved", this.onRemove, this);
64945     },
64946
64947     // private
64948     onRefresh : function(){
64949         var ds = this.grid.ds, i, v = this.grid.view;
64950         var s = this.selections;
64951         s.each(function(r){
64952             if((i = ds.indexOfId(r.id)) != -1){
64953                 v.onRowSelect(i);
64954                 s.add(ds.getAt(i)); // updating the selection relate data
64955             }else{
64956                 s.remove(r);
64957             }
64958         });
64959     },
64960
64961     // private
64962     onRemove : function(v, index, r){
64963         this.selections.remove(r);
64964     },
64965
64966     // private
64967     onRowUpdated : function(v, index, r){
64968         if(this.isSelected(r)){
64969             v.onRowSelect(index);
64970         }
64971     },
64972
64973     /**
64974      * Select records.
64975      * @param {Array} records The records to select
64976      * @param {Boolean} keepExisting (optional) True to keep existing selections
64977      */
64978     selectRecords : function(records, keepExisting){
64979         if(!keepExisting){
64980             this.clearSelections();
64981         }
64982         var ds = this.grid.ds;
64983         for(var i = 0, len = records.length; i < len; i++){
64984             this.selectRow(ds.indexOf(records[i]), true);
64985         }
64986     },
64987
64988     /**
64989      * Gets the number of selected rows.
64990      * @return {Number}
64991      */
64992     getCount : function(){
64993         return this.selections.length;
64994     },
64995
64996     /**
64997      * Selects the first row in the grid.
64998      */
64999     selectFirstRow : function(){
65000         this.selectRow(0);
65001     },
65002
65003     /**
65004      * Select the last row.
65005      * @param {Boolean} keepExisting (optional) True to keep existing selections
65006      */
65007     selectLastRow : function(keepExisting){
65008         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
65009     },
65010
65011     /**
65012      * Selects the row immediately following the last selected row.
65013      * @param {Boolean} keepExisting (optional) True to keep existing selections
65014      */
65015     selectNext : function(keepExisting){
65016         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
65017             this.selectRow(this.last+1, keepExisting);
65018             var view = this.grid.view ? this.grid.view : this.grid;
65019             view.focusRow(this.last);
65020         }
65021     },
65022
65023     /**
65024      * Selects the row that precedes the last selected row.
65025      * @param {Boolean} keepExisting (optional) True to keep existing selections
65026      */
65027     selectPrevious : function(keepExisting){
65028         if(this.last){
65029             this.selectRow(this.last-1, keepExisting);
65030             var view = this.grid.view ? this.grid.view : this.grid;
65031             view.focusRow(this.last);
65032         }
65033     },
65034
65035     /**
65036      * Returns the selected records
65037      * @return {Array} Array of selected records
65038      */
65039     getSelections : function(){
65040         return [].concat(this.selections.items);
65041     },
65042
65043     /**
65044      * Returns the first selected record.
65045      * @return {Record}
65046      */
65047     getSelected : function(){
65048         return this.selections.itemAt(0);
65049     },
65050
65051
65052     /**
65053      * Clears all selections.
65054      */
65055     clearSelections : function(fast){
65056         if(this.locked) {
65057             return;
65058         }
65059         if(fast !== true){
65060             var ds = this.grid.ds;
65061             var s = this.selections;
65062             s.each(function(r){
65063                 this.deselectRow(ds.indexOfId(r.id));
65064             }, this);
65065             s.clear();
65066         }else{
65067             this.selections.clear();
65068         }
65069         this.last = false;
65070     },
65071
65072
65073     /**
65074      * Selects all rows.
65075      */
65076     selectAll : function(){
65077         if(this.locked) {
65078             return;
65079         }
65080         this.selections.clear();
65081         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
65082             this.selectRow(i, true);
65083         }
65084     },
65085
65086     /**
65087      * Returns True if there is a selection.
65088      * @return {Boolean}
65089      */
65090     hasSelection : function(){
65091         return this.selections.length > 0;
65092     },
65093
65094     /**
65095      * Returns True if the specified row is selected.
65096      * @param {Number/Record} record The record or index of the record to check
65097      * @return {Boolean}
65098      */
65099     isSelected : function(index){
65100         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
65101         return (r && this.selections.key(r.id) ? true : false);
65102     },
65103
65104     /**
65105      * Returns True if the specified record id is selected.
65106      * @param {String} id The id of record to check
65107      * @return {Boolean}
65108      */
65109     isIdSelected : function(id){
65110         return (this.selections.key(id) ? true : false);
65111     },
65112
65113     // private
65114     handleMouseDown : function(e, t)
65115     {
65116         var view = this.grid.view ? this.grid.view : this.grid;
65117         var rowIndex;
65118         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
65119             return;
65120         };
65121         if(e.shiftKey && this.last !== false){
65122             var last = this.last;
65123             this.selectRange(last, rowIndex, e.ctrlKey);
65124             this.last = last; // reset the last
65125             view.focusRow(rowIndex);
65126         }else{
65127             var isSelected = this.isSelected(rowIndex);
65128             if(e.button !== 0 && isSelected){
65129                 view.focusRow(rowIndex);
65130             }else if(e.ctrlKey && isSelected){
65131                 this.deselectRow(rowIndex);
65132             }else if(!isSelected){
65133                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
65134                 view.focusRow(rowIndex);
65135             }
65136         }
65137         this.fireEvent("afterselectionchange", this);
65138     },
65139     // private
65140     handleDragableRowClick :  function(grid, rowIndex, e) 
65141     {
65142         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
65143             this.selectRow(rowIndex, false);
65144             var view = this.grid.view ? this.grid.view : this.grid;
65145             view.focusRow(rowIndex);
65146              this.fireEvent("afterselectionchange", this);
65147         }
65148     },
65149     
65150     /**
65151      * Selects multiple rows.
65152      * @param {Array} rows Array of the indexes of the row to select
65153      * @param {Boolean} keepExisting (optional) True to keep existing selections
65154      */
65155     selectRows : function(rows, keepExisting){
65156         if(!keepExisting){
65157             this.clearSelections();
65158         }
65159         for(var i = 0, len = rows.length; i < len; i++){
65160             this.selectRow(rows[i], true);
65161         }
65162     },
65163
65164     /**
65165      * Selects a range of rows. All rows in between startRow and endRow are also selected.
65166      * @param {Number} startRow The index of the first row in the range
65167      * @param {Number} endRow The index of the last row in the range
65168      * @param {Boolean} keepExisting (optional) True to retain existing selections
65169      */
65170     selectRange : function(startRow, endRow, keepExisting){
65171         if(this.locked) {
65172             return;
65173         }
65174         if(!keepExisting){
65175             this.clearSelections();
65176         }
65177         if(startRow <= endRow){
65178             for(var i = startRow; i <= endRow; i++){
65179                 this.selectRow(i, true);
65180             }
65181         }else{
65182             for(var i = startRow; i >= endRow; i--){
65183                 this.selectRow(i, true);
65184             }
65185         }
65186     },
65187
65188     /**
65189      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
65190      * @param {Number} startRow The index of the first row in the range
65191      * @param {Number} endRow The index of the last row in the range
65192      */
65193     deselectRange : function(startRow, endRow, preventViewNotify){
65194         if(this.locked) {
65195             return;
65196         }
65197         for(var i = startRow; i <= endRow; i++){
65198             this.deselectRow(i, preventViewNotify);
65199         }
65200     },
65201
65202     /**
65203      * Selects a row.
65204      * @param {Number} row The index of the row to select
65205      * @param {Boolean} keepExisting (optional) True to keep existing selections
65206      */
65207     selectRow : function(index, keepExisting, preventViewNotify){
65208         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
65209             return;
65210         }
65211         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
65212             if(!keepExisting || this.singleSelect){
65213                 this.clearSelections();
65214             }
65215             var r = this.grid.ds.getAt(index);
65216             this.selections.add(r);
65217             this.last = this.lastActive = index;
65218             if(!preventViewNotify){
65219                 var view = this.grid.view ? this.grid.view : this.grid;
65220                 view.onRowSelect(index);
65221             }
65222             this.fireEvent("rowselect", this, index, r);
65223             this.fireEvent("selectionchange", this);
65224         }
65225     },
65226
65227     /**
65228      * Deselects a row.
65229      * @param {Number} row The index of the row to deselect
65230      */
65231     deselectRow : function(index, preventViewNotify){
65232         if(this.locked) {
65233             return;
65234         }
65235         if(this.last == index){
65236             this.last = false;
65237         }
65238         if(this.lastActive == index){
65239             this.lastActive = false;
65240         }
65241         var r = this.grid.ds.getAt(index);
65242         this.selections.remove(r);
65243         if(!preventViewNotify){
65244             var view = this.grid.view ? this.grid.view : this.grid;
65245             view.onRowDeselect(index);
65246         }
65247         this.fireEvent("rowdeselect", this, index);
65248         this.fireEvent("selectionchange", this);
65249     },
65250
65251     // private
65252     restoreLast : function(){
65253         if(this._last){
65254             this.last = this._last;
65255         }
65256     },
65257
65258     // private
65259     acceptsNav : function(row, col, cm){
65260         return !cm.isHidden(col) && cm.isCellEditable(col, row);
65261     },
65262
65263     // private
65264     onEditorKey : function(field, e){
65265         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
65266         if(k == e.TAB){
65267             e.stopEvent();
65268             ed.completeEdit();
65269             if(e.shiftKey){
65270                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65271             }else{
65272                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65273             }
65274         }else if(k == e.ENTER && !e.ctrlKey){
65275             e.stopEvent();
65276             ed.completeEdit();
65277             if(e.shiftKey){
65278                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
65279             }else{
65280                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
65281             }
65282         }else if(k == e.ESC){
65283             ed.cancelEdit();
65284         }
65285         if(newCell){
65286             g.startEditing(newCell[0], newCell[1]);
65287         }
65288     }
65289 });/*
65290  * Based on:
65291  * Ext JS Library 1.1.1
65292  * Copyright(c) 2006-2007, Ext JS, LLC.
65293  *
65294  * Originally Released Under LGPL - original licence link has changed is not relivant.
65295  *
65296  * Fork - LGPL
65297  * <script type="text/javascript">
65298  */
65299 /**
65300  * @class Roo.grid.CellSelectionModel
65301  * @extends Roo.grid.AbstractSelectionModel
65302  * This class provides the basic implementation for cell selection in a grid.
65303  * @constructor
65304  * @param {Object} config The object containing the configuration of this model.
65305  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
65306  */
65307 Roo.grid.CellSelectionModel = function(config){
65308     Roo.apply(this, config);
65309
65310     this.selection = null;
65311
65312     this.addEvents({
65313         /**
65314              * @event beforerowselect
65315              * Fires before a cell is selected.
65316              * @param {SelectionModel} this
65317              * @param {Number} rowIndex The selected row index
65318              * @param {Number} colIndex The selected cell index
65319              */
65320             "beforecellselect" : true,
65321         /**
65322              * @event cellselect
65323              * Fires when a cell is selected.
65324              * @param {SelectionModel} this
65325              * @param {Number} rowIndex The selected row index
65326              * @param {Number} colIndex The selected cell index
65327              */
65328             "cellselect" : true,
65329         /**
65330              * @event selectionchange
65331              * Fires when the active selection changes.
65332              * @param {SelectionModel} this
65333              * @param {Object} selection null for no selection or an object (o) with two properties
65334                 <ul>
65335                 <li>o.record: the record object for the row the selection is in</li>
65336                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
65337                 </ul>
65338              */
65339             "selectionchange" : true,
65340         /**
65341              * @event tabend
65342              * Fires when the tab (or enter) was pressed on the last editable cell
65343              * You can use this to trigger add new row.
65344              * @param {SelectionModel} this
65345              */
65346             "tabend" : true,
65347          /**
65348              * @event beforeeditnext
65349              * Fires before the next editable sell is made active
65350              * You can use this to skip to another cell or fire the tabend
65351              *    if you set cell to false
65352              * @param {Object} eventdata object : { cell : [ row, col ] } 
65353              */
65354             "beforeeditnext" : true
65355     });
65356     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
65357 };
65358
65359 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
65360     
65361     enter_is_tab: false,
65362
65363     /** @ignore */
65364     initEvents : function(){
65365         this.grid.on("mousedown", this.handleMouseDown, this);
65366         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
65367         var view = this.grid.view;
65368         view.on("refresh", this.onViewChange, this);
65369         view.on("rowupdated", this.onRowUpdated, this);
65370         view.on("beforerowremoved", this.clearSelections, this);
65371         view.on("beforerowsinserted", this.clearSelections, this);
65372         if(this.grid.isEditor){
65373             this.grid.on("beforeedit", this.beforeEdit,  this);
65374         }
65375     },
65376
65377         //private
65378     beforeEdit : function(e){
65379         this.select(e.row, e.column, false, true, e.record);
65380     },
65381
65382         //private
65383     onRowUpdated : function(v, index, r){
65384         if(this.selection && this.selection.record == r){
65385             v.onCellSelect(index, this.selection.cell[1]);
65386         }
65387     },
65388
65389         //private
65390     onViewChange : function(){
65391         this.clearSelections(true);
65392     },
65393
65394         /**
65395          * Returns the currently selected cell,.
65396          * @return {Array} The selected cell (row, column) or null if none selected.
65397          */
65398     getSelectedCell : function(){
65399         return this.selection ? this.selection.cell : null;
65400     },
65401
65402     /**
65403      * Clears all selections.
65404      * @param {Boolean} true to prevent the gridview from being notified about the change.
65405      */
65406     clearSelections : function(preventNotify){
65407         var s = this.selection;
65408         if(s){
65409             if(preventNotify !== true){
65410                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
65411             }
65412             this.selection = null;
65413             this.fireEvent("selectionchange", this, null);
65414         }
65415     },
65416
65417     /**
65418      * Returns true if there is a selection.
65419      * @return {Boolean}
65420      */
65421     hasSelection : function(){
65422         return this.selection ? true : false;
65423     },
65424
65425     /** @ignore */
65426     handleMouseDown : function(e, t){
65427         var v = this.grid.getView();
65428         if(this.isLocked()){
65429             return;
65430         };
65431         var row = v.findRowIndex(t);
65432         var cell = v.findCellIndex(t);
65433         if(row !== false && cell !== false){
65434             this.select(row, cell);
65435         }
65436     },
65437
65438     /**
65439      * Selects a cell.
65440      * @param {Number} rowIndex
65441      * @param {Number} collIndex
65442      */
65443     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
65444         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
65445             this.clearSelections();
65446             r = r || this.grid.dataSource.getAt(rowIndex);
65447             this.selection = {
65448                 record : r,
65449                 cell : [rowIndex, colIndex]
65450             };
65451             if(!preventViewNotify){
65452                 var v = this.grid.getView();
65453                 v.onCellSelect(rowIndex, colIndex);
65454                 if(preventFocus !== true){
65455                     v.focusCell(rowIndex, colIndex);
65456                 }
65457             }
65458             this.fireEvent("cellselect", this, rowIndex, colIndex);
65459             this.fireEvent("selectionchange", this, this.selection);
65460         }
65461     },
65462
65463         //private
65464     isSelectable : function(rowIndex, colIndex, cm){
65465         return !cm.isHidden(colIndex);
65466     },
65467
65468     /** @ignore */
65469     handleKeyDown : function(e){
65470         //Roo.log('Cell Sel Model handleKeyDown');
65471         if(!e.isNavKeyPress()){
65472             return;
65473         }
65474         var g = this.grid, s = this.selection;
65475         if(!s){
65476             e.stopEvent();
65477             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
65478             if(cell){
65479                 this.select(cell[0], cell[1]);
65480             }
65481             return;
65482         }
65483         var sm = this;
65484         var walk = function(row, col, step){
65485             return g.walkCells(row, col, step, sm.isSelectable,  sm);
65486         };
65487         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
65488         var newCell;
65489
65490       
65491
65492         switch(k){
65493             case e.TAB:
65494                 // handled by onEditorKey
65495                 if (g.isEditor && g.editing) {
65496                     return;
65497                 }
65498                 if(e.shiftKey) {
65499                     newCell = walk(r, c-1, -1);
65500                 } else {
65501                     newCell = walk(r, c+1, 1);
65502                 }
65503                 break;
65504             
65505             case e.DOWN:
65506                newCell = walk(r+1, c, 1);
65507                 break;
65508             
65509             case e.UP:
65510                 newCell = walk(r-1, c, -1);
65511                 break;
65512             
65513             case e.RIGHT:
65514                 newCell = walk(r, c+1, 1);
65515                 break;
65516             
65517             case e.LEFT:
65518                 newCell = walk(r, c-1, -1);
65519                 break;
65520             
65521             case e.ENTER:
65522                 
65523                 if(g.isEditor && !g.editing){
65524                    g.startEditing(r, c);
65525                    e.stopEvent();
65526                    return;
65527                 }
65528                 
65529                 
65530              break;
65531         };
65532         if(newCell){
65533             this.select(newCell[0], newCell[1]);
65534             e.stopEvent();
65535             
65536         }
65537     },
65538
65539     acceptsNav : function(row, col, cm){
65540         return !cm.isHidden(col) && cm.isCellEditable(col, row);
65541     },
65542     /**
65543      * Selects a cell.
65544      * @param {Number} field (not used) - as it's normally used as a listener
65545      * @param {Number} e - event - fake it by using
65546      *
65547      * var e = Roo.EventObjectImpl.prototype;
65548      * e.keyCode = e.TAB
65549      *
65550      * 
65551      */
65552     onEditorKey : function(field, e){
65553         
65554         var k = e.getKey(),
65555             newCell,
65556             g = this.grid,
65557             ed = g.activeEditor,
65558             forward = false;
65559         ///Roo.log('onEditorKey' + k);
65560         
65561         
65562         if (this.enter_is_tab && k == e.ENTER) {
65563             k = e.TAB;
65564         }
65565         
65566         if(k == e.TAB){
65567             if(e.shiftKey){
65568                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65569             }else{
65570                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65571                 forward = true;
65572             }
65573             
65574             e.stopEvent();
65575             
65576         } else if(k == e.ENTER &&  !e.ctrlKey){
65577             ed.completeEdit();
65578             e.stopEvent();
65579             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65580         
65581                 } else if(k == e.ESC){
65582             ed.cancelEdit();
65583         }
65584                 
65585         if (newCell) {
65586             var ecall = { cell : newCell, forward : forward };
65587             this.fireEvent('beforeeditnext', ecall );
65588             newCell = ecall.cell;
65589                         forward = ecall.forward;
65590         }
65591                 
65592         if(newCell){
65593             //Roo.log('next cell after edit');
65594             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
65595         } else if (forward) {
65596             // tabbed past last
65597             this.fireEvent.defer(100, this, ['tabend',this]);
65598         }
65599     }
65600 });/*
65601  * Based on:
65602  * Ext JS Library 1.1.1
65603  * Copyright(c) 2006-2007, Ext JS, LLC.
65604  *
65605  * Originally Released Under LGPL - original licence link has changed is not relivant.
65606  *
65607  * Fork - LGPL
65608  * <script type="text/javascript">
65609  */
65610  
65611 /**
65612  * @class Roo.grid.EditorGrid
65613  * @extends Roo.grid.Grid
65614  * Class for creating and editable grid.
65615  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
65616  * The container MUST have some type of size defined for the grid to fill. The container will be 
65617  * automatically set to position relative if it isn't already.
65618  * @param {Object} dataSource The data model to bind to
65619  * @param {Object} colModel The column model with info about this grid's columns
65620  */
65621 Roo.grid.EditorGrid = function(container, config){
65622     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
65623     this.getGridEl().addClass("xedit-grid");
65624
65625     if(!this.selModel){
65626         this.selModel = new Roo.grid.CellSelectionModel();
65627     }
65628
65629     this.activeEditor = null;
65630
65631         this.addEvents({
65632             /**
65633              * @event beforeedit
65634              * Fires before cell editing is triggered. The edit event object has the following properties <br />
65635              * <ul style="padding:5px;padding-left:16px;">
65636              * <li>grid - This grid</li>
65637              * <li>record - The record being edited</li>
65638              * <li>field - The field name being edited</li>
65639              * <li>value - The value for the field being edited.</li>
65640              * <li>row - The grid row index</li>
65641              * <li>column - The grid column index</li>
65642              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
65643              * </ul>
65644              * @param {Object} e An edit event (see above for description)
65645              */
65646             "beforeedit" : true,
65647             /**
65648              * @event afteredit
65649              * Fires after a cell is edited. <br />
65650              * <ul style="padding:5px;padding-left:16px;">
65651              * <li>grid - This grid</li>
65652              * <li>record - The record being edited</li>
65653              * <li>field - The field name being edited</li>
65654              * <li>value - The value being set</li>
65655              * <li>originalValue - The original value for the field, before the edit.</li>
65656              * <li>row - The grid row index</li>
65657              * <li>column - The grid column index</li>
65658              * </ul>
65659              * @param {Object} e An edit event (see above for description)
65660              */
65661             "afteredit" : true,
65662             /**
65663              * @event validateedit
65664              * Fires after a cell is edited, but before the value is set in the record. 
65665          * You can use this to modify the value being set in the field, Return false
65666              * to cancel the change. The edit event object has the following properties <br />
65667              * <ul style="padding:5px;padding-left:16px;">
65668          * <li>editor - This editor</li>
65669              * <li>grid - This grid</li>
65670              * <li>record - The record being edited</li>
65671              * <li>field - The field name being edited</li>
65672              * <li>value - The value being set</li>
65673              * <li>originalValue - The original value for the field, before the edit.</li>
65674              * <li>row - The grid row index</li>
65675              * <li>column - The grid column index</li>
65676              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
65677              * </ul>
65678              * @param {Object} e An edit event (see above for description)
65679              */
65680             "validateedit" : true
65681         });
65682     this.on("bodyscroll", this.stopEditing,  this);
65683     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
65684 };
65685
65686 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
65687     /**
65688      * @cfg {Number} clicksToEdit
65689      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
65690      */
65691     clicksToEdit: 2,
65692
65693     // private
65694     isEditor : true,
65695     // private
65696     trackMouseOver: false, // causes very odd FF errors
65697
65698     onCellDblClick : function(g, row, col){
65699         this.startEditing(row, col);
65700     },
65701
65702     onEditComplete : function(ed, value, startValue){
65703         this.editing = false;
65704         this.activeEditor = null;
65705         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
65706         var r = ed.record;
65707         var field = this.colModel.getDataIndex(ed.col);
65708         var e = {
65709             grid: this,
65710             record: r,
65711             field: field,
65712             originalValue: startValue,
65713             value: value,
65714             row: ed.row,
65715             column: ed.col,
65716             cancel:false,
65717             editor: ed
65718         };
65719         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
65720         cell.show();
65721           
65722         if(String(value) !== String(startValue)){
65723             
65724             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
65725                 r.set(field, e.value);
65726                 // if we are dealing with a combo box..
65727                 // then we also set the 'name' colum to be the displayField
65728                 if (ed.field.displayField && ed.field.name) {
65729                     r.set(ed.field.name, ed.field.el.dom.value);
65730                 }
65731                 
65732                 delete e.cancel; //?? why!!!
65733                 this.fireEvent("afteredit", e);
65734             }
65735         } else {
65736             this.fireEvent("afteredit", e); // always fire it!
65737         }
65738         this.view.focusCell(ed.row, ed.col);
65739     },
65740
65741     /**
65742      * Starts editing the specified for the specified row/column
65743      * @param {Number} rowIndex
65744      * @param {Number} colIndex
65745      */
65746     startEditing : function(row, col){
65747         this.stopEditing();
65748         if(this.colModel.isCellEditable(col, row)){
65749             this.view.ensureVisible(row, col, true);
65750           
65751             var r = this.dataSource.getAt(row);
65752             var field = this.colModel.getDataIndex(col);
65753             var cell = Roo.get(this.view.getCell(row,col));
65754             var e = {
65755                 grid: this,
65756                 record: r,
65757                 field: field,
65758                 value: r.data[field],
65759                 row: row,
65760                 column: col,
65761                 cancel:false 
65762             };
65763             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
65764                 this.editing = true;
65765                 var ed = this.colModel.getCellEditor(col, row);
65766                 
65767                 if (!ed) {
65768                     return;
65769                 }
65770                 if(!ed.rendered){
65771                     ed.render(ed.parentEl || document.body);
65772                 }
65773                 ed.field.reset();
65774                
65775                 cell.hide();
65776                 
65777                 (function(){ // complex but required for focus issues in safari, ie and opera
65778                     ed.row = row;
65779                     ed.col = col;
65780                     ed.record = r;
65781                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
65782                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
65783                     this.activeEditor = ed;
65784                     var v = r.data[field];
65785                     ed.startEdit(this.view.getCell(row, col), v);
65786                     // combo's with 'displayField and name set
65787                     if (ed.field.displayField && ed.field.name) {
65788                         ed.field.el.dom.value = r.data[ed.field.name];
65789                     }
65790                     
65791                     
65792                 }).defer(50, this);
65793             }
65794         }
65795     },
65796         
65797     /**
65798      * Stops any active editing
65799      */
65800     stopEditing : function(){
65801         if(this.activeEditor){
65802             this.activeEditor.completeEdit();
65803         }
65804         this.activeEditor = null;
65805     },
65806         
65807          /**
65808      * Called to get grid's drag proxy text, by default returns this.ddText.
65809      * @return {String}
65810      */
65811     getDragDropText : function(){
65812         var count = this.selModel.getSelectedCell() ? 1 : 0;
65813         return String.format(this.ddText, count, count == 1 ? '' : 's');
65814     }
65815         
65816 });/*
65817  * Based on:
65818  * Ext JS Library 1.1.1
65819  * Copyright(c) 2006-2007, Ext JS, LLC.
65820  *
65821  * Originally Released Under LGPL - original licence link has changed is not relivant.
65822  *
65823  * Fork - LGPL
65824  * <script type="text/javascript">
65825  */
65826
65827 // private - not really -- you end up using it !
65828 // This is a support class used internally by the Grid components
65829
65830 /**
65831  * @class Roo.grid.GridEditor
65832  * @extends Roo.Editor
65833  * Class for creating and editable grid elements.
65834  * @param {Object} config any settings (must include field)
65835  */
65836 Roo.grid.GridEditor = function(field, config){
65837     if (!config && field.field) {
65838         config = field;
65839         field = Roo.factory(config.field, Roo.form);
65840     }
65841     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
65842     field.monitorTab = false;
65843 };
65844
65845 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
65846     
65847     /**
65848      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
65849      */
65850     
65851     alignment: "tl-tl",
65852     autoSize: "width",
65853     hideEl : false,
65854     cls: "x-small-editor x-grid-editor",
65855     shim:false,
65856     shadow:"frame"
65857 });/*
65858  * Based on:
65859  * Ext JS Library 1.1.1
65860  * Copyright(c) 2006-2007, Ext JS, LLC.
65861  *
65862  * Originally Released Under LGPL - original licence link has changed is not relivant.
65863  *
65864  * Fork - LGPL
65865  * <script type="text/javascript">
65866  */
65867   
65868
65869   
65870 Roo.grid.PropertyRecord = Roo.data.Record.create([
65871     {name:'name',type:'string'},  'value'
65872 ]);
65873
65874
65875 Roo.grid.PropertyStore = function(grid, source){
65876     this.grid = grid;
65877     this.store = new Roo.data.Store({
65878         recordType : Roo.grid.PropertyRecord
65879     });
65880     this.store.on('update', this.onUpdate,  this);
65881     if(source){
65882         this.setSource(source);
65883     }
65884     Roo.grid.PropertyStore.superclass.constructor.call(this);
65885 };
65886
65887
65888
65889 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
65890     setSource : function(o){
65891         this.source = o;
65892         this.store.removeAll();
65893         var data = [];
65894         for(var k in o){
65895             if(this.isEditableValue(o[k])){
65896                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
65897             }
65898         }
65899         this.store.loadRecords({records: data}, {}, true);
65900     },
65901
65902     onUpdate : function(ds, record, type){
65903         if(type == Roo.data.Record.EDIT){
65904             var v = record.data['value'];
65905             var oldValue = record.modified['value'];
65906             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
65907                 this.source[record.id] = v;
65908                 record.commit();
65909                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
65910             }else{
65911                 record.reject();
65912             }
65913         }
65914     },
65915
65916     getProperty : function(row){
65917        return this.store.getAt(row);
65918     },
65919
65920     isEditableValue: function(val){
65921         if(val && val instanceof Date){
65922             return true;
65923         }else if(typeof val == 'object' || typeof val == 'function'){
65924             return false;
65925         }
65926         return true;
65927     },
65928
65929     setValue : function(prop, value){
65930         this.source[prop] = value;
65931         this.store.getById(prop).set('value', value);
65932     },
65933
65934     getSource : function(){
65935         return this.source;
65936     }
65937 });
65938
65939 Roo.grid.PropertyColumnModel = function(grid, store){
65940     this.grid = grid;
65941     var g = Roo.grid;
65942     g.PropertyColumnModel.superclass.constructor.call(this, [
65943         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
65944         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
65945     ]);
65946     this.store = store;
65947     this.bselect = Roo.DomHelper.append(document.body, {
65948         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
65949             {tag: 'option', value: 'true', html: 'true'},
65950             {tag: 'option', value: 'false', html: 'false'}
65951         ]
65952     });
65953     Roo.id(this.bselect);
65954     var f = Roo.form;
65955     this.editors = {
65956         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
65957         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
65958         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
65959         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
65960         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
65961     };
65962     this.renderCellDelegate = this.renderCell.createDelegate(this);
65963     this.renderPropDelegate = this.renderProp.createDelegate(this);
65964 };
65965
65966 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
65967     
65968     
65969     nameText : 'Name',
65970     valueText : 'Value',
65971     
65972     dateFormat : 'm/j/Y',
65973     
65974     
65975     renderDate : function(dateVal){
65976         return dateVal.dateFormat(this.dateFormat);
65977     },
65978
65979     renderBool : function(bVal){
65980         return bVal ? 'true' : 'false';
65981     },
65982
65983     isCellEditable : function(colIndex, rowIndex){
65984         return colIndex == 1;
65985     },
65986
65987     getRenderer : function(col){
65988         return col == 1 ?
65989             this.renderCellDelegate : this.renderPropDelegate;
65990     },
65991
65992     renderProp : function(v){
65993         return this.getPropertyName(v);
65994     },
65995
65996     renderCell : function(val){
65997         var rv = val;
65998         if(val instanceof Date){
65999             rv = this.renderDate(val);
66000         }else if(typeof val == 'boolean'){
66001             rv = this.renderBool(val);
66002         }
66003         return Roo.util.Format.htmlEncode(rv);
66004     },
66005
66006     getPropertyName : function(name){
66007         var pn = this.grid.propertyNames;
66008         return pn && pn[name] ? pn[name] : name;
66009     },
66010
66011     getCellEditor : function(colIndex, rowIndex){
66012         var p = this.store.getProperty(rowIndex);
66013         var n = p.data['name'], val = p.data['value'];
66014         
66015         if(typeof(this.grid.customEditors[n]) == 'string'){
66016             return this.editors[this.grid.customEditors[n]];
66017         }
66018         if(typeof(this.grid.customEditors[n]) != 'undefined'){
66019             return this.grid.customEditors[n];
66020         }
66021         if(val instanceof Date){
66022             return this.editors['date'];
66023         }else if(typeof val == 'number'){
66024             return this.editors['number'];
66025         }else if(typeof val == 'boolean'){
66026             return this.editors['boolean'];
66027         }else{
66028             return this.editors['string'];
66029         }
66030     }
66031 });
66032
66033 /**
66034  * @class Roo.grid.PropertyGrid
66035  * @extends Roo.grid.EditorGrid
66036  * This class represents the  interface of a component based property grid control.
66037  * <br><br>Usage:<pre><code>
66038  var grid = new Roo.grid.PropertyGrid("my-container-id", {
66039       
66040  });
66041  // set any options
66042  grid.render();
66043  * </code></pre>
66044   
66045  * @constructor
66046  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66047  * The container MUST have some type of size defined for the grid to fill. The container will be
66048  * automatically set to position relative if it isn't already.
66049  * @param {Object} config A config object that sets properties on this grid.
66050  */
66051 Roo.grid.PropertyGrid = function(container, config){
66052     config = config || {};
66053     var store = new Roo.grid.PropertyStore(this);
66054     this.store = store;
66055     var cm = new Roo.grid.PropertyColumnModel(this, store);
66056     store.store.sort('name', 'ASC');
66057     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
66058         ds: store.store,
66059         cm: cm,
66060         enableColLock:false,
66061         enableColumnMove:false,
66062         stripeRows:false,
66063         trackMouseOver: false,
66064         clicksToEdit:1
66065     }, config));
66066     this.getGridEl().addClass('x-props-grid');
66067     this.lastEditRow = null;
66068     this.on('columnresize', this.onColumnResize, this);
66069     this.addEvents({
66070          /**
66071              * @event beforepropertychange
66072              * Fires before a property changes (return false to stop?)
66073              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66074              * @param {String} id Record Id
66075              * @param {String} newval New Value
66076          * @param {String} oldval Old Value
66077              */
66078         "beforepropertychange": true,
66079         /**
66080              * @event propertychange
66081              * Fires after a property changes
66082              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66083              * @param {String} id Record Id
66084              * @param {String} newval New Value
66085          * @param {String} oldval Old Value
66086              */
66087         "propertychange": true
66088     });
66089     this.customEditors = this.customEditors || {};
66090 };
66091 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
66092     
66093      /**
66094      * @cfg {Object} customEditors map of colnames=> custom editors.
66095      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
66096      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
66097      * false disables editing of the field.
66098          */
66099     
66100       /**
66101      * @cfg {Object} propertyNames map of property Names to their displayed value
66102          */
66103     
66104     render : function(){
66105         Roo.grid.PropertyGrid.superclass.render.call(this);
66106         this.autoSize.defer(100, this);
66107     },
66108
66109     autoSize : function(){
66110         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
66111         if(this.view){
66112             this.view.fitColumns();
66113         }
66114     },
66115
66116     onColumnResize : function(){
66117         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
66118         this.autoSize();
66119     },
66120     /**
66121      * Sets the data for the Grid
66122      * accepts a Key => Value object of all the elements avaiable.
66123      * @param {Object} data  to appear in grid.
66124      */
66125     setSource : function(source){
66126         this.store.setSource(source);
66127         //this.autoSize();
66128     },
66129     /**
66130      * Gets all the data from the grid.
66131      * @return {Object} data  data stored in grid
66132      */
66133     getSource : function(){
66134         return this.store.getSource();
66135     }
66136 });/*
66137   
66138  * Licence LGPL
66139  
66140  */
66141  
66142 /**
66143  * @class Roo.grid.Calendar
66144  * @extends Roo.grid.Grid
66145  * This class extends the Grid to provide a calendar widget
66146  * <br><br>Usage:<pre><code>
66147  var grid = new Roo.grid.Calendar("my-container-id", {
66148      ds: myDataStore,
66149      cm: myColModel,
66150      selModel: mySelectionModel,
66151      autoSizeColumns: true,
66152      monitorWindowResize: false,
66153      trackMouseOver: true
66154      eventstore : real data store..
66155  });
66156  // set any options
66157  grid.render();
66158   
66159   * @constructor
66160  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66161  * The container MUST have some type of size defined for the grid to fill. The container will be
66162  * automatically set to position relative if it isn't already.
66163  * @param {Object} config A config object that sets properties on this grid.
66164  */
66165 Roo.grid.Calendar = function(container, config){
66166         // initialize the container
66167         this.container = Roo.get(container);
66168         this.container.update("");
66169         this.container.setStyle("overflow", "hidden");
66170     this.container.addClass('x-grid-container');
66171
66172     this.id = this.container.id;
66173
66174     Roo.apply(this, config);
66175     // check and correct shorthanded configs
66176     
66177     var rows = [];
66178     var d =1;
66179     for (var r = 0;r < 6;r++) {
66180         
66181         rows[r]=[];
66182         for (var c =0;c < 7;c++) {
66183             rows[r][c]= '';
66184         }
66185     }
66186     if (this.eventStore) {
66187         this.eventStore= Roo.factory(this.eventStore, Roo.data);
66188         this.eventStore.on('load',this.onLoad, this);
66189         this.eventStore.on('beforeload',this.clearEvents, this);
66190          
66191     }
66192     
66193     this.dataSource = new Roo.data.Store({
66194             proxy: new Roo.data.MemoryProxy(rows),
66195             reader: new Roo.data.ArrayReader({}, [
66196                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
66197     });
66198
66199     this.dataSource.load();
66200     this.ds = this.dataSource;
66201     this.ds.xmodule = this.xmodule || false;
66202     
66203     
66204     var cellRender = function(v,x,r)
66205     {
66206         return String.format(
66207             '<div class="fc-day  fc-widget-content"><div>' +
66208                 '<div class="fc-event-container"></div>' +
66209                 '<div class="fc-day-number">{0}</div>'+
66210                 
66211                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
66212             '</div></div>', v);
66213     
66214     }
66215     
66216     
66217     this.colModel = new Roo.grid.ColumnModel( [
66218         {
66219             xtype: 'ColumnModel',
66220             xns: Roo.grid,
66221             dataIndex : 'weekday0',
66222             header : 'Sunday',
66223             renderer : cellRender
66224         },
66225         {
66226             xtype: 'ColumnModel',
66227             xns: Roo.grid,
66228             dataIndex : 'weekday1',
66229             header : 'Monday',
66230             renderer : cellRender
66231         },
66232         {
66233             xtype: 'ColumnModel',
66234             xns: Roo.grid,
66235             dataIndex : 'weekday2',
66236             header : 'Tuesday',
66237             renderer : cellRender
66238         },
66239         {
66240             xtype: 'ColumnModel',
66241             xns: Roo.grid,
66242             dataIndex : 'weekday3',
66243             header : 'Wednesday',
66244             renderer : cellRender
66245         },
66246         {
66247             xtype: 'ColumnModel',
66248             xns: Roo.grid,
66249             dataIndex : 'weekday4',
66250             header : 'Thursday',
66251             renderer : cellRender
66252         },
66253         {
66254             xtype: 'ColumnModel',
66255             xns: Roo.grid,
66256             dataIndex : 'weekday5',
66257             header : 'Friday',
66258             renderer : cellRender
66259         },
66260         {
66261             xtype: 'ColumnModel',
66262             xns: Roo.grid,
66263             dataIndex : 'weekday6',
66264             header : 'Saturday',
66265             renderer : cellRender
66266         }
66267     ]);
66268     this.cm = this.colModel;
66269     this.cm.xmodule = this.xmodule || false;
66270  
66271         
66272           
66273     //this.selModel = new Roo.grid.CellSelectionModel();
66274     //this.sm = this.selModel;
66275     //this.selModel.init(this);
66276     
66277     
66278     if(this.width){
66279         this.container.setWidth(this.width);
66280     }
66281
66282     if(this.height){
66283         this.container.setHeight(this.height);
66284     }
66285     /** @private */
66286         this.addEvents({
66287         // raw events
66288         /**
66289          * @event click
66290          * The raw click event for the entire grid.
66291          * @param {Roo.EventObject} e
66292          */
66293         "click" : true,
66294         /**
66295          * @event dblclick
66296          * The raw dblclick event for the entire grid.
66297          * @param {Roo.EventObject} e
66298          */
66299         "dblclick" : true,
66300         /**
66301          * @event contextmenu
66302          * The raw contextmenu event for the entire grid.
66303          * @param {Roo.EventObject} e
66304          */
66305         "contextmenu" : true,
66306         /**
66307          * @event mousedown
66308          * The raw mousedown event for the entire grid.
66309          * @param {Roo.EventObject} e
66310          */
66311         "mousedown" : true,
66312         /**
66313          * @event mouseup
66314          * The raw mouseup event for the entire grid.
66315          * @param {Roo.EventObject} e
66316          */
66317         "mouseup" : true,
66318         /**
66319          * @event mouseover
66320          * The raw mouseover event for the entire grid.
66321          * @param {Roo.EventObject} e
66322          */
66323         "mouseover" : true,
66324         /**
66325          * @event mouseout
66326          * The raw mouseout event for the entire grid.
66327          * @param {Roo.EventObject} e
66328          */
66329         "mouseout" : true,
66330         /**
66331          * @event keypress
66332          * The raw keypress event for the entire grid.
66333          * @param {Roo.EventObject} e
66334          */
66335         "keypress" : true,
66336         /**
66337          * @event keydown
66338          * The raw keydown event for the entire grid.
66339          * @param {Roo.EventObject} e
66340          */
66341         "keydown" : true,
66342
66343         // custom events
66344
66345         /**
66346          * @event cellclick
66347          * Fires when a cell is clicked
66348          * @param {Grid} this
66349          * @param {Number} rowIndex
66350          * @param {Number} columnIndex
66351          * @param {Roo.EventObject} e
66352          */
66353         "cellclick" : true,
66354         /**
66355          * @event celldblclick
66356          * Fires when a cell is double clicked
66357          * @param {Grid} this
66358          * @param {Number} rowIndex
66359          * @param {Number} columnIndex
66360          * @param {Roo.EventObject} e
66361          */
66362         "celldblclick" : true,
66363         /**
66364          * @event rowclick
66365          * Fires when a row is clicked
66366          * @param {Grid} this
66367          * @param {Number} rowIndex
66368          * @param {Roo.EventObject} e
66369          */
66370         "rowclick" : true,
66371         /**
66372          * @event rowdblclick
66373          * Fires when a row is double clicked
66374          * @param {Grid} this
66375          * @param {Number} rowIndex
66376          * @param {Roo.EventObject} e
66377          */
66378         "rowdblclick" : true,
66379         /**
66380          * @event headerclick
66381          * Fires when a header is clicked
66382          * @param {Grid} this
66383          * @param {Number} columnIndex
66384          * @param {Roo.EventObject} e
66385          */
66386         "headerclick" : true,
66387         /**
66388          * @event headerdblclick
66389          * Fires when a header cell is double clicked
66390          * @param {Grid} this
66391          * @param {Number} columnIndex
66392          * @param {Roo.EventObject} e
66393          */
66394         "headerdblclick" : true,
66395         /**
66396          * @event rowcontextmenu
66397          * Fires when a row is right clicked
66398          * @param {Grid} this
66399          * @param {Number} rowIndex
66400          * @param {Roo.EventObject} e
66401          */
66402         "rowcontextmenu" : true,
66403         /**
66404          * @event cellcontextmenu
66405          * Fires when a cell is right clicked
66406          * @param {Grid} this
66407          * @param {Number} rowIndex
66408          * @param {Number} cellIndex
66409          * @param {Roo.EventObject} e
66410          */
66411          "cellcontextmenu" : true,
66412         /**
66413          * @event headercontextmenu
66414          * Fires when a header is right clicked
66415          * @param {Grid} this
66416          * @param {Number} columnIndex
66417          * @param {Roo.EventObject} e
66418          */
66419         "headercontextmenu" : true,
66420         /**
66421          * @event bodyscroll
66422          * Fires when the body element is scrolled
66423          * @param {Number} scrollLeft
66424          * @param {Number} scrollTop
66425          */
66426         "bodyscroll" : true,
66427         /**
66428          * @event columnresize
66429          * Fires when the user resizes a column
66430          * @param {Number} columnIndex
66431          * @param {Number} newSize
66432          */
66433         "columnresize" : true,
66434         /**
66435          * @event columnmove
66436          * Fires when the user moves a column
66437          * @param {Number} oldIndex
66438          * @param {Number} newIndex
66439          */
66440         "columnmove" : true,
66441         /**
66442          * @event startdrag
66443          * Fires when row(s) start being dragged
66444          * @param {Grid} this
66445          * @param {Roo.GridDD} dd The drag drop object
66446          * @param {event} e The raw browser event
66447          */
66448         "startdrag" : true,
66449         /**
66450          * @event enddrag
66451          * Fires when a drag operation is complete
66452          * @param {Grid} this
66453          * @param {Roo.GridDD} dd The drag drop object
66454          * @param {event} e The raw browser event
66455          */
66456         "enddrag" : true,
66457         /**
66458          * @event dragdrop
66459          * Fires when dragged row(s) are dropped on a valid DD target
66460          * @param {Grid} this
66461          * @param {Roo.GridDD} dd The drag drop object
66462          * @param {String} targetId The target drag drop object
66463          * @param {event} e The raw browser event
66464          */
66465         "dragdrop" : true,
66466         /**
66467          * @event dragover
66468          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
66469          * @param {Grid} this
66470          * @param {Roo.GridDD} dd The drag drop object
66471          * @param {String} targetId The target drag drop object
66472          * @param {event} e The raw browser event
66473          */
66474         "dragover" : true,
66475         /**
66476          * @event dragenter
66477          *  Fires when the dragged row(s) first cross another DD target while being dragged
66478          * @param {Grid} this
66479          * @param {Roo.GridDD} dd The drag drop object
66480          * @param {String} targetId The target drag drop object
66481          * @param {event} e The raw browser event
66482          */
66483         "dragenter" : true,
66484         /**
66485          * @event dragout
66486          * Fires when the dragged row(s) leave another DD target while being dragged
66487          * @param {Grid} this
66488          * @param {Roo.GridDD} dd The drag drop object
66489          * @param {String} targetId The target drag drop object
66490          * @param {event} e The raw browser event
66491          */
66492         "dragout" : true,
66493         /**
66494          * @event rowclass
66495          * Fires when a row is rendered, so you can change add a style to it.
66496          * @param {GridView} gridview   The grid view
66497          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
66498          */
66499         'rowclass' : true,
66500
66501         /**
66502          * @event render
66503          * Fires when the grid is rendered
66504          * @param {Grid} grid
66505          */
66506         'render' : true,
66507             /**
66508              * @event select
66509              * Fires when a date is selected
66510              * @param {DatePicker} this
66511              * @param {Date} date The selected date
66512              */
66513         'select': true,
66514         /**
66515              * @event monthchange
66516              * Fires when the displayed month changes 
66517              * @param {DatePicker} this
66518              * @param {Date} date The selected month
66519              */
66520         'monthchange': true,
66521         /**
66522              * @event evententer
66523              * Fires when mouse over an event
66524              * @param {Calendar} this
66525              * @param {event} Event
66526              */
66527         'evententer': true,
66528         /**
66529              * @event eventleave
66530              * Fires when the mouse leaves an
66531              * @param {Calendar} this
66532              * @param {event}
66533              */
66534         'eventleave': true,
66535         /**
66536              * @event eventclick
66537              * Fires when the mouse click an
66538              * @param {Calendar} this
66539              * @param {event}
66540              */
66541         'eventclick': true,
66542         /**
66543              * @event eventrender
66544              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
66545              * @param {Calendar} this
66546              * @param {data} data to be modified
66547              */
66548         'eventrender': true
66549         
66550     });
66551
66552     Roo.grid.Grid.superclass.constructor.call(this);
66553     this.on('render', function() {
66554         this.view.el.addClass('x-grid-cal'); 
66555         
66556         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
66557
66558     },this);
66559     
66560     if (!Roo.grid.Calendar.style) {
66561         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
66562             
66563             
66564             '.x-grid-cal .x-grid-col' :  {
66565                 height: 'auto !important',
66566                 'vertical-align': 'top'
66567             },
66568             '.x-grid-cal  .fc-event-hori' : {
66569                 height: '14px'
66570             }
66571              
66572             
66573         }, Roo.id());
66574     }
66575
66576     
66577     
66578 };
66579 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
66580     /**
66581      * @cfg {Store} eventStore The store that loads events.
66582      */
66583     eventStore : 25,
66584
66585      
66586     activeDate : false,
66587     startDay : 0,
66588     autoWidth : true,
66589     monitorWindowResize : false,
66590
66591     
66592     resizeColumns : function() {
66593         var col = (this.view.el.getWidth() / 7) - 3;
66594         // loop through cols, and setWidth
66595         for(var i =0 ; i < 7 ; i++){
66596             this.cm.setColumnWidth(i, col);
66597         }
66598     },
66599      setDate :function(date) {
66600         
66601         Roo.log('setDate?');
66602         
66603         this.resizeColumns();
66604         var vd = this.activeDate;
66605         this.activeDate = date;
66606 //        if(vd && this.el){
66607 //            var t = date.getTime();
66608 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
66609 //                Roo.log('using add remove');
66610 //                
66611 //                this.fireEvent('monthchange', this, date);
66612 //                
66613 //                this.cells.removeClass("fc-state-highlight");
66614 //                this.cells.each(function(c){
66615 //                   if(c.dateValue == t){
66616 //                       c.addClass("fc-state-highlight");
66617 //                       setTimeout(function(){
66618 //                            try{c.dom.firstChild.focus();}catch(e){}
66619 //                       }, 50);
66620 //                       return false;
66621 //                   }
66622 //                   return true;
66623 //                });
66624 //                return;
66625 //            }
66626 //        }
66627         
66628         var days = date.getDaysInMonth();
66629         
66630         var firstOfMonth = date.getFirstDateOfMonth();
66631         var startingPos = firstOfMonth.getDay()-this.startDay;
66632         
66633         if(startingPos < this.startDay){
66634             startingPos += 7;
66635         }
66636         
66637         var pm = date.add(Date.MONTH, -1);
66638         var prevStart = pm.getDaysInMonth()-startingPos;
66639 //        
66640         
66641         
66642         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
66643         
66644         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
66645         //this.cells.addClassOnOver('fc-state-hover');
66646         
66647         var cells = this.cells.elements;
66648         var textEls = this.textNodes;
66649         
66650         //Roo.each(cells, function(cell){
66651         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
66652         //});
66653         
66654         days += startingPos;
66655
66656         // convert everything to numbers so it's fast
66657         var day = 86400000;
66658         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
66659         //Roo.log(d);
66660         //Roo.log(pm);
66661         //Roo.log(prevStart);
66662         
66663         var today = new Date().clearTime().getTime();
66664         var sel = date.clearTime().getTime();
66665         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
66666         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
66667         var ddMatch = this.disabledDatesRE;
66668         var ddText = this.disabledDatesText;
66669         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
66670         var ddaysText = this.disabledDaysText;
66671         var format = this.format;
66672         
66673         var setCellClass = function(cal, cell){
66674             
66675             //Roo.log('set Cell Class');
66676             cell.title = "";
66677             var t = d.getTime();
66678             
66679             //Roo.log(d);
66680             
66681             
66682             cell.dateValue = t;
66683             if(t == today){
66684                 cell.className += " fc-today";
66685                 cell.className += " fc-state-highlight";
66686                 cell.title = cal.todayText;
66687             }
66688             if(t == sel){
66689                 // disable highlight in other month..
66690                 cell.className += " fc-state-highlight";
66691                 
66692             }
66693             // disabling
66694             if(t < min) {
66695                 //cell.className = " fc-state-disabled";
66696                 cell.title = cal.minText;
66697                 return;
66698             }
66699             if(t > max) {
66700                 //cell.className = " fc-state-disabled";
66701                 cell.title = cal.maxText;
66702                 return;
66703             }
66704             if(ddays){
66705                 if(ddays.indexOf(d.getDay()) != -1){
66706                     // cell.title = ddaysText;
66707                    // cell.className = " fc-state-disabled";
66708                 }
66709             }
66710             if(ddMatch && format){
66711                 var fvalue = d.dateFormat(format);
66712                 if(ddMatch.test(fvalue)){
66713                     cell.title = ddText.replace("%0", fvalue);
66714                    cell.className = " fc-state-disabled";
66715                 }
66716             }
66717             
66718             if (!cell.initialClassName) {
66719                 cell.initialClassName = cell.dom.className;
66720             }
66721             
66722             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
66723         };
66724
66725         var i = 0;
66726         
66727         for(; i < startingPos; i++) {
66728             cells[i].dayName =  (++prevStart);
66729             Roo.log(textEls[i]);
66730             d.setDate(d.getDate()+1);
66731             
66732             //cells[i].className = "fc-past fc-other-month";
66733             setCellClass(this, cells[i]);
66734         }
66735         
66736         var intDay = 0;
66737         
66738         for(; i < days; i++){
66739             intDay = i - startingPos + 1;
66740             cells[i].dayName =  (intDay);
66741             d.setDate(d.getDate()+1);
66742             
66743             cells[i].className = ''; // "x-date-active";
66744             setCellClass(this, cells[i]);
66745         }
66746         var extraDays = 0;
66747         
66748         for(; i < 42; i++) {
66749             //textEls[i].innerHTML = (++extraDays);
66750             
66751             d.setDate(d.getDate()+1);
66752             cells[i].dayName = (++extraDays);
66753             cells[i].className = "fc-future fc-other-month";
66754             setCellClass(this, cells[i]);
66755         }
66756         
66757         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
66758         
66759         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
66760         
66761         // this will cause all the cells to mis
66762         var rows= [];
66763         var i =0;
66764         for (var r = 0;r < 6;r++) {
66765             for (var c =0;c < 7;c++) {
66766                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
66767             }    
66768         }
66769         
66770         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
66771         for(i=0;i<cells.length;i++) {
66772             
66773             this.cells.elements[i].dayName = cells[i].dayName ;
66774             this.cells.elements[i].className = cells[i].className;
66775             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
66776             this.cells.elements[i].title = cells[i].title ;
66777             this.cells.elements[i].dateValue = cells[i].dateValue ;
66778         }
66779         
66780         
66781         
66782         
66783         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
66784         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
66785         
66786         ////if(totalRows != 6){
66787             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
66788            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
66789        // }
66790         
66791         this.fireEvent('monthchange', this, date);
66792         
66793         
66794     },
66795  /**
66796      * Returns the grid's SelectionModel.
66797      * @return {SelectionModel}
66798      */
66799     getSelectionModel : function(){
66800         if(!this.selModel){
66801             this.selModel = new Roo.grid.CellSelectionModel();
66802         }
66803         return this.selModel;
66804     },
66805
66806     load: function() {
66807         this.eventStore.load()
66808         
66809         
66810         
66811     },
66812     
66813     findCell : function(dt) {
66814         dt = dt.clearTime().getTime();
66815         var ret = false;
66816         this.cells.each(function(c){
66817             //Roo.log("check " +c.dateValue + '?=' + dt);
66818             if(c.dateValue == dt){
66819                 ret = c;
66820                 return false;
66821             }
66822             return true;
66823         });
66824         
66825         return ret;
66826     },
66827     
66828     findCells : function(rec) {
66829         var s = rec.data.start_dt.clone().clearTime().getTime();
66830        // Roo.log(s);
66831         var e= rec.data.end_dt.clone().clearTime().getTime();
66832        // Roo.log(e);
66833         var ret = [];
66834         this.cells.each(function(c){
66835              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
66836             
66837             if(c.dateValue > e){
66838                 return ;
66839             }
66840             if(c.dateValue < s){
66841                 return ;
66842             }
66843             ret.push(c);
66844         });
66845         
66846         return ret;    
66847     },
66848     
66849     findBestRow: function(cells)
66850     {
66851         var ret = 0;
66852         
66853         for (var i =0 ; i < cells.length;i++) {
66854             ret  = Math.max(cells[i].rows || 0,ret);
66855         }
66856         return ret;
66857         
66858     },
66859     
66860     
66861     addItem : function(rec)
66862     {
66863         // look for vertical location slot in
66864         var cells = this.findCells(rec);
66865         
66866         rec.row = this.findBestRow(cells);
66867         
66868         // work out the location.
66869         
66870         var crow = false;
66871         var rows = [];
66872         for(var i =0; i < cells.length; i++) {
66873             if (!crow) {
66874                 crow = {
66875                     start : cells[i],
66876                     end :  cells[i]
66877                 };
66878                 continue;
66879             }
66880             if (crow.start.getY() == cells[i].getY()) {
66881                 // on same row.
66882                 crow.end = cells[i];
66883                 continue;
66884             }
66885             // different row.
66886             rows.push(crow);
66887             crow = {
66888                 start: cells[i],
66889                 end : cells[i]
66890             };
66891             
66892         }
66893         
66894         rows.push(crow);
66895         rec.els = [];
66896         rec.rows = rows;
66897         rec.cells = cells;
66898         for (var i = 0; i < cells.length;i++) {
66899             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
66900             
66901         }
66902         
66903         
66904     },
66905     
66906     clearEvents: function() {
66907         
66908         if (!this.eventStore.getCount()) {
66909             return;
66910         }
66911         // reset number of rows in cells.
66912         Roo.each(this.cells.elements, function(c){
66913             c.rows = 0;
66914         });
66915         
66916         this.eventStore.each(function(e) {
66917             this.clearEvent(e);
66918         },this);
66919         
66920     },
66921     
66922     clearEvent : function(ev)
66923     {
66924         if (ev.els) {
66925             Roo.each(ev.els, function(el) {
66926                 el.un('mouseenter' ,this.onEventEnter, this);
66927                 el.un('mouseleave' ,this.onEventLeave, this);
66928                 el.remove();
66929             },this);
66930             ev.els = [];
66931         }
66932     },
66933     
66934     
66935     renderEvent : function(ev,ctr) {
66936         if (!ctr) {
66937              ctr = this.view.el.select('.fc-event-container',true).first();
66938         }
66939         
66940          
66941         this.clearEvent(ev);
66942             //code
66943        
66944         
66945         
66946         ev.els = [];
66947         var cells = ev.cells;
66948         var rows = ev.rows;
66949         this.fireEvent('eventrender', this, ev);
66950         
66951         for(var i =0; i < rows.length; i++) {
66952             
66953             cls = '';
66954             if (i == 0) {
66955                 cls += ' fc-event-start';
66956             }
66957             if ((i+1) == rows.length) {
66958                 cls += ' fc-event-end';
66959             }
66960             
66961             //Roo.log(ev.data);
66962             // how many rows should it span..
66963             var cg = this.eventTmpl.append(ctr,Roo.apply({
66964                 fccls : cls
66965                 
66966             }, ev.data) , true);
66967             
66968             
66969             cg.on('mouseenter' ,this.onEventEnter, this, ev);
66970             cg.on('mouseleave' ,this.onEventLeave, this, ev);
66971             cg.on('click', this.onEventClick, this, ev);
66972             
66973             ev.els.push(cg);
66974             
66975             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
66976             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
66977             //Roo.log(cg);
66978              
66979             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
66980             cg.setWidth(ebox.right - sbox.x -2);
66981         }
66982     },
66983     
66984     renderEvents: function()
66985     {   
66986         // first make sure there is enough space..
66987         
66988         if (!this.eventTmpl) {
66989             this.eventTmpl = new Roo.Template(
66990                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
66991                     '<div class="fc-event-inner">' +
66992                         '<span class="fc-event-time">{time}</span>' +
66993                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
66994                     '</div>' +
66995                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
66996                 '</div>'
66997             );
66998                 
66999         }
67000                
67001         
67002         
67003         this.cells.each(function(c) {
67004             //Roo.log(c.select('.fc-day-content div',true).first());
67005             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
67006         });
67007         
67008         var ctr = this.view.el.select('.fc-event-container',true).first();
67009         
67010         var cls;
67011         this.eventStore.each(function(ev){
67012             
67013             this.renderEvent(ev);
67014              
67015              
67016         }, this);
67017         this.view.layout();
67018         
67019     },
67020     
67021     onEventEnter: function (e, el,event,d) {
67022         this.fireEvent('evententer', this, el, event);
67023     },
67024     
67025     onEventLeave: function (e, el,event,d) {
67026         this.fireEvent('eventleave', this, el, event);
67027     },
67028     
67029     onEventClick: function (e, el,event,d) {
67030         this.fireEvent('eventclick', this, el, event);
67031     },
67032     
67033     onMonthChange: function () {
67034         this.store.load();
67035     },
67036     
67037     onLoad: function () {
67038         
67039         //Roo.log('calendar onload');
67040 //         
67041         if(this.eventStore.getCount() > 0){
67042             
67043            
67044             
67045             this.eventStore.each(function(d){
67046                 
67047                 
67048                 // FIXME..
67049                 var add =   d.data;
67050                 if (typeof(add.end_dt) == 'undefined')  {
67051                     Roo.log("Missing End time in calendar data: ");
67052                     Roo.log(d);
67053                     return;
67054                 }
67055                 if (typeof(add.start_dt) == 'undefined')  {
67056                     Roo.log("Missing Start time in calendar data: ");
67057                     Roo.log(d);
67058                     return;
67059                 }
67060                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
67061                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
67062                 add.id = add.id || d.id;
67063                 add.title = add.title || '??';
67064                 
67065                 this.addItem(d);
67066                 
67067              
67068             },this);
67069         }
67070         
67071         this.renderEvents();
67072     }
67073     
67074
67075 });
67076 /*
67077  grid : {
67078                 xtype: 'Grid',
67079                 xns: Roo.grid,
67080                 listeners : {
67081                     render : function ()
67082                     {
67083                         _this.grid = this;
67084                         
67085                         if (!this.view.el.hasClass('course-timesheet')) {
67086                             this.view.el.addClass('course-timesheet');
67087                         }
67088                         if (this.tsStyle) {
67089                             this.ds.load({});
67090                             return; 
67091                         }
67092                         Roo.log('width');
67093                         Roo.log(_this.grid.view.el.getWidth());
67094                         
67095                         
67096                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
67097                             '.course-timesheet .x-grid-row' : {
67098                                 height: '80px'
67099                             },
67100                             '.x-grid-row td' : {
67101                                 'vertical-align' : 0
67102                             },
67103                             '.course-edit-link' : {
67104                                 'color' : 'blue',
67105                                 'text-overflow' : 'ellipsis',
67106                                 'overflow' : 'hidden',
67107                                 'white-space' : 'nowrap',
67108                                 'cursor' : 'pointer'
67109                             },
67110                             '.sub-link' : {
67111                                 'color' : 'green'
67112                             },
67113                             '.de-act-sup-link' : {
67114                                 'color' : 'purple',
67115                                 'text-decoration' : 'line-through'
67116                             },
67117                             '.de-act-link' : {
67118                                 'color' : 'red',
67119                                 'text-decoration' : 'line-through'
67120                             },
67121                             '.course-timesheet .course-highlight' : {
67122                                 'border-top-style': 'dashed !important',
67123                                 'border-bottom-bottom': 'dashed !important'
67124                             },
67125                             '.course-timesheet .course-item' : {
67126                                 'font-family'   : 'tahoma, arial, helvetica',
67127                                 'font-size'     : '11px',
67128                                 'overflow'      : 'hidden',
67129                                 'padding-left'  : '10px',
67130                                 'padding-right' : '10px',
67131                                 'padding-top' : '10px' 
67132                             }
67133                             
67134                         }, Roo.id());
67135                                 this.ds.load({});
67136                     }
67137                 },
67138                 autoWidth : true,
67139                 monitorWindowResize : false,
67140                 cellrenderer : function(v,x,r)
67141                 {
67142                     return v;
67143                 },
67144                 sm : {
67145                     xtype: 'CellSelectionModel',
67146                     xns: Roo.grid
67147                 },
67148                 dataSource : {
67149                     xtype: 'Store',
67150                     xns: Roo.data,
67151                     listeners : {
67152                         beforeload : function (_self, options)
67153                         {
67154                             options.params = options.params || {};
67155                             options.params._month = _this.monthField.getValue();
67156                             options.params.limit = 9999;
67157                             options.params['sort'] = 'when_dt';    
67158                             options.params['dir'] = 'ASC';    
67159                             this.proxy.loadResponse = this.loadResponse;
67160                             Roo.log("load?");
67161                             //this.addColumns();
67162                         },
67163                         load : function (_self, records, options)
67164                         {
67165                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
67166                                 // if you click on the translation.. you can edit it...
67167                                 var el = Roo.get(this);
67168                                 var id = el.dom.getAttribute('data-id');
67169                                 var d = el.dom.getAttribute('data-date');
67170                                 var t = el.dom.getAttribute('data-time');
67171                                 //var id = this.child('span').dom.textContent;
67172                                 
67173                                 //Roo.log(this);
67174                                 Pman.Dialog.CourseCalendar.show({
67175                                     id : id,
67176                                     when_d : d,
67177                                     when_t : t,
67178                                     productitem_active : id ? 1 : 0
67179                                 }, function() {
67180                                     _this.grid.ds.load({});
67181                                 });
67182                            
67183                            });
67184                            
67185                            _this.panel.fireEvent('resize', [ '', '' ]);
67186                         }
67187                     },
67188                     loadResponse : function(o, success, response){
67189                             // this is overridden on before load..
67190                             
67191                             Roo.log("our code?");       
67192                             //Roo.log(success);
67193                             //Roo.log(response)
67194                             delete this.activeRequest;
67195                             if(!success){
67196                                 this.fireEvent("loadexception", this, o, response);
67197                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67198                                 return;
67199                             }
67200                             var result;
67201                             try {
67202                                 result = o.reader.read(response);
67203                             }catch(e){
67204                                 Roo.log("load exception?");
67205                                 this.fireEvent("loadexception", this, o, response, e);
67206                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67207                                 return;
67208                             }
67209                             Roo.log("ready...");        
67210                             // loop through result.records;
67211                             // and set this.tdate[date] = [] << array of records..
67212                             _this.tdata  = {};
67213                             Roo.each(result.records, function(r){
67214                                 //Roo.log(r.data);
67215                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
67216                                     _this.tdata[r.data.when_dt.format('j')] = [];
67217                                 }
67218                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
67219                             });
67220                             
67221                             //Roo.log(_this.tdata);
67222                             
67223                             result.records = [];
67224                             result.totalRecords = 6;
67225                     
67226                             // let's generate some duumy records for the rows.
67227                             //var st = _this.dateField.getValue();
67228                             
67229                             // work out monday..
67230                             //st = st.add(Date.DAY, -1 * st.format('w'));
67231                             
67232                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67233                             
67234                             var firstOfMonth = date.getFirstDayOfMonth();
67235                             var days = date.getDaysInMonth();
67236                             var d = 1;
67237                             var firstAdded = false;
67238                             for (var i = 0; i < result.totalRecords ; i++) {
67239                                 //var d= st.add(Date.DAY, i);
67240                                 var row = {};
67241                                 var added = 0;
67242                                 for(var w = 0 ; w < 7 ; w++){
67243                                     if(!firstAdded && firstOfMonth != w){
67244                                         continue;
67245                                     }
67246                                     if(d > days){
67247                                         continue;
67248                                     }
67249                                     firstAdded = true;
67250                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
67251                                     row['weekday'+w] = String.format(
67252                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
67253                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
67254                                                     d,
67255                                                     date.format('Y-m-')+dd
67256                                                 );
67257                                     added++;
67258                                     if(typeof(_this.tdata[d]) != 'undefined'){
67259                                         Roo.each(_this.tdata[d], function(r){
67260                                             var is_sub = '';
67261                                             var deactive = '';
67262                                             var id = r.id;
67263                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
67264                                             if(r.parent_id*1>0){
67265                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
67266                                                 id = r.parent_id;
67267                                             }
67268                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
67269                                                 deactive = 'de-act-link';
67270                                             }
67271                                             
67272                                             row['weekday'+w] += String.format(
67273                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
67274                                                     id, //0
67275                                                     r.product_id_name, //1
67276                                                     r.when_dt.format('h:ia'), //2
67277                                                     is_sub, //3
67278                                                     deactive, //4
67279                                                     desc // 5
67280                                             );
67281                                         });
67282                                     }
67283                                     d++;
67284                                 }
67285                                 
67286                                 // only do this if something added..
67287                                 if(added > 0){ 
67288                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
67289                                 }
67290                                 
67291                                 
67292                                 // push it twice. (second one with an hour..
67293                                 
67294                             }
67295                             //Roo.log(result);
67296                             this.fireEvent("load", this, o, o.request.arg);
67297                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
67298                         },
67299                     sortInfo : {field: 'when_dt', direction : 'ASC' },
67300                     proxy : {
67301                         xtype: 'HttpProxy',
67302                         xns: Roo.data,
67303                         method : 'GET',
67304                         url : baseURL + '/Roo/Shop_course.php'
67305                     },
67306                     reader : {
67307                         xtype: 'JsonReader',
67308                         xns: Roo.data,
67309                         id : 'id',
67310                         fields : [
67311                             {
67312                                 'name': 'id',
67313                                 'type': 'int'
67314                             },
67315                             {
67316                                 'name': 'when_dt',
67317                                 'type': 'string'
67318                             },
67319                             {
67320                                 'name': 'end_dt',
67321                                 'type': 'string'
67322                             },
67323                             {
67324                                 'name': 'parent_id',
67325                                 'type': 'int'
67326                             },
67327                             {
67328                                 'name': 'product_id',
67329                                 'type': 'int'
67330                             },
67331                             {
67332                                 'name': 'productitem_id',
67333                                 'type': 'int'
67334                             },
67335                             {
67336                                 'name': 'guid',
67337                                 'type': 'int'
67338                             }
67339                         ]
67340                     }
67341                 },
67342                 toolbar : {
67343                     xtype: 'Toolbar',
67344                     xns: Roo,
67345                     items : [
67346                         {
67347                             xtype: 'Button',
67348                             xns: Roo.Toolbar,
67349                             listeners : {
67350                                 click : function (_self, e)
67351                                 {
67352                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67353                                     sd.setMonth(sd.getMonth()-1);
67354                                     _this.monthField.setValue(sd.format('Y-m-d'));
67355                                     _this.grid.ds.load({});
67356                                 }
67357                             },
67358                             text : "Back"
67359                         },
67360                         {
67361                             xtype: 'Separator',
67362                             xns: Roo.Toolbar
67363                         },
67364                         {
67365                             xtype: 'MonthField',
67366                             xns: Roo.form,
67367                             listeners : {
67368                                 render : function (_self)
67369                                 {
67370                                     _this.monthField = _self;
67371                                    // _this.monthField.set  today
67372                                 },
67373                                 select : function (combo, date)
67374                                 {
67375                                     _this.grid.ds.load({});
67376                                 }
67377                             },
67378                             value : (function() { return new Date(); })()
67379                         },
67380                         {
67381                             xtype: 'Separator',
67382                             xns: Roo.Toolbar
67383                         },
67384                         {
67385                             xtype: 'TextItem',
67386                             xns: Roo.Toolbar,
67387                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
67388                         },
67389                         {
67390                             xtype: 'Fill',
67391                             xns: Roo.Toolbar
67392                         },
67393                         {
67394                             xtype: 'Button',
67395                             xns: Roo.Toolbar,
67396                             listeners : {
67397                                 click : function (_self, e)
67398                                 {
67399                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67400                                     sd.setMonth(sd.getMonth()+1);
67401                                     _this.monthField.setValue(sd.format('Y-m-d'));
67402                                     _this.grid.ds.load({});
67403                                 }
67404                             },
67405                             text : "Next"
67406                         }
67407                     ]
67408                 },
67409                  
67410             }
67411         };
67412         
67413         *//*
67414  * Based on:
67415  * Ext JS Library 1.1.1
67416  * Copyright(c) 2006-2007, Ext JS, LLC.
67417  *
67418  * Originally Released Under LGPL - original licence link has changed is not relivant.
67419  *
67420  * Fork - LGPL
67421  * <script type="text/javascript">
67422  */
67423  
67424 /**
67425  * @class Roo.LoadMask
67426  * A simple utility class for generically masking elements while loading data.  If the element being masked has
67427  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
67428  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
67429  * element's UpdateManager load indicator and will be destroyed after the initial load.
67430  * @constructor
67431  * Create a new LoadMask
67432  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
67433  * @param {Object} config The config object
67434  */
67435 Roo.LoadMask = function(el, config){
67436     this.el = Roo.get(el);
67437     Roo.apply(this, config);
67438     if(this.store){
67439         this.store.on('beforeload', this.onBeforeLoad, this);
67440         this.store.on('load', this.onLoad, this);
67441         this.store.on('loadexception', this.onLoadException, this);
67442         this.removeMask = false;
67443     }else{
67444         var um = this.el.getUpdateManager();
67445         um.showLoadIndicator = false; // disable the default indicator
67446         um.on('beforeupdate', this.onBeforeLoad, this);
67447         um.on('update', this.onLoad, this);
67448         um.on('failure', this.onLoad, this);
67449         this.removeMask = true;
67450     }
67451 };
67452
67453 Roo.LoadMask.prototype = {
67454     /**
67455      * @cfg {Boolean} removeMask
67456      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
67457      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
67458      */
67459     removeMask : false,
67460     /**
67461      * @cfg {String} msg
67462      * The text to display in a centered loading message box (defaults to 'Loading...')
67463      */
67464     msg : 'Loading...',
67465     /**
67466      * @cfg {String} msgCls
67467      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
67468      */
67469     msgCls : 'x-mask-loading',
67470
67471     /**
67472      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
67473      * @type Boolean
67474      */
67475     disabled: false,
67476
67477     /**
67478      * Disables the mask to prevent it from being displayed
67479      */
67480     disable : function(){
67481        this.disabled = true;
67482     },
67483
67484     /**
67485      * Enables the mask so that it can be displayed
67486      */
67487     enable : function(){
67488         this.disabled = false;
67489     },
67490     
67491     onLoadException : function()
67492     {
67493         Roo.log(arguments);
67494         
67495         if (typeof(arguments[3]) != 'undefined') {
67496             Roo.MessageBox.alert("Error loading",arguments[3]);
67497         } 
67498         /*
67499         try {
67500             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
67501                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
67502             }   
67503         } catch(e) {
67504             
67505         }
67506         */
67507     
67508         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67509     },
67510     // private
67511     onLoad : function()
67512     {
67513         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
67514     },
67515
67516     // private
67517     onBeforeLoad : function(){
67518         if(!this.disabled){
67519             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
67520         }
67521     },
67522
67523     // private
67524     destroy : function(){
67525         if(this.store){
67526             this.store.un('beforeload', this.onBeforeLoad, this);
67527             this.store.un('load', this.onLoad, this);
67528             this.store.un('loadexception', this.onLoadException, this);
67529         }else{
67530             var um = this.el.getUpdateManager();
67531             um.un('beforeupdate', this.onBeforeLoad, this);
67532             um.un('update', this.onLoad, this);
67533             um.un('failure', this.onLoad, this);
67534         }
67535     }
67536 };/*
67537  * Based on:
67538  * Ext JS Library 1.1.1
67539  * Copyright(c) 2006-2007, Ext JS, LLC.
67540  *
67541  * Originally Released Under LGPL - original licence link has changed is not relivant.
67542  *
67543  * Fork - LGPL
67544  * <script type="text/javascript">
67545  */
67546
67547
67548 /**
67549  * @class Roo.XTemplate
67550  * @extends Roo.Template
67551  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
67552 <pre><code>
67553 var t = new Roo.XTemplate(
67554         '&lt;select name="{name}"&gt;',
67555                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
67556         '&lt;/select&gt;'
67557 );
67558  
67559 // then append, applying the master template values
67560  </code></pre>
67561  *
67562  * Supported features:
67563  *
67564  *  Tags:
67565
67566 <pre><code>
67567       {a_variable} - output encoded.
67568       {a_variable.format:("Y-m-d")} - call a method on the variable
67569       {a_variable:raw} - unencoded output
67570       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
67571       {a_variable:this.method_on_template(...)} - call a method on the template object.
67572  
67573 </code></pre>
67574  *  The tpl tag:
67575 <pre><code>
67576         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
67577         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
67578         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
67579         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
67580   
67581         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
67582         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
67583 </code></pre>
67584  *      
67585  */
67586 Roo.XTemplate = function()
67587 {
67588     Roo.XTemplate.superclass.constructor.apply(this, arguments);
67589     if (this.html) {
67590         this.compile();
67591     }
67592 };
67593
67594
67595 Roo.extend(Roo.XTemplate, Roo.Template, {
67596
67597     /**
67598      * The various sub templates
67599      */
67600     tpls : false,
67601     /**
67602      *
67603      * basic tag replacing syntax
67604      * WORD:WORD()
67605      *
67606      * // you can fake an object call by doing this
67607      *  x.t:(test,tesT) 
67608      * 
67609      */
67610     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
67611
67612     /**
67613      * compile the template
67614      *
67615      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
67616      *
67617      */
67618     compile: function()
67619     {
67620         var s = this.html;
67621      
67622         s = ['<tpl>', s, '</tpl>'].join('');
67623     
67624         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
67625             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
67626             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
67627             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
67628             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
67629             m,
67630             id     = 0,
67631             tpls   = [];
67632     
67633         while(true == !!(m = s.match(re))){
67634             var forMatch   = m[0].match(nameRe),
67635                 ifMatch   = m[0].match(ifRe),
67636                 execMatch   = m[0].match(execRe),
67637                 namedMatch   = m[0].match(namedRe),
67638                 
67639                 exp  = null, 
67640                 fn   = null,
67641                 exec = null,
67642                 name = forMatch && forMatch[1] ? forMatch[1] : '';
67643                 
67644             if (ifMatch) {
67645                 // if - puts fn into test..
67646                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
67647                 if(exp){
67648                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
67649                 }
67650             }
67651             
67652             if (execMatch) {
67653                 // exec - calls a function... returns empty if true is  returned.
67654                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
67655                 if(exp){
67656                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
67657                 }
67658             }
67659             
67660             
67661             if (name) {
67662                 // for = 
67663                 switch(name){
67664                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
67665                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
67666                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
67667                 }
67668             }
67669             var uid = namedMatch ? namedMatch[1] : id;
67670             
67671             
67672             tpls.push({
67673                 id:     namedMatch ? namedMatch[1] : id,
67674                 target: name,
67675                 exec:   exec,
67676                 test:   fn,
67677                 body:   m[1] || ''
67678             });
67679             if (namedMatch) {
67680                 s = s.replace(m[0], '');
67681             } else { 
67682                 s = s.replace(m[0], '{xtpl'+ id + '}');
67683             }
67684             ++id;
67685         }
67686         this.tpls = [];
67687         for(var i = tpls.length-1; i >= 0; --i){
67688             this.compileTpl(tpls[i]);
67689             this.tpls[tpls[i].id] = tpls[i];
67690         }
67691         this.master = tpls[tpls.length-1];
67692         return this;
67693     },
67694     /**
67695      * same as applyTemplate, except it's done to one of the subTemplates
67696      * when using named templates, you can do:
67697      *
67698      * var str = pl.applySubTemplate('your-name', values);
67699      *
67700      * 
67701      * @param {Number} id of the template
67702      * @param {Object} values to apply to template
67703      * @param {Object} parent (normaly the instance of this object)
67704      */
67705     applySubTemplate : function(id, values, parent)
67706     {
67707         
67708         
67709         var t = this.tpls[id];
67710         
67711         
67712         try { 
67713             if(t.test && !t.test.call(this, values, parent)){
67714                 return '';
67715             }
67716         } catch(e) {
67717             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
67718             Roo.log(e.toString());
67719             Roo.log(t.test);
67720             return ''
67721         }
67722         try { 
67723             
67724             if(t.exec && t.exec.call(this, values, parent)){
67725                 return '';
67726             }
67727         } catch(e) {
67728             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
67729             Roo.log(e.toString());
67730             Roo.log(t.exec);
67731             return ''
67732         }
67733         try {
67734             var vs = t.target ? t.target.call(this, values, parent) : values;
67735             parent = t.target ? values : parent;
67736             if(t.target && vs instanceof Array){
67737                 var buf = [];
67738                 for(var i = 0, len = vs.length; i < len; i++){
67739                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
67740                 }
67741                 return buf.join('');
67742             }
67743             return t.compiled.call(this, vs, parent);
67744         } catch (e) {
67745             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
67746             Roo.log(e.toString());
67747             Roo.log(t.compiled);
67748             return '';
67749         }
67750     },
67751
67752     compileTpl : function(tpl)
67753     {
67754         var fm = Roo.util.Format;
67755         var useF = this.disableFormats !== true;
67756         var sep = Roo.isGecko ? "+" : ",";
67757         var undef = function(str) {
67758             Roo.log("Property not found :"  + str);
67759             return '';
67760         };
67761         
67762         var fn = function(m, name, format, args)
67763         {
67764             //Roo.log(arguments);
67765             args = args ? args.replace(/\\'/g,"'") : args;
67766             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
67767             if (typeof(format) == 'undefined') {
67768                 format= 'htmlEncode';
67769             }
67770             if (format == 'raw' ) {
67771                 format = false;
67772             }
67773             
67774             if(name.substr(0, 4) == 'xtpl'){
67775                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
67776             }
67777             
67778             // build an array of options to determine if value is undefined..
67779             
67780             // basically get 'xxxx.yyyy' then do
67781             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
67782             //    (function () { Roo.log("Property not found"); return ''; })() :
67783             //    ......
67784             
67785             var udef_ar = [];
67786             var lookfor = '';
67787             Roo.each(name.split('.'), function(st) {
67788                 lookfor += (lookfor.length ? '.': '') + st;
67789                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
67790             });
67791             
67792             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
67793             
67794             
67795             if(format && useF){
67796                 
67797                 args = args ? ',' + args : "";
67798                  
67799                 if(format.substr(0, 5) != "this."){
67800                     format = "fm." + format + '(';
67801                 }else{
67802                     format = 'this.call("'+ format.substr(5) + '", ';
67803                     args = ", values";
67804                 }
67805                 
67806                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
67807             }
67808              
67809             if (args.length) {
67810                 // called with xxyx.yuu:(test,test)
67811                 // change to ()
67812                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
67813             }
67814             // raw.. - :raw modifier..
67815             return "'"+ sep + udef_st  + name + ")"+sep+"'";
67816             
67817         };
67818         var body;
67819         // branched to use + in gecko and [].join() in others
67820         if(Roo.isGecko){
67821             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
67822                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
67823                     "';};};";
67824         }else{
67825             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
67826             body.push(tpl.body.replace(/(\r\n|\n)/g,
67827                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
67828             body.push("'].join('');};};");
67829             body = body.join('');
67830         }
67831         
67832         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
67833        
67834         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
67835         eval(body);
67836         
67837         return this;
67838     },
67839
67840     applyTemplate : function(values){
67841         return this.master.compiled.call(this, values, {});
67842         //var s = this.subs;
67843     },
67844
67845     apply : function(){
67846         return this.applyTemplate.apply(this, arguments);
67847     }
67848
67849  });
67850
67851 Roo.XTemplate.from = function(el){
67852     el = Roo.getDom(el);
67853     return new Roo.XTemplate(el.value || el.innerHTML);
67854 };