roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal (non-delayed, will get a return value..)
509         callback : function(cb, scope, args, delay)
510                 {
511             if(typeof cb != "function"){
512                                 return false;
513                         }
514                         if(delay){
515                                 cb.defer(delay, scope, args || []);
516                                 return false
517             }
518                         return cb.apply(scope, args || []);
519
520         },
521
522         /**
523          * Return the dom node for the passed string (id), dom node, or Roo.Element
524          * @param {String/HTMLElement/Roo.Element} el
525          * @return HTMLElement
526          */
527         getDom : function(el){
528             if(!el){
529                 return null;
530             }
531             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
532         },
533
534         /**
535         * Shorthand for {@link Roo.ComponentMgr#get}
536         * @param {String} id
537         * @return Roo.Component
538         */
539         getCmp : function(id){
540             return Roo.ComponentMgr.get(id);
541         },
542          
543         num : function(v, defaultValue){
544             if(typeof v != 'number'){
545                 return defaultValue;
546             }
547             return v;
548         },
549
550         destroy : function(){
551             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
552                 var as = a[i];
553                 if(as){
554                     if(as.dom){
555                         as.removeAllListeners();
556                         as.remove();
557                         continue;
558                     }
559                     if(typeof as.purgeListeners == 'function'){
560                         as.purgeListeners();
561                     }
562                     if(typeof as.destroy == 'function'){
563                         as.destroy();
564                     }
565                 }
566             }
567         },
568
569         // inpired by a similar function in mootools library
570         /**
571          * Returns the type of object that is passed in. If the object passed in is null or undefined it
572          * return false otherwise it returns one of the following values:<ul>
573          * <li><b>string</b>: If the object passed is a string</li>
574          * <li><b>number</b>: If the object passed is a number</li>
575          * <li><b>boolean</b>: If the object passed is a boolean value</li>
576          * <li><b>function</b>: If the object passed is a function reference</li>
577          * <li><b>object</b>: If the object passed is an object</li>
578          * <li><b>array</b>: If the object passed is an array</li>
579          * <li><b>regexp</b>: If the object passed is a regular expression</li>
580          * <li><b>element</b>: If the object passed is a DOM Element</li>
581          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
582          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
583          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
584          * @param {Mixed} object
585          * @return {String}
586          */
587         type : function(o){
588             if(o === undefined || o === null){
589                 return false;
590             }
591             if(o.htmlElement){
592                 return 'element';
593             }
594             var t = typeof o;
595             if(t == 'object' && o.nodeName) {
596                 switch(o.nodeType) {
597                     case 1: return 'element';
598                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
599                 }
600             }
601             if(t == 'object' || t == 'function') {
602                 switch(o.constructor) {
603                     case Array: return 'array';
604                     case RegExp: return 'regexp';
605                 }
606                 if(typeof o.length == 'number' && typeof o.item == 'function') {
607                     return 'nodelist';
608                 }
609             }
610             return t;
611         },
612
613         /**
614          * Returns true if the passed value is null, undefined or an empty string (optional).
615          * @param {Mixed} value The value to test
616          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
617          * @return {Boolean}
618          */
619         isEmpty : function(v, allowBlank){
620             return v === null || v === undefined || (!allowBlank ? v === '' : false);
621         },
622         
623         /** @type Boolean */
624         isOpera : isOpera,
625         /** @type Boolean */
626         isSafari : isSafari,
627         /** @type Boolean */
628         isFirefox : isFirefox,
629         /** @type Boolean */
630         isIE : isIE,
631         /** @type Boolean */
632         isIE7 : isIE7,
633         /** @type Boolean */
634         isIE11 : isIE11,
635         /** @type Boolean */
636         isEdge : isEdge,
637         /** @type Boolean */
638         isGecko : isGecko,
639         /** @type Boolean */
640         isBorderBox : isBorderBox,
641         /** @type Boolean */
642         isWindows : isWindows,
643         /** @type Boolean */
644         isLinux : isLinux,
645         /** @type Boolean */
646         isMac : isMac,
647         /** @type Boolean */
648         isIOS : isIOS,
649         /** @type Boolean */
650         isAndroid : isAndroid,
651         /** @type Boolean */
652         isTouch : isTouch,
653
654         /**
655          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
656          * you may want to set this to true.
657          * @type Boolean
658          */
659         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
660         
661         
662                 
663         /**
664          * Selects a single element as a Roo Element
665          * This is about as close as you can get to jQuery's $('do crazy stuff')
666          * @param {String} selector The selector/xpath query
667          * @param {Node} root (optional) The start of the query (defaults to document).
668          * @return {Roo.Element}
669          */
670         selectNode : function(selector, root) 
671         {
672             var node = Roo.DomQuery.selectNode(selector,root);
673             return node ? Roo.get(node) : new Roo.Element(false);
674         },
675                 /**
676                  * Find the current bootstrap width Grid size
677                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
678                  * @returns {String} (xs|sm|md|lg|xl)
679                  */
680                 
681                 getGridSize : function()
682                 {
683                         var w = Roo.lib.Dom.getViewWidth();
684                         switch(true) {
685                                 case w > 1200:
686                                         return 'xl';
687                                 case w > 992:
688                                         return 'lg';
689                                 case w > 768:
690                                         return 'md';
691                                 case w > 576:
692                                         return 'sm';
693                                 default:
694                                         return 'xs'
695                         }
696                         
697                 } 
698         
699     });
700
701
702 })();
703
704 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
705                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
706                 "Roo.app", "Roo.ux" 
707                );
708 /*
709  * Based on:
710  * Ext JS Library 1.1.1
711  * Copyright(c) 2006-2007, Ext JS, LLC.
712  *
713  * Originally Released Under LGPL - original licence link has changed is not relivant.
714  *
715  * Fork - LGPL
716  * <script type="text/javascript">
717  */
718
719 (function() {    
720     // wrappedn so fnCleanup is not in global scope...
721     if(Roo.isIE) {
722         function fnCleanUp() {
723             var p = Function.prototype;
724             delete p.createSequence;
725             delete p.defer;
726             delete p.createDelegate;
727             delete p.createCallback;
728             delete p.createInterceptor;
729
730             window.detachEvent("onunload", fnCleanUp);
731         }
732         window.attachEvent("onunload", fnCleanUp);
733     }
734 })();
735
736
737 /**
738  * @class Function
739  * These functions are available on every Function object (any JavaScript function).
740  */
741 Roo.apply(Function.prototype, {
742      /**
743      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
744      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
745      * Will create a function that is bound to those 2 args.
746      * @return {Function} The new function
747     */
748     createCallback : function(/*args...*/){
749         // make args available, in function below
750         var args = arguments;
751         var method = this;
752         return function() {
753             return method.apply(window, args);
754         };
755     },
756
757     /**
758      * Creates a delegate (callback) that sets the scope to obj.
759      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
760      * Will create a function that is automatically scoped to this.
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Function} The new function
766      */
767     createDelegate : function(obj, args, appendArgs){
768         var method = this;
769         return function() {
770             var callArgs = args || arguments;
771             if(appendArgs === true){
772                 callArgs = Array.prototype.slice.call(arguments, 0);
773                 callArgs = callArgs.concat(args);
774             }else if(typeof appendArgs == "number"){
775                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
776                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
777                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
778             }
779             return method.apply(obj || window, callArgs);
780         };
781     },
782
783     /**
784      * Calls this function after the number of millseconds specified.
785      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
786      * @param {Object} obj (optional) The object for which the scope is set
787      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
788      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
789      *                                             if a number the args are inserted at the specified position
790      * @return {Number} The timeout id that can be used with clearTimeout
791      */
792     defer : function(millis, obj, args, appendArgs){
793         var fn = this.createDelegate(obj, args, appendArgs);
794         if(millis){
795             return setTimeout(fn, millis);
796         }
797         fn();
798         return 0;
799     },
800     /**
801      * Create a combined function call sequence of the original function + the passed function.
802      * The resulting function returns the results of the original function.
803      * The passed fcn is called with the parameters of the original function
804      * @param {Function} fcn The function to sequence
805      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
806      * @return {Function} The new function
807      */
808     createSequence : function(fcn, scope){
809         if(typeof fcn != "function"){
810             return this;
811         }
812         var method = this;
813         return function() {
814             var retval = method.apply(this || window, arguments);
815             fcn.apply(scope || this || window, arguments);
816             return retval;
817         };
818     },
819
820     /**
821      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
822      * The resulting function returns the results of the original function.
823      * The passed fcn is called with the parameters of the original function.
824      * @addon
825      * @param {Function} fcn The function to call before the original
826      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
827      * @return {Function} The new function
828      */
829     createInterceptor : function(fcn, scope){
830         if(typeof fcn != "function"){
831             return this;
832         }
833         var method = this;
834         return function() {
835             fcn.target = this;
836             fcn.method = method;
837             if(fcn.apply(scope || this || window, arguments) === false){
838                 return;
839             }
840             return method.apply(this || window, arguments);
841         };
842     }
843 });
844 /*
845  * Based on:
846  * Ext JS Library 1.1.1
847  * Copyright(c) 2006-2007, Ext JS, LLC.
848  *
849  * Originally Released Under LGPL - original licence link has changed is not relivant.
850  *
851  * Fork - LGPL
852  * <script type="text/javascript">
853  */
854
855 Roo.applyIf(String, {
856     
857     /** @scope String */
858     
859     /**
860      * Escapes the passed string for ' and \
861      * @param {String} string The string to escape
862      * @return {String} The escaped string
863      * @static
864      */
865     escape : function(string) {
866         return string.replace(/('|\\)/g, "\\$1");
867     },
868
869     /**
870      * Pads the left side of a string with a specified character.  This is especially useful
871      * for normalizing number and date strings.  Example usage:
872      * <pre><code>
873 var s = String.leftPad('123', 5, '0');
874 // s now contains the string: '00123'
875 </code></pre>
876      * @param {String} string The original string
877      * @param {Number} size The total length of the output string
878      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
879      * @return {String} The padded string
880      * @static
881      */
882     leftPad : function (val, size, ch) {
883         var result = new String(val);
884         if(ch === null || ch === undefined || ch === '') {
885             ch = " ";
886         }
887         while (result.length < size) {
888             result = ch + result;
889         }
890         return result;
891     },
892
893     /**
894      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
895      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
896      * <pre><code>
897 var cls = 'my-class', text = 'Some text';
898 var s = String.format('<div class="{0}">{1}</div>', cls, text);
899 // s now contains the string: '<div class="my-class">Some text</div>'
900 </code></pre>
901      * @param {String} string The tokenized string to be formatted
902      * @param {String} value1 The value to replace token {0}
903      * @param {String} value2 Etc...
904      * @return {String} The formatted string
905      * @static
906      */
907     format : function(format){
908         var args = Array.prototype.slice.call(arguments, 1);
909         return format.replace(/\{(\d+)\}/g, function(m, i){
910             return Roo.util.Format.htmlEncode(args[i]);
911         });
912     }
913   
914     
915 });
916
917 /**
918  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
919  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
920  * they are already different, the first value passed in is returned.  Note that this method returns the new value
921  * but does not change the current string.
922  * <pre><code>
923 // alternate sort directions
924 sort = sort.toggle('ASC', 'DESC');
925
926 // instead of conditional logic:
927 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
928 </code></pre>
929  * @param {String} value The value to compare to the current string
930  * @param {String} other The new value to use if the string already equals the first value passed in
931  * @return {String} The new value
932  */
933  
934 String.prototype.toggle = function(value, other){
935     return this == value ? other : value;
936 };
937
938
939 /**
940   * Remove invalid unicode characters from a string 
941   *
942   * @return {String} The clean string
943   */
944 String.prototype.unicodeClean = function () {
945     return this.replace(/[\s\S]/g,
946         function(character) {
947             if (character.charCodeAt()< 256) {
948               return character;
949            }
950            try {
951                 encodeURIComponent(character);
952            } catch(e) { 
953               return '';
954            }
955            return character;
956         }
957     );
958 };
959   
960
961 /**
962   * Make the first letter of a string uppercase
963   *
964   * @return {String} The new string.
965   */
966 String.prototype.toUpperCaseFirst = function () {
967     return this.charAt(0).toUpperCase() + this.slice(1);
968 };  
969   
970 /*
971  * Based on:
972  * Ext JS Library 1.1.1
973  * Copyright(c) 2006-2007, Ext JS, LLC.
974  *
975  * Originally Released Under LGPL - original licence link has changed is not relivant.
976  *
977  * Fork - LGPL
978  * <script type="text/javascript">
979  */
980
981  /**
982  * @class Number
983  */
984 Roo.applyIf(Number.prototype, {
985     /**
986      * Checks whether or not the current number is within a desired range.  If the number is already within the
987      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
988      * exceeded.  Note that this method returns the constrained value but does not change the current number.
989      * @param {Number} min The minimum number in the range
990      * @param {Number} max The maximum number in the range
991      * @return {Number} The constrained value if outside the range, otherwise the current value
992      */
993     constrain : function(min, max){
994         return Math.min(Math.max(this, min), max);
995     }
996 });/*
997  * Based on:
998  * Ext JS Library 1.1.1
999  * Copyright(c) 2006-2007, Ext JS, LLC.
1000  *
1001  * Originally Released Under LGPL - original licence link has changed is not relivant.
1002  *
1003  * Fork - LGPL
1004  * <script type="text/javascript">
1005  */
1006  /**
1007  * @class Array
1008  */
1009 Roo.applyIf(Array.prototype, {
1010     /**
1011      * 
1012      * Checks whether or not the specified object exists in the array.
1013      * @param {Object} o The object to check for
1014      * @return {Number} The index of o in the array (or -1 if it is not found)
1015      */
1016     indexOf : function(o){
1017        for (var i = 0, len = this.length; i < len; i++){
1018               if(this[i] == o) { return i; }
1019        }
1020            return -1;
1021     },
1022
1023     /**
1024      * Removes the specified object from the array.  If the object is not found nothing happens.
1025      * @param {Object} o The object to remove
1026      */
1027     remove : function(o){
1028        var index = this.indexOf(o);
1029        if(index != -1){
1030            this.splice(index, 1);
1031        }
1032     },
1033     /**
1034      * Map (JS 1.6 compatibility)
1035      * @param {Function} function  to call
1036      */
1037     map : function(fun )
1038     {
1039         var len = this.length >>> 0;
1040         if (typeof fun != "function") {
1041             throw new TypeError();
1042         }
1043         var res = new Array(len);
1044         var thisp = arguments[1];
1045         for (var i = 0; i < len; i++)
1046         {
1047             if (i in this) {
1048                 res[i] = fun.call(thisp, this[i], i, this);
1049             }
1050         }
1051
1052         return res;
1053     },
1054     /**
1055      * equals
1056      * @param {Array} o The array to compare to
1057      * @returns {Boolean} true if the same
1058      */
1059     equals : function(b)
1060     {
1061             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1062         if (this === b) {
1063             return true;
1064         }
1065         if (b == null) {
1066             return false;
1067         }
1068         if (this.length !== b.length) {
1069             return false;
1070         }
1071           
1072         // sort?? a.sort().equals(b.sort());
1073           
1074         for (var i = 0; i < this.length; ++i) {
1075             if (this[i] !== b[i]) {
1076             return false;
1077             }
1078         }
1079         return true;
1080     } 
1081     
1082     
1083     
1084     
1085 });
1086
1087 Roo.applyIf(Array, {
1088  /**
1089      * from
1090      * @static
1091      * @param {Array} o Or Array like object (eg. nodelist)
1092      * @returns {Array} 
1093      */
1094     from : function(o)
1095     {
1096         var ret= [];
1097     
1098         for (var i =0; i < o.length; i++) { 
1099             ret[i] = o[i];
1100         }
1101         return ret;
1102       
1103     }
1104 });
1105 /*
1106  * Based on:
1107  * Ext JS Library 1.1.1
1108  * Copyright(c) 2006-2007, Ext JS, LLC.
1109  *
1110  * Originally Released Under LGPL - original licence link has changed is not relivant.
1111  *
1112  * Fork - LGPL
1113  * <script type="text/javascript">
1114  */
1115
1116 /**
1117  * @class Date
1118  *
1119  * The date parsing and format syntax is a subset of
1120  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1121  * supported will provide results equivalent to their PHP versions.
1122  *
1123  * Following is the list of all currently supported formats:
1124  *<pre>
1125 Sample date:
1126 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1127
1128 Format  Output      Description
1129 ------  ----------  --------------------------------------------------------------
1130   d      10         Day of the month, 2 digits with leading zeros
1131   D      Wed        A textual representation of a day, three letters
1132   j      10         Day of the month without leading zeros
1133   l      Wednesday  A full textual representation of the day of the week
1134   S      th         English ordinal day of month suffix, 2 chars (use with j)
1135   w      3          Numeric representation of the day of the week
1136   z      9          The julian date, or day of the year (0-365)
1137   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1138   F      January    A full textual representation of the month
1139   m      01         Numeric representation of a month, with leading zeros
1140   M      Jan        Month name abbreviation, three letters
1141   n      1          Numeric representation of a month, without leading zeros
1142   t      31         Number of days in the given month
1143   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1144   Y      2007       A full numeric representation of a year, 4 digits
1145   y      07         A two digit representation of a year
1146   a      pm         Lowercase Ante meridiem and Post meridiem
1147   A      PM         Uppercase Ante meridiem and Post meridiem
1148   g      3          12-hour format of an hour without leading zeros
1149   G      15         24-hour format of an hour without leading zeros
1150   h      03         12-hour format of an hour with leading zeros
1151   H      15         24-hour format of an hour with leading zeros
1152   i      05         Minutes with leading zeros
1153   s      01         Seconds, with leading zeros
1154   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1155   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1156   T      CST        Timezone setting of the machine running the code
1157   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1158 </pre>
1159  *
1160  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1161  * <pre><code>
1162 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1163 document.write(dt.format('Y-m-d'));                         //2007-01-10
1164 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1165 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
1166  </code></pre>
1167  *
1168  * Here are some standard date/time patterns that you might find helpful.  They
1169  * are not part of the source of Date.js, but to use them you can simply copy this
1170  * block of code into any script that is included after Date.js and they will also become
1171  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1172  * <pre><code>
1173 Date.patterns = {
1174     ISO8601Long:"Y-m-d H:i:s",
1175     ISO8601Short:"Y-m-d",
1176     ShortDate: "n/j/Y",
1177     LongDate: "l, F d, Y",
1178     FullDateTime: "l, F d, Y g:i:s A",
1179     MonthDay: "F d",
1180     ShortTime: "g:i A",
1181     LongTime: "g:i:s A",
1182     SortableDateTime: "Y-m-d\\TH:i:s",
1183     UniversalSortableDateTime: "Y-m-d H:i:sO",
1184     YearMonth: "F, Y"
1185 };
1186 </code></pre>
1187  *
1188  * Example usage:
1189  * <pre><code>
1190 var dt = new Date();
1191 document.write(dt.format(Date.patterns.ShortDate));
1192  </code></pre>
1193  */
1194
1195 /*
1196  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1197  * They generate precompiled functions from date formats instead of parsing and
1198  * processing the pattern every time you format a date.  These functions are available
1199  * on every Date object (any javascript function).
1200  *
1201  * The original article and download are here:
1202  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1203  *
1204  */
1205  
1206  
1207  // was in core
1208 /**
1209  Returns the number of milliseconds between this date and date
1210  @param {Date} date (optional) Defaults to now
1211  @param {String} interval (optional) Default Date.MILLI, A valid date interval enum value (eg. Date.DAY) 
1212  @return {Number} The diff in milliseconds or units of interval
1213  @member Date getElapsed
1214  */
1215 Date.prototype.getElapsed = function(date, interval)
1216 {
1217     date = date ||  new Date();
1218     var ret = Math.abs(date.getTime()-this.getTime());
1219     switch (interval) {
1220        
1221         case  Date.SECOND:
1222             return Math.floor(ret / (1000));
1223         case  Date.MINUTE:
1224             return Math.floor(ret / (1000*60));
1225         case  Date.HOUR:
1226             return Math.floor(ret / (1000*60*60));
1227         case  Date.DAY:
1228             return Math.floor(ret / (1000*60*60*24));
1229         case  Date.MONTH: // this does not give exact number...??
1230             return ((date.format("Y") - this.format("Y")) * 12) + (date.format("m") - this.format("m"));
1231         case  Date.YEAR: // this does not give exact number...??
1232             return (date.format("Y") - this.format("Y"));
1233        
1234         case  Date.MILLI:
1235         default:
1236             return ret;
1237     }
1238 };
1239  
1240 // was in date file..
1241
1242
1243 // private
1244 Date.parseFunctions = {count:0};
1245 // private
1246 Date.parseRegexes = [];
1247 // private
1248 Date.formatFunctions = {count:0};
1249
1250 // private
1251 Date.prototype.dateFormat = function(format) {
1252     if (Date.formatFunctions[format] == null) {
1253         Date.createNewFormat(format);
1254     }
1255     var func = Date.formatFunctions[format];
1256     return this[func]();
1257 };
1258
1259
1260 /**
1261  * Formats a date given the supplied format string
1262  * @param {String} format The format string
1263  * @return {String} The formatted date
1264  * @method
1265  */
1266 Date.prototype.format = Date.prototype.dateFormat;
1267
1268 // private
1269 Date.createNewFormat = function(format) {
1270     var funcName = "format" + Date.formatFunctions.count++;
1271     Date.formatFunctions[format] = funcName;
1272     var code = "Date.prototype." + funcName + " = function(){return ";
1273     var special = false;
1274     var ch = '';
1275     for (var i = 0; i < format.length; ++i) {
1276         ch = format.charAt(i);
1277         if (!special && ch == "\\") {
1278             special = true;
1279         }
1280         else if (special) {
1281             special = false;
1282             code += "'" + String.escape(ch) + "' + ";
1283         }
1284         else {
1285             code += Date.getFormatCode(ch);
1286         }
1287     }
1288     /** eval:var:zzzzzzzzzzzzz */
1289     eval(code.substring(0, code.length - 3) + ";}");
1290 };
1291
1292 // private
1293 Date.getFormatCode = function(character) {
1294     switch (character) {
1295     case "d":
1296         return "String.leftPad(this.getDate(), 2, '0') + ";
1297     case "D":
1298         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1299     case "j":
1300         return "this.getDate() + ";
1301     case "l":
1302         return "Date.dayNames[this.getDay()] + ";
1303     case "S":
1304         return "this.getSuffix() + ";
1305     case "w":
1306         return "this.getDay() + ";
1307     case "z":
1308         return "this.getDayOfYear() + ";
1309     case "W":
1310         return "this.getWeekOfYear() + ";
1311     case "F":
1312         return "Date.monthNames[this.getMonth()] + ";
1313     case "m":
1314         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1315     case "M":
1316         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1317     case "n":
1318         return "(this.getMonth() + 1) + ";
1319     case "t":
1320         return "this.getDaysInMonth() + ";
1321     case "L":
1322         return "(this.isLeapYear() ? 1 : 0) + ";
1323     case "Y":
1324         return "this.getFullYear() + ";
1325     case "y":
1326         return "('' + this.getFullYear()).substring(2, 4) + ";
1327     case "a":
1328         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1329     case "A":
1330         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1331     case "g":
1332         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1333     case "G":
1334         return "this.getHours() + ";
1335     case "h":
1336         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1337     case "H":
1338         return "String.leftPad(this.getHours(), 2, '0') + ";
1339     case "i":
1340         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1341     case "s":
1342         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1343     case "O":
1344         return "this.getGMTOffset() + ";
1345     case "P":
1346         return "this.getGMTColonOffset() + ";
1347     case "T":
1348         return "this.getTimezone() + ";
1349     case "Z":
1350         return "(this.getTimezoneOffset() * -60) + ";
1351     default:
1352         return "'" + String.escape(character) + "' + ";
1353     }
1354 };
1355
1356 /**
1357  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1358  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1359  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1360  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1361  * string or the parse operation will fail.
1362  * Example Usage:
1363 <pre><code>
1364 //dt = Fri May 25 2007 (current date)
1365 var dt = new Date();
1366
1367 //dt = Thu May 25 2006 (today's month/day in 2006)
1368 dt = Date.parseDate("2006", "Y");
1369
1370 //dt = Sun Jan 15 2006 (all date parts specified)
1371 dt = Date.parseDate("2006-1-15", "Y-m-d");
1372
1373 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1374 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1375 </code></pre>
1376  * @param {String} input The unparsed date as a string
1377  * @param {String} format The format the date is in
1378  * @return {Date} The parsed date
1379  * @static
1380  */
1381 Date.parseDate = function(input, format) {
1382     if (Date.parseFunctions[format] == null) {
1383         Date.createParser(format);
1384     }
1385     var func = Date.parseFunctions[format];
1386     return Date[func](input);
1387 };
1388 /**
1389  * @private
1390  */
1391
1392 Date.createParser = function(format) {
1393     var funcName = "parse" + Date.parseFunctions.count++;
1394     var regexNum = Date.parseRegexes.length;
1395     var currentGroup = 1;
1396     Date.parseFunctions[format] = funcName;
1397
1398     var code = "Date." + funcName + " = function(input){\n"
1399         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1400         + "var d = new Date();\n"
1401         + "y = d.getFullYear();\n"
1402         + "m = d.getMonth();\n"
1403         + "d = d.getDate();\n"
1404         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1405         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1406         + "if (results && results.length > 0) {";
1407     var regex = "";
1408
1409     var special = false;
1410     var ch = '';
1411     for (var i = 0; i < format.length; ++i) {
1412         ch = format.charAt(i);
1413         if (!special && ch == "\\") {
1414             special = true;
1415         }
1416         else if (special) {
1417             special = false;
1418             regex += String.escape(ch);
1419         }
1420         else {
1421             var obj = Date.formatCodeToRegex(ch, currentGroup);
1422             currentGroup += obj.g;
1423             regex += obj.s;
1424             if (obj.g && obj.c) {
1425                 code += obj.c;
1426             }
1427         }
1428     }
1429
1430     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1431         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1432         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1433         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1434         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1435         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1436         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1437         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1438         + "else if (y >= 0 && m >= 0)\n"
1439         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1440         + "else if (y >= 0)\n"
1441         + "{v = new Date(y); v.setFullYear(y);}\n"
1442         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1443         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1444         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1445         + ";}";
1446
1447     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1448     /** eval:var:zzzzzzzzzzzzz */
1449     eval(code);
1450 };
1451
1452 // private
1453 Date.formatCodeToRegex = function(character, currentGroup) {
1454     switch (character) {
1455     case "D":
1456         return {g:0,
1457         c:null,
1458         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1459     case "j":
1460         return {g:1,
1461             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1462             s:"(\\d{1,2})"}; // day of month without leading zeroes
1463     case "d":
1464         return {g:1,
1465             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1466             s:"(\\d{2})"}; // day of month with leading zeroes
1467     case "l":
1468         return {g:0,
1469             c:null,
1470             s:"(?:" + Date.dayNames.join("|") + ")"};
1471     case "S":
1472         return {g:0,
1473             c:null,
1474             s:"(?:st|nd|rd|th)"};
1475     case "w":
1476         return {g:0,
1477             c:null,
1478             s:"\\d"};
1479     case "z":
1480         return {g:0,
1481             c:null,
1482             s:"(?:\\d{1,3})"};
1483     case "W":
1484         return {g:0,
1485             c:null,
1486             s:"(?:\\d{2})"};
1487     case "F":
1488         return {g:1,
1489             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1490             s:"(" + Date.monthNames.join("|") + ")"};
1491     case "M":
1492         return {g:1,
1493             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1494             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1495     case "n":
1496         return {g:1,
1497             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1498             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1499     case "m":
1500         return {g:1,
1501             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1502             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1503     case "t":
1504         return {g:0,
1505             c:null,
1506             s:"\\d{1,2}"};
1507     case "L":
1508         return {g:0,
1509             c:null,
1510             s:"(?:1|0)"};
1511     case "Y":
1512         return {g:1,
1513             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1514             s:"(\\d{4})"};
1515     case "y":
1516         return {g:1,
1517             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1518                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1519             s:"(\\d{1,2})"};
1520     case "a":
1521         return {g:1,
1522             c:"if (results[" + currentGroup + "] == 'am') {\n"
1523                 + "if (h == 12) { h = 0; }\n"
1524                 + "} else { if (h < 12) { h += 12; }}",
1525             s:"(am|pm)"};
1526     case "A":
1527         return {g:1,
1528             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1529                 + "if (h == 12) { h = 0; }\n"
1530                 + "} else { if (h < 12) { h += 12; }}",
1531             s:"(AM|PM)"};
1532     case "g":
1533     case "G":
1534         return {g:1,
1535             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1536             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1537     case "h":
1538     case "H":
1539         return {g:1,
1540             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1541             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1542     case "i":
1543         return {g:1,
1544             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1545             s:"(\\d{2})"};
1546     case "s":
1547         return {g:1,
1548             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1549             s:"(\\d{2})"};
1550     case "O":
1551         return {g:1,
1552             c:[
1553                 "o = results[", currentGroup, "];\n",
1554                 "var sn = o.substring(0,1);\n", // get + / - sign
1555                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1556                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1557                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1558                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1559             ].join(""),
1560             s:"([+\-]\\d{2,4})"};
1561     
1562     
1563     case "P":
1564         return {g:1,
1565                 c:[
1566                    "o = results[", currentGroup, "];\n",
1567                    "var sn = o.substring(0,1);\n",
1568                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1569                    "var mn = o.substring(4,6) % 60;\n",
1570                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1571                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1572             ].join(""),
1573             s:"([+\-]\\d{4})"};
1574     case "T":
1575         return {g:0,
1576             c:null,
1577             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1578     case "Z":
1579         return {g:1,
1580             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1581                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1582             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1583     default:
1584         return {g:0,
1585             c:null,
1586             s:String.escape(character)};
1587     }
1588 };
1589
1590 /**
1591  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1592  * @return {String} The abbreviated timezone name (e.g. 'CST')
1593  */
1594 Date.prototype.getTimezone = function() {
1595     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1596 };
1597
1598 /**
1599  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1600  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1601  */
1602 Date.prototype.getGMTOffset = function() {
1603     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1604         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1605         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1606 };
1607
1608 /**
1609  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1610  * @return {String} 2-characters representing hours and 2-characters representing minutes
1611  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1612  */
1613 Date.prototype.getGMTColonOffset = function() {
1614         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1615                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1616                 + ":"
1617                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1618 }
1619
1620 /**
1621  * Get the numeric day number of the year, adjusted for leap year.
1622  * @return {Number} 0 through 364 (365 in leap years)
1623  */
1624 Date.prototype.getDayOfYear = function() {
1625     var num = 0;
1626     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1627     for (var i = 0; i < this.getMonth(); ++i) {
1628         num += Date.daysInMonth[i];
1629     }
1630     return num + this.getDate() - 1;
1631 };
1632
1633 /**
1634  * Get the string representation of the numeric week number of the year
1635  * (equivalent to the format specifier 'W').
1636  * @return {String} '00' through '52'
1637  */
1638 Date.prototype.getWeekOfYear = function() {
1639     // Skip to Thursday of this week
1640     var now = this.getDayOfYear() + (4 - this.getDay());
1641     // Find the first Thursday of the year
1642     var jan1 = new Date(this.getFullYear(), 0, 1);
1643     var then = (7 - jan1.getDay() + 4);
1644     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1645 };
1646
1647 /**
1648  * Whether or not the current date is in a leap year.
1649  * @return {Boolean} True if the current date is in a leap year, else false
1650  */
1651 Date.prototype.isLeapYear = function() {
1652     var year = this.getFullYear();
1653     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1654 };
1655
1656 /**
1657  * Get the first day of the current month, adjusted for leap year.  The returned value
1658  * is the numeric day index within the week (0-6) which can be used in conjunction with
1659  * the {@link #monthNames} array to retrieve the textual day name.
1660  * Example:
1661  *<pre><code>
1662 var dt = new Date('1/10/2007');
1663 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1664 </code></pre>
1665  * @return {Number} The day number (0-6)
1666  */
1667 Date.prototype.getFirstDayOfMonth = function() {
1668     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1669     return (day < 0) ? (day + 7) : day;
1670 };
1671
1672 /**
1673  * Get the last day of the current month, adjusted for leap year.  The returned value
1674  * is the numeric day index within the week (0-6) which can be used in conjunction with
1675  * the {@link #monthNames} array to retrieve the textual day name.
1676  * Example:
1677  *<pre><code>
1678 var dt = new Date('1/10/2007');
1679 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1680 </code></pre>
1681  * @return {Number} The day number (0-6)
1682  */
1683 Date.prototype.getLastDayOfMonth = function() {
1684     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1685     return (day < 0) ? (day + 7) : day;
1686 };
1687
1688
1689 /**
1690  * Get the first date of this date's month
1691  * @return {Date}
1692  */
1693 Date.prototype.getFirstDateOfMonth = function() {
1694     return new Date(this.getFullYear(), this.getMonth(), 1);
1695 };
1696
1697 /**
1698  * Get the last date of this date's month
1699  * @return {Date}
1700  */
1701 Date.prototype.getLastDateOfMonth = function() {
1702     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1703 };
1704 /**
1705  * Get the number of days in the current month, adjusted for leap year.
1706  * @return {Number} The number of days in the month
1707  */
1708 Date.prototype.getDaysInMonth = function() {
1709     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1710     return Date.daysInMonth[this.getMonth()];
1711 };
1712
1713 /**
1714  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1715  * @return {String} 'st, 'nd', 'rd' or 'th'
1716  */
1717 Date.prototype.getSuffix = function() {
1718     switch (this.getDate()) {
1719         case 1:
1720         case 21:
1721         case 31:
1722             return "st";
1723         case 2:
1724         case 22:
1725             return "nd";
1726         case 3:
1727         case 23:
1728             return "rd";
1729         default:
1730             return "th";
1731     }
1732 };
1733
1734 // private
1735 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1736
1737 /**
1738  * An array of textual month names.
1739  * Override these values for international dates, for example...
1740  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1741  * @type Array
1742  * @static
1743  */
1744 Date.monthNames =
1745    ["January",
1746     "February",
1747     "March",
1748     "April",
1749     "May",
1750     "June",
1751     "July",
1752     "August",
1753     "September",
1754     "October",
1755     "November",
1756     "December"];
1757
1758 /**
1759  * An array of textual day names.
1760  * Override these values for international dates, for example...
1761  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1762  * @type Array
1763  * @static
1764  */
1765 Date.dayNames =
1766    ["Sunday",
1767     "Monday",
1768     "Tuesday",
1769     "Wednesday",
1770     "Thursday",
1771     "Friday",
1772     "Saturday"];
1773
1774 // private
1775 Date.y2kYear = 50;
1776 // private
1777 Date.monthNumbers = {
1778     Jan:0,
1779     Feb:1,
1780     Mar:2,
1781     Apr:3,
1782     May:4,
1783     Jun:5,
1784     Jul:6,
1785     Aug:7,
1786     Sep:8,
1787     Oct:9,
1788     Nov:10,
1789     Dec:11};
1790
1791 /**
1792  * Creates and returns a new Date instance with the exact same date value as the called instance.
1793  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1794  * variable will also be changed.  When the intention is to create a new variable that will not
1795  * modify the original instance, you should create a clone.
1796  *
1797  * Example of correctly cloning a date:
1798  * <pre><code>
1799 //wrong way:
1800 var orig = new Date('10/1/2006');
1801 var copy = orig;
1802 copy.setDate(5);
1803 document.write(orig);  //returns 'Thu Oct 05 2006'!
1804
1805 //correct way:
1806 var orig = new Date('10/1/2006');
1807 var copy = orig.clone();
1808 copy.setDate(5);
1809 document.write(orig);  //returns 'Thu Oct 01 2006'
1810 </code></pre>
1811  * @return {Date} The new Date instance
1812  */
1813 Date.prototype.clone = function() {
1814         return new Date(this.getTime());
1815 };
1816
1817 /**
1818  * Clears any time information from this date
1819  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1820  @return {Date} this or the clone
1821  */
1822 Date.prototype.clearTime = function(clone){
1823     if(clone){
1824         return this.clone().clearTime();
1825     }
1826     this.setHours(0);
1827     this.setMinutes(0);
1828     this.setSeconds(0);
1829     this.setMilliseconds(0);
1830     return this;
1831 };
1832
1833 // private
1834 // safari setMonth is broken -- check that this is only donw once...
1835 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1836     Date.brokenSetMonth = Date.prototype.setMonth;
1837         Date.prototype.setMonth = function(num){
1838                 if(num <= -1){
1839                         var n = Math.ceil(-num);
1840                         var back_year = Math.ceil(n/12);
1841                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1842                         this.setFullYear(this.getFullYear() - back_year);
1843                         return Date.brokenSetMonth.call(this, month);
1844                 } else {
1845                         return Date.brokenSetMonth.apply(this, arguments);
1846                 }
1847         };
1848 }
1849
1850 /** Date interval constant 
1851 * @static 
1852 * @type String */
1853 Date.MILLI = "ms";
1854 /** Date interval constant 
1855 * @static 
1856 * @type String */
1857 Date.SECOND = "s";
1858 /** Date interval constant 
1859 * @static 
1860 * @type String */
1861 Date.MINUTE = "mi";
1862 /** Date interval constant 
1863 * @static 
1864 * @type String */
1865 Date.HOUR = "h";
1866 /** Date interval constant 
1867 * @static 
1868 * @type String */
1869 Date.DAY = "d";
1870 /** Date interval constant 
1871 * @static 
1872 * @type String */
1873 Date.MONTH = "mo";
1874 /** Date interval constant 
1875 * @static 
1876 * @type String */
1877 Date.YEAR = "y";
1878
1879 /**
1880  * Provides a convenient method of performing basic date arithmetic.  This method
1881  * does not modify the Date instance being called - it creates and returns
1882  * a new Date instance containing the resulting date value.
1883  *
1884  * Examples:
1885  * <pre><code>
1886 //Basic usage:
1887 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1888 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1889
1890 //Negative values will subtract correctly:
1891 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1892 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1893
1894 //You can even chain several calls together in one line!
1895 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1896 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1897  </code></pre>
1898  *
1899  * @param {String} interval   A valid date interval enum value
1900  * @param {Number} value      The amount to add to the current date
1901  * @return {Date} The new Date instance
1902  */
1903 Date.prototype.add = function(interval, value){
1904   var d = this.clone();
1905   if (!interval || value === 0) { return d; }
1906   switch(interval.toLowerCase()){
1907     case Date.MILLI:
1908       d.setMilliseconds(this.getMilliseconds() + value);
1909       break;
1910     case Date.SECOND:
1911       d.setSeconds(this.getSeconds() + value);
1912       break;
1913     case Date.MINUTE:
1914       d.setMinutes(this.getMinutes() + value);
1915       break;
1916     case Date.HOUR:
1917       d.setHours(this.getHours() + value);
1918       break;
1919     case Date.DAY:
1920       d.setDate(this.getDate() + value);
1921       break;
1922     case Date.MONTH:
1923       var day = this.getDate();
1924       if(day > 28){
1925           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1926       }
1927       d.setDate(day);
1928       d.setMonth(this.getMonth() + value);
1929       break;
1930     case Date.YEAR:
1931       d.setFullYear(this.getFullYear() + value);
1932       break;
1933   }
1934   return d;
1935 };
1936 /**
1937  * @class Roo.lib.Dom
1938  * @licence LGPL
1939  * @static
1940  * 
1941  * Dom utils (from YIU afaik)
1942  *
1943  * 
1944  **/
1945 Roo.lib.Dom = {
1946     /**
1947      * Get the view width
1948      * @param {Boolean} full True will get the full document, otherwise it's the view width
1949      * @return {Number} The width
1950      */
1951      
1952     getViewWidth : function(full) {
1953         return full ? this.getDocumentWidth() : this.getViewportWidth();
1954     },
1955     /**
1956      * Get the view height
1957      * @param {Boolean} full True will get the full document, otherwise it's the view height
1958      * @return {Number} The height
1959      */
1960     getViewHeight : function(full) {
1961         return full ? this.getDocumentHeight() : this.getViewportHeight();
1962     },
1963     /**
1964      * Get the Full Document height 
1965      * @return {Number} The height
1966      */
1967     getDocumentHeight: function() {
1968         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1969         return Math.max(scrollHeight, this.getViewportHeight());
1970     },
1971     /**
1972      * Get the Full Document width
1973      * @return {Number} The width
1974      */
1975     getDocumentWidth: function() {
1976         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1977         return Math.max(scrollWidth, this.getViewportWidth());
1978     },
1979     /**
1980      * Get the Window Viewport height
1981      * @return {Number} The height
1982      */
1983     getViewportHeight: function() {
1984         var height = self.innerHeight;
1985         var mode = document.compatMode;
1986
1987         if ((mode || Roo.isIE) && !Roo.isOpera) {
1988             height = (mode == "CSS1Compat") ?
1989                      document.documentElement.clientHeight :
1990                      document.body.clientHeight;
1991         }
1992
1993         return height;
1994     },
1995     /**
1996      * Get the Window Viewport width
1997      * @return {Number} The width
1998      */
1999     getViewportWidth: function() {
2000         var width = self.innerWidth;
2001         var mode = document.compatMode;
2002
2003         if (mode || Roo.isIE) {
2004             width = (mode == "CSS1Compat") ?
2005                     document.documentElement.clientWidth :
2006                     document.body.clientWidth;
2007         }
2008         return width;
2009     },
2010
2011     isAncestor : function(p, c) {
2012         p = Roo.getDom(p);
2013         c = Roo.getDom(c);
2014         if (!p || !c) {
2015             return false;
2016         }
2017
2018         if (p.contains && !Roo.isSafari) {
2019             return p.contains(c);
2020         } else if (p.compareDocumentPosition) {
2021             return !!(p.compareDocumentPosition(c) & 16);
2022         } else {
2023             var parent = c.parentNode;
2024             while (parent) {
2025                 if (parent == p) {
2026                     return true;
2027                 }
2028                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
2029                     return false;
2030                 }
2031                 parent = parent.parentNode;
2032             }
2033             return false;
2034         }
2035     },
2036
2037     getRegion : function(el) {
2038         return Roo.lib.Region.getRegion(el);
2039     },
2040
2041     getY : function(el) {
2042         return this.getXY(el)[1];
2043     },
2044
2045     getX : function(el) {
2046         return this.getXY(el)[0];
2047     },
2048
2049     getXY : function(el) {
2050         var p, pe, b, scroll, bd = document.body;
2051         el = Roo.getDom(el);
2052         var fly = Roo.lib.AnimBase.fly;
2053         if (el.getBoundingClientRect) {
2054             b = el.getBoundingClientRect();
2055             scroll = fly(document).getScroll();
2056             return [b.left + scroll.left, b.top + scroll.top];
2057         }
2058         var x = 0, y = 0;
2059
2060         p = el;
2061
2062         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2063
2064         while (p) {
2065
2066             x += p.offsetLeft;
2067             y += p.offsetTop;
2068
2069             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2070                 hasAbsolute = true;
2071             }
2072
2073             if (Roo.isGecko) {
2074                 pe = fly(p);
2075
2076                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2077                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2078
2079
2080                 x += bl;
2081                 y += bt;
2082
2083
2084                 if (p != el && pe.getStyle('overflow') != 'visible') {
2085                     x += bl;
2086                     y += bt;
2087                 }
2088             }
2089             p = p.offsetParent;
2090         }
2091
2092         if (Roo.isSafari && hasAbsolute) {
2093             x -= bd.offsetLeft;
2094             y -= bd.offsetTop;
2095         }
2096
2097         if (Roo.isGecko && !hasAbsolute) {
2098             var dbd = fly(bd);
2099             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2100             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2101         }
2102
2103         p = el.parentNode;
2104         while (p && p != bd) {
2105             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2106                 x -= p.scrollLeft;
2107                 y -= p.scrollTop;
2108             }
2109             p = p.parentNode;
2110         }
2111         return [x, y];
2112     },
2113  
2114   
2115
2116
2117     setXY : function(el, xy) {
2118         el = Roo.fly(el, '_setXY');
2119         el.position();
2120         var pts = el.translatePoints(xy);
2121         if (xy[0] !== false) {
2122             el.dom.style.left = pts.left + "px";
2123         }
2124         if (xy[1] !== false) {
2125             el.dom.style.top = pts.top + "px";
2126         }
2127     },
2128
2129     setX : function(el, x) {
2130         this.setXY(el, [x, false]);
2131     },
2132
2133     setY : function(el, y) {
2134         this.setXY(el, [false, y]);
2135     }
2136 };
2137 /*
2138  * Portions of this file are based on pieces of Yahoo User Interface Library
2139  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2140  * YUI licensed under the BSD License:
2141  * http://developer.yahoo.net/yui/license.txt
2142  * <script type="text/javascript">
2143  *
2144  */
2145
2146 Roo.lib.Event = function() {
2147     var loadComplete = false;
2148     var listeners = [];
2149     var unloadListeners = [];
2150     var retryCount = 0;
2151     var onAvailStack = [];
2152     var counter = 0;
2153     var lastError = null;
2154
2155     return {
2156         POLL_RETRYS: 200,
2157         POLL_INTERVAL: 20,
2158         EL: 0,
2159         TYPE: 1,
2160         FN: 2,
2161         WFN: 3,
2162         OBJ: 3,
2163         ADJ_SCOPE: 4,
2164         _interval: null,
2165
2166         startInterval: function() {
2167             if (!this._interval) {
2168                 var self = this;
2169                 var callback = function() {
2170                     self._tryPreloadAttach();
2171                 };
2172                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2173
2174             }
2175         },
2176
2177         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2178             onAvailStack.push({ id:         p_id,
2179                 fn:         p_fn,
2180                 obj:        p_obj,
2181                 override:   p_override,
2182                 checkReady: false    });
2183
2184             retryCount = this.POLL_RETRYS;
2185             this.startInterval();
2186         },
2187
2188
2189         addListener: function(el, eventName, fn) {
2190             el = Roo.getDom(el);
2191             if (!el || !fn) {
2192                 return false;
2193             }
2194
2195             if ("unload" == eventName) {
2196                 unloadListeners[unloadListeners.length] =
2197                 [el, eventName, fn];
2198                 return true;
2199             }
2200
2201             var wrappedFn = function(e) {
2202                 return fn(Roo.lib.Event.getEvent(e));
2203             };
2204
2205             var li = [el, eventName, fn, wrappedFn];
2206
2207             var index = listeners.length;
2208             listeners[index] = li;
2209
2210             this.doAdd(el, eventName, wrappedFn, false);
2211             return true;
2212
2213         },
2214
2215
2216         removeListener: function(el, eventName, fn) {
2217             var i, len;
2218
2219             el = Roo.getDom(el);
2220
2221             if(!fn) {
2222                 return this.purgeElement(el, false, eventName);
2223             }
2224
2225
2226             if ("unload" == eventName) {
2227
2228                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2229                     var li = unloadListeners[i];
2230                     if (li &&
2231                         li[0] == el &&
2232                         li[1] == eventName &&
2233                         li[2] == fn) {
2234                         unloadListeners.splice(i, 1);
2235                         return true;
2236                     }
2237                 }
2238
2239                 return false;
2240             }
2241
2242             var cacheItem = null;
2243
2244
2245             var index = arguments[3];
2246
2247             if ("undefined" == typeof index) {
2248                 index = this._getCacheIndex(el, eventName, fn);
2249             }
2250
2251             if (index >= 0) {
2252                 cacheItem = listeners[index];
2253             }
2254
2255             if (!el || !cacheItem) {
2256                 return false;
2257             }
2258
2259             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2260
2261             delete listeners[index][this.WFN];
2262             delete listeners[index][this.FN];
2263             listeners.splice(index, 1);
2264
2265             return true;
2266
2267         },
2268
2269
2270         getTarget: function(ev, resolveTextNode) {
2271             ev = ev.browserEvent || ev;
2272             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2273             var t = ev.target || ev.srcElement;
2274             return this.resolveTextNode(t);
2275         },
2276
2277
2278         resolveTextNode: function(node) {
2279             if (Roo.isSafari && node && 3 == node.nodeType) {
2280                 return node.parentNode;
2281             } else {
2282                 return node;
2283             }
2284         },
2285
2286
2287         getPageX: function(ev) {
2288             ev = ev.browserEvent || ev;
2289             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2290             var x = ev.pageX;
2291             if (!x && 0 !== x) {
2292                 x = ev.clientX || 0;
2293
2294                 if (Roo.isIE) {
2295                     x += this.getScroll()[1];
2296                 }
2297             }
2298
2299             return x;
2300         },
2301
2302
2303         getPageY: function(ev) {
2304             ev = ev.browserEvent || ev;
2305             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2306             var y = ev.pageY;
2307             if (!y && 0 !== y) {
2308                 y = ev.clientY || 0;
2309
2310                 if (Roo.isIE) {
2311                     y += this.getScroll()[0];
2312                 }
2313             }
2314
2315
2316             return y;
2317         },
2318
2319
2320         getXY: function(ev) {
2321             ev = ev.browserEvent || ev;
2322             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2323             return [this.getPageX(ev), this.getPageY(ev)];
2324         },
2325
2326
2327         getRelatedTarget: function(ev) {
2328             ev = ev.browserEvent || ev;
2329             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2330             var t = ev.relatedTarget;
2331             if (!t) {
2332                 if (ev.type == "mouseout") {
2333                     t = ev.toElement;
2334                 } else if (ev.type == "mouseover") {
2335                     t = ev.fromElement;
2336                 }
2337             }
2338
2339             return this.resolveTextNode(t);
2340         },
2341
2342
2343         getTime: function(ev) {
2344             ev = ev.browserEvent || ev;
2345             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2346             if (!ev.time) {
2347                 var t = new Date().getTime();
2348                 try {
2349                     ev.time = t;
2350                 } catch(ex) {
2351                     this.lastError = ex;
2352                     return t;
2353                 }
2354             }
2355
2356             return ev.time;
2357         },
2358
2359
2360         stopEvent: function(ev) {
2361             this.stopPropagation(ev);
2362             this.preventDefault(ev);
2363         },
2364
2365
2366         stopPropagation: function(ev) {
2367             ev = ev.browserEvent || ev;
2368             if (ev.stopPropagation) {
2369                 ev.stopPropagation();
2370             } else {
2371                 ev.cancelBubble = true;
2372             }
2373         },
2374
2375
2376         preventDefault: function(ev) {
2377             ev = ev.browserEvent || ev;
2378             if(ev.preventDefault) {
2379                 ev.preventDefault();
2380             } else {
2381                 ev.returnValue = false;
2382             }
2383         },
2384
2385
2386         getEvent: function(e) {
2387             var ev = e || window.event;
2388             if (!ev) {
2389                 var c = this.getEvent.caller;
2390                 while (c) {
2391                     ev = c.arguments[0];
2392                     if (ev && Event == ev.constructor) {
2393                         break;
2394                     }
2395                     c = c.caller;
2396                 }
2397             }
2398             return ev;
2399         },
2400
2401
2402         getCharCode: function(ev) {
2403             ev = ev.browserEvent || ev;
2404             return ev.charCode || ev.keyCode || 0;
2405         },
2406
2407
2408         _getCacheIndex: function(el, eventName, fn) {
2409             for (var i = 0,len = listeners.length; i < len; ++i) {
2410                 var li = listeners[i];
2411                 if (li &&
2412                     li[this.FN] == fn &&
2413                     li[this.EL] == el &&
2414                     li[this.TYPE] == eventName) {
2415                     return i;
2416                 }
2417             }
2418
2419             return -1;
2420         },
2421
2422
2423         elCache: {},
2424
2425
2426         getEl: function(id) {
2427             return document.getElementById(id);
2428         },
2429
2430
2431         clearCache: function() {
2432         },
2433
2434
2435         _load: function(e) {
2436             loadComplete = true;
2437             var EU = Roo.lib.Event;
2438
2439
2440             if (Roo.isIE) {
2441                 EU.doRemove(window, "load", EU._load);
2442             }
2443         },
2444
2445
2446         _tryPreloadAttach: function() {
2447
2448             if (this.locked) {
2449                 return false;
2450             }
2451
2452             this.locked = true;
2453
2454
2455             var tryAgain = !loadComplete;
2456             if (!tryAgain) {
2457                 tryAgain = (retryCount > 0);
2458             }
2459
2460
2461             var notAvail = [];
2462             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2463                 var item = onAvailStack[i];
2464                 if (item) {
2465                     var el = this.getEl(item.id);
2466
2467                     if (el) {
2468                         if (!item.checkReady ||
2469                             loadComplete ||
2470                             el.nextSibling ||
2471                             (document && document.body)) {
2472
2473                             var scope = el;
2474                             if (item.override) {
2475                                 if (item.override === true) {
2476                                     scope = item.obj;
2477                                 } else {
2478                                     scope = item.override;
2479                                 }
2480                             }
2481                             item.fn.call(scope, item.obj);
2482                             onAvailStack[i] = null;
2483                         }
2484                     } else {
2485                         notAvail.push(item);
2486                     }
2487                 }
2488             }
2489
2490             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2491
2492             if (tryAgain) {
2493
2494                 this.startInterval();
2495             } else {
2496                 clearInterval(this._interval);
2497                 this._interval = null;
2498             }
2499
2500             this.locked = false;
2501
2502             return true;
2503
2504         },
2505
2506
2507         purgeElement: function(el, recurse, eventName) {
2508             var elListeners = this.getListeners(el, eventName);
2509             if (elListeners) {
2510                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2511                     var l = elListeners[i];
2512                     this.removeListener(el, l.type, l.fn);
2513                 }
2514             }
2515
2516             if (recurse && el && el.childNodes) {
2517                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2518                     this.purgeElement(el.childNodes[i], recurse, eventName);
2519                 }
2520             }
2521         },
2522
2523
2524         getListeners: function(el, eventName) {
2525             var results = [], searchLists;
2526             if (!eventName) {
2527                 searchLists = [listeners, unloadListeners];
2528             } else if (eventName == "unload") {
2529                 searchLists = [unloadListeners];
2530             } else {
2531                 searchLists = [listeners];
2532             }
2533
2534             for (var j = 0; j < searchLists.length; ++j) {
2535                 var searchList = searchLists[j];
2536                 if (searchList && searchList.length > 0) {
2537                     for (var i = 0,len = searchList.length; i < len; ++i) {
2538                         var l = searchList[i];
2539                         if (l && l[this.EL] === el &&
2540                             (!eventName || eventName === l[this.TYPE])) {
2541                             results.push({
2542                                 type:   l[this.TYPE],
2543                                 fn:     l[this.FN],
2544                                 obj:    l[this.OBJ],
2545                                 adjust: l[this.ADJ_SCOPE],
2546                                 index:  i
2547                             });
2548                         }
2549                     }
2550                 }
2551             }
2552
2553             return (results.length) ? results : null;
2554         },
2555
2556
2557         _unload: function(e) {
2558
2559             var EU = Roo.lib.Event, i, j, l, len, index;
2560
2561             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2562                 l = unloadListeners[i];
2563                 if (l) {
2564                     var scope = window;
2565                     if (l[EU.ADJ_SCOPE]) {
2566                         if (l[EU.ADJ_SCOPE] === true) {
2567                             scope = l[EU.OBJ];
2568                         } else {
2569                             scope = l[EU.ADJ_SCOPE];
2570                         }
2571                     }
2572                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2573                     unloadListeners[i] = null;
2574                     l = null;
2575                     scope = null;
2576                 }
2577             }
2578
2579             unloadListeners = null;
2580
2581             if (listeners && listeners.length > 0) {
2582                 j = listeners.length;
2583                 while (j) {
2584                     index = j - 1;
2585                     l = listeners[index];
2586                     if (l) {
2587                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2588                                 l[EU.FN], index);
2589                     }
2590                     j = j - 1;
2591                 }
2592                 l = null;
2593
2594                 EU.clearCache();
2595             }
2596
2597             EU.doRemove(window, "unload", EU._unload);
2598
2599         },
2600
2601
2602         getScroll: function() {
2603             var dd = document.documentElement, db = document.body;
2604             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2605                 return [dd.scrollTop, dd.scrollLeft];
2606             } else if (db) {
2607                 return [db.scrollTop, db.scrollLeft];
2608             } else {
2609                 return [0, 0];
2610             }
2611         },
2612
2613
2614         doAdd: function () {
2615             if (window.addEventListener) {
2616                 return function(el, eventName, fn, capture) {
2617                     el.addEventListener(eventName, fn, (capture));
2618                 };
2619             } else if (window.attachEvent) {
2620                 return function(el, eventName, fn, capture) {
2621                     el.attachEvent("on" + eventName, fn);
2622                 };
2623             } else {
2624                 return function() {
2625                 };
2626             }
2627         }(),
2628
2629
2630         doRemove: function() {
2631             if (window.removeEventListener) {
2632                 return function (el, eventName, fn, capture) {
2633                     el.removeEventListener(eventName, fn, (capture));
2634                 };
2635             } else if (window.detachEvent) {
2636                 return function (el, eventName, fn) {
2637                     el.detachEvent("on" + eventName, fn);
2638                 };
2639             } else {
2640                 return function() {
2641                 };
2642             }
2643         }()
2644     };
2645     
2646 }();
2647 (function() {     
2648    
2649     var E = Roo.lib.Event;
2650     E.on = E.addListener;
2651     E.un = E.removeListener;
2652
2653     if (document && document.body) {
2654         E._load();
2655     } else {
2656         E.doAdd(window, "load", E._load);
2657     }
2658     E.doAdd(window, "unload", E._unload);
2659     E._tryPreloadAttach();
2660 })();
2661
2662  
2663
2664 (function() {
2665     /**
2666      * @class Roo.lib.Ajax
2667      *
2668      * provide a simple Ajax request utility functions
2669      * 
2670      * Portions of this file are based on pieces of Yahoo User Interface Library
2671     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2672     * YUI licensed under the BSD License:
2673     * http://developer.yahoo.net/yui/license.txt
2674     * <script type="text/javascript">
2675     *
2676      *
2677      */
2678     Roo.lib.Ajax = {
2679         /**
2680          * @static 
2681          */
2682         request : function(method, uri, cb, data, options) {
2683             if(options){
2684                 var hs = options.headers;
2685                 if(hs){
2686                     for(var h in hs){
2687                         if(hs.hasOwnProperty(h)){
2688                             this.initHeader(h, hs[h], false);
2689                         }
2690                     }
2691                 }
2692                 if(options.xmlData){
2693                     this.initHeader('Content-Type', 'text/xml', false);
2694                     method = 'POST';
2695                     data = options.xmlData;
2696                 }
2697             }
2698
2699             return this.asyncRequest(method, uri, cb, data);
2700         },
2701         /**
2702          * serialize a form
2703          *
2704          * @static
2705          * @param {DomForm} form element
2706          * @return {String} urlencode form output.
2707          */
2708         serializeForm : function(form) {
2709             if(typeof form == 'string') {
2710                 form = (document.getElementById(form) || document.forms[form]);
2711             }
2712
2713             var el, name, val, disabled, data = '', hasSubmit = false;
2714             for (var i = 0; i < form.elements.length; i++) {
2715                 el = form.elements[i];
2716                 disabled = form.elements[i].disabled;
2717                 name = form.elements[i].name;
2718                 val = form.elements[i].value;
2719
2720                 if (!disabled && name){
2721                     switch (el.type)
2722                             {
2723                         case 'select-one':
2724                         case 'select-multiple':
2725                             for (var j = 0; j < el.options.length; j++) {
2726                                 if (el.options[j].selected) {
2727                                     if (Roo.isIE) {
2728                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2729                                     }
2730                                     else {
2731                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2732                                     }
2733                                 }
2734                             }
2735                             break;
2736                         case 'radio':
2737                         case 'checkbox':
2738                             if (el.checked) {
2739                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2740                             }
2741                             break;
2742                         case 'file':
2743
2744                         case undefined:
2745
2746                         case 'reset':
2747
2748                         case 'button':
2749
2750                             break;
2751                         case 'submit':
2752                             if(hasSubmit == false) {
2753                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2754                                 hasSubmit = true;
2755                             }
2756                             break;
2757                         default:
2758                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2759                             break;
2760                     }
2761                 }
2762             }
2763             data = data.substr(0, data.length - 1);
2764             return data;
2765         },
2766
2767         headers:{},
2768
2769         hasHeaders:false,
2770
2771         useDefaultHeader:true,
2772
2773         defaultPostHeader:'application/x-www-form-urlencoded',
2774
2775         useDefaultXhrHeader:true,
2776
2777         defaultXhrHeader:'XMLHttpRequest',
2778
2779         hasDefaultHeaders:true,
2780
2781         defaultHeaders:{},
2782
2783         poll:{},
2784
2785         timeout:{},
2786
2787         pollInterval:50,
2788
2789         transactionId:0,
2790
2791         setProgId:function(id)
2792         {
2793             this.activeX.unshift(id);
2794         },
2795
2796         setDefaultPostHeader:function(b)
2797         {
2798             this.useDefaultHeader = b;
2799         },
2800
2801         setDefaultXhrHeader:function(b)
2802         {
2803             this.useDefaultXhrHeader = b;
2804         },
2805
2806         setPollingInterval:function(i)
2807         {
2808             if (typeof i == 'number' && isFinite(i)) {
2809                 this.pollInterval = i;
2810             }
2811         },
2812
2813         createXhrObject:function(transactionId)
2814         {
2815             var obj,http;
2816             try
2817             {
2818
2819                 http = new XMLHttpRequest();
2820
2821                 obj = { conn:http, tId:transactionId };
2822             }
2823             catch(e)
2824             {
2825                 for (var i = 0; i < this.activeX.length; ++i) {
2826                     try
2827                     {
2828
2829                         http = new ActiveXObject(this.activeX[i]);
2830
2831                         obj = { conn:http, tId:transactionId };
2832                         break;
2833                     }
2834                     catch(e) {
2835                     }
2836                 }
2837             }
2838             finally
2839             {
2840                 return obj;
2841             }
2842         },
2843
2844         getConnectionObject:function()
2845         {
2846             var o;
2847             var tId = this.transactionId;
2848
2849             try
2850             {
2851                 o = this.createXhrObject(tId);
2852                 if (o) {
2853                     this.transactionId++;
2854                 }
2855             }
2856             catch(e) {
2857             }
2858             finally
2859             {
2860                 return o;
2861             }
2862         },
2863
2864         asyncRequest:function(method, uri, callback, postData)
2865         {
2866             var o = this.getConnectionObject();
2867
2868             if (!o) {
2869                 return null;
2870             }
2871             else {
2872                 o.conn.open(method, uri, true);
2873
2874                 if (this.useDefaultXhrHeader) {
2875                     if (!this.defaultHeaders['X-Requested-With']) {
2876                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2877                     }
2878                 }
2879
2880                 if(postData && this.useDefaultHeader){
2881                     this.initHeader('Content-Type', this.defaultPostHeader);
2882                 }
2883
2884                  if (this.hasDefaultHeaders || this.hasHeaders) {
2885                     this.setHeader(o);
2886                 }
2887
2888                 this.handleReadyState(o, callback);
2889                 o.conn.send(postData || null);
2890
2891                 return o;
2892             }
2893         },
2894
2895         handleReadyState:function(o, callback)
2896         {
2897             var oConn = this;
2898
2899             if (callback && callback.timeout) {
2900                 
2901                 this.timeout[o.tId] = window.setTimeout(function() {
2902                     oConn.abort(o, callback, true);
2903                 }, callback.timeout);
2904             }
2905
2906             this.poll[o.tId] = window.setInterval(
2907                     function() {
2908                         if (o.conn && o.conn.readyState == 4) {
2909                             window.clearInterval(oConn.poll[o.tId]);
2910                             delete oConn.poll[o.tId];
2911
2912                             if(callback && callback.timeout) {
2913                                 window.clearTimeout(oConn.timeout[o.tId]);
2914                                 delete oConn.timeout[o.tId];
2915                             }
2916
2917                             oConn.handleTransactionResponse(o, callback);
2918                         }
2919                     }
2920                     , this.pollInterval);
2921         },
2922
2923         handleTransactionResponse:function(o, callback, isAbort)
2924         {
2925
2926             if (!callback) {
2927                 this.releaseObject(o);
2928                 return;
2929             }
2930
2931             var httpStatus, responseObject;
2932
2933             try
2934             {
2935                 if (o.conn.status !== undefined && o.conn.status != 0) {
2936                     httpStatus = o.conn.status;
2937                 }
2938                 else {
2939                     httpStatus = 13030;
2940                 }
2941             }
2942             catch(e) {
2943
2944
2945                 httpStatus = 13030;
2946             }
2947
2948             if (httpStatus >= 200 && httpStatus < 300) {
2949                 responseObject = this.createResponseObject(o, callback.argument);
2950                 if (callback.success) {
2951                     if (!callback.scope) {
2952                         callback.success(responseObject);
2953                     }
2954                     else {
2955
2956
2957                         callback.success.apply(callback.scope, [responseObject]);
2958                     }
2959                 }
2960             }
2961             else {
2962                 switch (httpStatus) {
2963
2964                     case 12002:
2965                     case 12029:
2966                     case 12030:
2967                     case 12031:
2968                     case 12152:
2969                     case 13030:
2970                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2971                         if (callback.failure) {
2972                             if (!callback.scope) {
2973                                 callback.failure(responseObject);
2974                             }
2975                             else {
2976                                 callback.failure.apply(callback.scope, [responseObject]);
2977                             }
2978                         }
2979                         break;
2980                     default:
2981                         responseObject = this.createResponseObject(o, callback.argument);
2982                         if (callback.failure) {
2983                             if (!callback.scope) {
2984                                 callback.failure(responseObject);
2985                             }
2986                             else {
2987                                 callback.failure.apply(callback.scope, [responseObject]);
2988                             }
2989                         }
2990                 }
2991             }
2992
2993             this.releaseObject(o);
2994             responseObject = null;
2995         },
2996
2997         createResponseObject:function(o, callbackArg)
2998         {
2999             var obj = {};
3000             var headerObj = {};
3001
3002             try
3003             {
3004                 var headerStr = o.conn.getAllResponseHeaders();
3005                 var header = headerStr.split('\n');
3006                 for (var i = 0; i < header.length; i++) {
3007                     var delimitPos = header[i].indexOf(':');
3008                     if (delimitPos != -1) {
3009                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
3010                     }
3011                 }
3012             }
3013             catch(e) {
3014             }
3015
3016             obj.tId = o.tId;
3017             obj.status = o.conn.status;
3018             obj.statusText = o.conn.statusText;
3019             obj.getResponseHeader = headerObj;
3020             obj.getAllResponseHeaders = headerStr;
3021             obj.responseText = o.conn.responseText;
3022             obj.responseXML = o.conn.responseXML;
3023
3024             if (typeof callbackArg !== undefined) {
3025                 obj.argument = callbackArg;
3026             }
3027
3028             return obj;
3029         },
3030
3031         createExceptionObject:function(tId, callbackArg, isAbort)
3032         {
3033             var COMM_CODE = 0;
3034             var COMM_ERROR = 'communication failure';
3035             var ABORT_CODE = -1;
3036             var ABORT_ERROR = 'transaction aborted';
3037
3038             var obj = {};
3039
3040             obj.tId = tId;
3041             if (isAbort) {
3042                 obj.status = ABORT_CODE;
3043                 obj.statusText = ABORT_ERROR;
3044             }
3045             else {
3046                 obj.status = COMM_CODE;
3047                 obj.statusText = COMM_ERROR;
3048             }
3049
3050             if (callbackArg) {
3051                 obj.argument = callbackArg;
3052             }
3053
3054             return obj;
3055         },
3056
3057         initHeader:function(label, value, isDefault)
3058         {
3059             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3060
3061             if (headerObj[label] === undefined) {
3062                 headerObj[label] = value;
3063             }
3064             else {
3065
3066
3067                 headerObj[label] = value + "," + headerObj[label];
3068             }
3069
3070             if (isDefault) {
3071                 this.hasDefaultHeaders = true;
3072             }
3073             else {
3074                 this.hasHeaders = true;
3075             }
3076         },
3077
3078
3079         setHeader:function(o)
3080         {
3081             if (this.hasDefaultHeaders) {
3082                 for (var prop in this.defaultHeaders) {
3083                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3084                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3085                     }
3086                 }
3087             }
3088
3089             if (this.hasHeaders) {
3090                 for (var prop in this.headers) {
3091                     if (this.headers.hasOwnProperty(prop)) {
3092                         o.conn.setRequestHeader(prop, this.headers[prop]);
3093                     }
3094                 }
3095                 this.headers = {};
3096                 this.hasHeaders = false;
3097             }
3098         },
3099
3100         resetDefaultHeaders:function() {
3101             delete this.defaultHeaders;
3102             this.defaultHeaders = {};
3103             this.hasDefaultHeaders = false;
3104         },
3105
3106         abort:function(o, callback, isTimeout)
3107         {
3108             if(this.isCallInProgress(o)) {
3109                 o.conn.abort();
3110                 window.clearInterval(this.poll[o.tId]);
3111                 delete this.poll[o.tId];
3112                 if (isTimeout) {
3113                     delete this.timeout[o.tId];
3114                 }
3115
3116                 this.handleTransactionResponse(o, callback, true);
3117
3118                 return true;
3119             }
3120             else {
3121                 return false;
3122             }
3123         },
3124
3125
3126         isCallInProgress:function(o)
3127         {
3128             if (o && o.conn) {
3129                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3130             }
3131             else {
3132
3133                 return false;
3134             }
3135         },
3136
3137
3138         releaseObject:function(o)
3139         {
3140
3141             o.conn = null;
3142
3143             o = null;
3144         },
3145
3146         activeX:[
3147         'MSXML2.XMLHTTP.3.0',
3148         'MSXML2.XMLHTTP',
3149         'Microsoft.XMLHTTP'
3150         ]
3151
3152
3153     };
3154 })();/*
3155  * Portions of this file are based on pieces of Yahoo User Interface Library
3156  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3157  * YUI licensed under the BSD License:
3158  * http://developer.yahoo.net/yui/license.txt
3159  * <script type="text/javascript">
3160  *
3161  */
3162
3163 Roo.lib.Region = function(t, r, b, l) {
3164     this.top = t;
3165     this[1] = t;
3166     this.right = r;
3167     this.bottom = b;
3168     this.left = l;
3169     this[0] = l;
3170 };
3171
3172
3173 Roo.lib.Region.prototype = {
3174     contains : function(region) {
3175         return ( region.left >= this.left &&
3176                  region.right <= this.right &&
3177                  region.top >= this.top &&
3178                  region.bottom <= this.bottom    );
3179
3180     },
3181
3182     getArea : function() {
3183         return ( (this.bottom - this.top) * (this.right - this.left) );
3184     },
3185
3186     intersect : function(region) {
3187         var t = Math.max(this.top, region.top);
3188         var r = Math.min(this.right, region.right);
3189         var b = Math.min(this.bottom, region.bottom);
3190         var l = Math.max(this.left, region.left);
3191
3192         if (b >= t && r >= l) {
3193             return new Roo.lib.Region(t, r, b, l);
3194         } else {
3195             return null;
3196         }
3197     },
3198     union : function(region) {
3199         var t = Math.min(this.top, region.top);
3200         var r = Math.max(this.right, region.right);
3201         var b = Math.max(this.bottom, region.bottom);
3202         var l = Math.min(this.left, region.left);
3203
3204         return new Roo.lib.Region(t, r, b, l);
3205     },
3206
3207     adjust : function(t, l, b, r) {
3208         this.top += t;
3209         this.left += l;
3210         this.right += r;
3211         this.bottom += b;
3212         return this;
3213     }
3214 };
3215
3216 Roo.lib.Region.getRegion = function(el) {
3217     var p = Roo.lib.Dom.getXY(el);
3218
3219     var t = p[1];
3220     var r = p[0] + el.offsetWidth;
3221     var b = p[1] + el.offsetHeight;
3222     var l = p[0];
3223
3224     return new Roo.lib.Region(t, r, b, l);
3225 };
3226 /*
3227  * Portions of this file are based on pieces of Yahoo User Interface Library
3228  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3229  * YUI licensed under the BSD License:
3230  * http://developer.yahoo.net/yui/license.txt
3231  * <script type="text/javascript">
3232  *
3233  */
3234 //@@dep Roo.lib.Region
3235
3236
3237 Roo.lib.Point = function(x, y) {
3238     if (x instanceof Array) {
3239         y = x[1];
3240         x = x[0];
3241     }
3242     this.x = this.right = this.left = this[0] = x;
3243     this.y = this.top = this.bottom = this[1] = y;
3244 };
3245
3246 Roo.lib.Point.prototype = new Roo.lib.Region();
3247 /*
3248  * Portions of this file are based on pieces of Yahoo User Interface Library
3249  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3250  * YUI licensed under the BSD License:
3251  * http://developer.yahoo.net/yui/license.txt
3252  * <script type="text/javascript">
3253  *
3254  */
3255  
3256 (function() {   
3257
3258     Roo.lib.Anim = {
3259         scroll : function(el, args, duration, easing, cb, scope) {
3260             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3261         },
3262
3263         motion : function(el, args, duration, easing, cb, scope) {
3264             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3265         },
3266
3267         color : function(el, args, duration, easing, cb, scope) {
3268             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3269         },
3270
3271         run : function(el, args, duration, easing, cb, scope, type) {
3272             type = type || Roo.lib.AnimBase;
3273             if (typeof easing == "string") {
3274                 easing = Roo.lib.Easing[easing];
3275             }
3276             var anim = new type(el, args, duration, easing);
3277             anim.animateX(function() {
3278                 Roo.callback(cb, scope);
3279             });
3280             return anim;
3281         }
3282     };
3283 })();/*
3284  * Portions of this file are based on pieces of Yahoo User Interface Library
3285  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3286  * YUI licensed under the BSD License:
3287  * http://developer.yahoo.net/yui/license.txt
3288  * <script type="text/javascript">
3289  *
3290  */
3291
3292 (function() {    
3293     var libFlyweight;
3294     
3295     function fly(el) {
3296         if (!libFlyweight) {
3297             libFlyweight = new Roo.Element.Flyweight();
3298         }
3299         libFlyweight.dom = el;
3300         return libFlyweight;
3301     }
3302
3303     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3304     
3305    
3306     
3307     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3308         if (el) {
3309             this.init(el, attributes, duration, method);
3310         }
3311     };
3312
3313     Roo.lib.AnimBase.fly = fly;
3314     
3315     
3316     
3317     Roo.lib.AnimBase.prototype = {
3318
3319         toString: function() {
3320             var el = this.getEl();
3321             var id = el.id || el.tagName;
3322             return ("Anim " + id);
3323         },
3324
3325         patterns: {
3326             noNegatives:        /width|height|opacity|padding/i,
3327             offsetAttribute:  /^((width|height)|(top|left))$/,
3328             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3329             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3330         },
3331
3332
3333         doMethod: function(attr, start, end) {
3334             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3335         },
3336
3337
3338         setAttribute: function(attr, val, unit) {
3339             if (this.patterns.noNegatives.test(attr)) {
3340                 val = (val > 0) ? val : 0;
3341             }
3342
3343             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3344         },
3345
3346
3347         getAttribute: function(attr) {
3348             var el = this.getEl();
3349             var val = fly(el).getStyle(attr);
3350
3351             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3352                 return parseFloat(val);
3353             }
3354
3355             var a = this.patterns.offsetAttribute.exec(attr) || [];
3356             var pos = !!( a[3] );
3357             var box = !!( a[2] );
3358
3359
3360             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3361                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3362             } else {
3363                 val = 0;
3364             }
3365
3366             return val;
3367         },
3368
3369
3370         getDefaultUnit: function(attr) {
3371             if (this.patterns.defaultUnit.test(attr)) {
3372                 return 'px';
3373             }
3374
3375             return '';
3376         },
3377
3378         animateX : function(callback, scope) {
3379             var f = function() {
3380                 this.onComplete.removeListener(f);
3381                 if (typeof callback == "function") {
3382                     callback.call(scope || this, this);
3383                 }
3384             };
3385             this.onComplete.addListener(f, this);
3386             this.animate();
3387         },
3388
3389
3390         setRuntimeAttribute: function(attr) {
3391             var start;
3392             var end;
3393             var attributes = this.attributes;
3394
3395             this.runtimeAttributes[attr] = {};
3396
3397             var isset = function(prop) {
3398                 return (typeof prop !== 'undefined');
3399             };
3400
3401             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3402                 return false;
3403             }
3404
3405             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3406
3407
3408             if (isset(attributes[attr]['to'])) {
3409                 end = attributes[attr]['to'];
3410             } else if (isset(attributes[attr]['by'])) {
3411                 if (start.constructor == Array) {
3412                     end = [];
3413                     for (var i = 0, len = start.length; i < len; ++i) {
3414                         end[i] = start[i] + attributes[attr]['by'][i];
3415                     }
3416                 } else {
3417                     end = start + attributes[attr]['by'];
3418                 }
3419             }
3420
3421             this.runtimeAttributes[attr].start = start;
3422             this.runtimeAttributes[attr].end = end;
3423
3424
3425             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3426         },
3427
3428
3429         init: function(el, attributes, duration, method) {
3430
3431             var isAnimated = false;
3432
3433
3434             var startTime = null;
3435
3436
3437             var actualFrames = 0;
3438
3439
3440             el = Roo.getDom(el);
3441
3442
3443             this.attributes = attributes || {};
3444
3445
3446             this.duration = duration || 1;
3447
3448
3449             this.method = method || Roo.lib.Easing.easeNone;
3450
3451
3452             this.useSeconds = true;
3453
3454
3455             this.currentFrame = 0;
3456
3457
3458             this.totalFrames = Roo.lib.AnimMgr.fps;
3459
3460
3461             this.getEl = function() {
3462                 return el;
3463             };
3464
3465
3466             this.isAnimated = function() {
3467                 return isAnimated;
3468             };
3469
3470
3471             this.getStartTime = function() {
3472                 return startTime;
3473             };
3474
3475             this.runtimeAttributes = {};
3476
3477
3478             this.animate = function() {
3479                 if (this.isAnimated()) {
3480                     return false;
3481                 }
3482
3483                 this.currentFrame = 0;
3484
3485                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3486
3487                 Roo.lib.AnimMgr.registerElement(this);
3488             };
3489
3490
3491             this.stop = function(finish) {
3492                 if (finish) {
3493                     this.currentFrame = this.totalFrames;
3494                     this._onTween.fire();
3495                 }
3496                 Roo.lib.AnimMgr.stop(this);
3497             };
3498
3499             var onStart = function() {
3500                 this.onStart.fire();
3501
3502                 this.runtimeAttributes = {};
3503                 for (var attr in this.attributes) {
3504                     this.setRuntimeAttribute(attr);
3505                 }
3506
3507                 isAnimated = true;
3508                 actualFrames = 0;
3509                 startTime = new Date();
3510             };
3511
3512
3513             var onTween = function() {
3514                 var data = {
3515                     duration: new Date() - this.getStartTime(),
3516                     currentFrame: this.currentFrame
3517                 };
3518
3519                 data.toString = function() {
3520                     return (
3521                             'duration: ' + data.duration +
3522                             ', currentFrame: ' + data.currentFrame
3523                             );
3524                 };
3525
3526                 this.onTween.fire(data);
3527
3528                 var runtimeAttributes = this.runtimeAttributes;
3529
3530                 for (var attr in runtimeAttributes) {
3531                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3532                 }
3533
3534                 actualFrames += 1;
3535             };
3536
3537             var onComplete = function() {
3538                 var actual_duration = (new Date() - startTime) / 1000 ;
3539
3540                 var data = {
3541                     duration: actual_duration,
3542                     frames: actualFrames,
3543                     fps: actualFrames / actual_duration
3544                 };
3545
3546                 data.toString = function() {
3547                     return (
3548                             'duration: ' + data.duration +
3549                             ', frames: ' + data.frames +
3550                             ', fps: ' + data.fps
3551                             );
3552                 };
3553
3554                 isAnimated = false;
3555                 actualFrames = 0;
3556                 this.onComplete.fire(data);
3557             };
3558
3559
3560             this._onStart = new Roo.util.Event(this);
3561             this.onStart = new Roo.util.Event(this);
3562             this.onTween = new Roo.util.Event(this);
3563             this._onTween = new Roo.util.Event(this);
3564             this.onComplete = new Roo.util.Event(this);
3565             this._onComplete = new Roo.util.Event(this);
3566             this._onStart.addListener(onStart);
3567             this._onTween.addListener(onTween);
3568             this._onComplete.addListener(onComplete);
3569         }
3570     };
3571 })();
3572 /*
3573  * Portions of this file are based on pieces of Yahoo User Interface Library
3574  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3575  * YUI licensed under the BSD License:
3576  * http://developer.yahoo.net/yui/license.txt
3577  * <script type="text/javascript">
3578  *
3579  */
3580
3581 Roo.lib.AnimMgr = new function() {
3582
3583     var thread = null;
3584
3585
3586     var queue = [];
3587
3588
3589     var tweenCount = 0;
3590
3591
3592     this.fps = 1000;
3593
3594
3595     this.delay = 1;
3596
3597
3598     this.registerElement = function(tween) {
3599         queue[queue.length] = tween;
3600         tweenCount += 1;
3601         tween._onStart.fire();
3602         this.start();
3603     };
3604
3605
3606     this.unRegister = function(tween, index) {
3607         tween._onComplete.fire();
3608         index = index || getIndex(tween);
3609         if (index != -1) {
3610             queue.splice(index, 1);
3611         }
3612
3613         tweenCount -= 1;
3614         if (tweenCount <= 0) {
3615             this.stop();
3616         }
3617     };
3618
3619
3620     this.start = function() {
3621         if (thread === null) {
3622             thread = setInterval(this.run, this.delay);
3623         }
3624     };
3625
3626
3627     this.stop = function(tween) {
3628         if (!tween) {
3629             clearInterval(thread);
3630
3631             for (var i = 0, len = queue.length; i < len; ++i) {
3632                 if (queue[0].isAnimated()) {
3633                     this.unRegister(queue[0], 0);
3634                 }
3635             }
3636
3637             queue = [];
3638             thread = null;
3639             tweenCount = 0;
3640         }
3641         else {
3642             this.unRegister(tween);
3643         }
3644     };
3645
3646
3647     this.run = function() {
3648         for (var i = 0, len = queue.length; i < len; ++i) {
3649             var tween = queue[i];
3650             if (!tween || !tween.isAnimated()) {
3651                 continue;
3652             }
3653
3654             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3655             {
3656                 tween.currentFrame += 1;
3657
3658                 if (tween.useSeconds) {
3659                     correctFrame(tween);
3660                 }
3661                 tween._onTween.fire();
3662             }
3663             else {
3664                 Roo.lib.AnimMgr.stop(tween, i);
3665             }
3666         }
3667     };
3668
3669     var getIndex = function(anim) {
3670         for (var i = 0, len = queue.length; i < len; ++i) {
3671             if (queue[i] == anim) {
3672                 return i;
3673             }
3674         }
3675         return -1;
3676     };
3677
3678
3679     var correctFrame = function(tween) {
3680         var frames = tween.totalFrames;
3681         var frame = tween.currentFrame;
3682         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3683         var elapsed = (new Date() - tween.getStartTime());
3684         var tweak = 0;
3685
3686         if (elapsed < tween.duration * 1000) {
3687             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3688         } else {
3689             tweak = frames - (frame + 1);
3690         }
3691         if (tweak > 0 && isFinite(tweak)) {
3692             if (tween.currentFrame + tweak >= frames) {
3693                 tweak = frames - (frame + 1);
3694             }
3695
3696             tween.currentFrame += tweak;
3697         }
3698     };
3699 };
3700
3701     /*
3702  * Portions of this file are based on pieces of Yahoo User Interface Library
3703  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3704  * YUI licensed under the BSD License:
3705  * http://developer.yahoo.net/yui/license.txt
3706  * <script type="text/javascript">
3707  *
3708  */
3709 Roo.lib.Bezier = new function() {
3710
3711         this.getPosition = function(points, t) {
3712             var n = points.length;
3713             var tmp = [];
3714
3715             for (var i = 0; i < n; ++i) {
3716                 tmp[i] = [points[i][0], points[i][1]];
3717             }
3718
3719             for (var j = 1; j < n; ++j) {
3720                 for (i = 0; i < n - j; ++i) {
3721                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3722                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3723                 }
3724             }
3725
3726             return [ tmp[0][0], tmp[0][1] ];
3727
3728         };
3729     }; 
3730
3731 /**
3732  * @class Roo.lib.Color
3733  * @constructor
3734  * An abstract Color implementation. Concrete Color implementations should use
3735  * an instance of this function as their prototype, and implement the getRGB and
3736  * getHSL functions. getRGB should return an object representing the RGB
3737  * components of this Color, with the red, green, and blue components in the
3738  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3739  * return an object representing the HSL components of this Color, with the hue
3740  * component in the range [0,360), the saturation and lightness components in
3741  * the range [0,100], and the alpha component in the range [0,1].
3742  *
3743  *
3744  * Color.js
3745  *
3746  * Functions for Color handling and processing.
3747  *
3748  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3749  *
3750  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3751  * rights to this program, with the intention of it becoming part of the public
3752  * domain. Because this program is released into the public domain, it comes with
3753  * no warranty either expressed or implied, to the extent permitted by law.
3754  * 
3755  * For more free and public domain JavaScript code by the same author, visit:
3756  * http://www.safalra.com/web-design/javascript/
3757  * 
3758  */
3759 Roo.lib.Color = function() { }
3760
3761
3762 Roo.apply(Roo.lib.Color.prototype, {
3763   
3764   rgb : null,
3765   hsv : null,
3766   hsl : null,
3767   
3768   /**
3769    * getIntegerRGB
3770    * @return {Object} an object representing the RGBA components of this Color. The red,
3771    * green, and blue components are converted to integers in the range [0,255].
3772    * The alpha is a value in the range [0,1].
3773    */
3774   getIntegerRGB : function(){
3775
3776     // get the RGB components of this Color
3777     var rgb = this.getRGB();
3778
3779     // return the integer components
3780     return {
3781       'r' : Math.round(rgb.r),
3782       'g' : Math.round(rgb.g),
3783       'b' : Math.round(rgb.b),
3784       'a' : rgb.a
3785     };
3786
3787   },
3788
3789   /**
3790    * getPercentageRGB
3791    * @return {Object} an object representing the RGBA components of this Color. The red,
3792    * green, and blue components are converted to numbers in the range [0,100].
3793    * The alpha is a value in the range [0,1].
3794    */
3795   getPercentageRGB : function(){
3796
3797     // get the RGB components of this Color
3798     var rgb = this.getRGB();
3799
3800     // return the percentage components
3801     return {
3802       'r' : 100 * rgb.r / 255,
3803       'g' : 100 * rgb.g / 255,
3804       'b' : 100 * rgb.b / 255,
3805       'a' : rgb.a
3806     };
3807
3808   },
3809
3810   /**
3811    * getCSSHexadecimalRGB
3812    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3813    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3814    * are two-digit hexadecimal numbers.
3815    */
3816   getCSSHexadecimalRGB : function()
3817   {
3818
3819     // get the integer RGB components
3820     var rgb = this.getIntegerRGB();
3821
3822     // determine the hexadecimal equivalents
3823     var r16 = rgb.r.toString(16);
3824     var g16 = rgb.g.toString(16);
3825     var b16 = rgb.b.toString(16);
3826
3827     // return the CSS RGB Color value
3828     return '#'
3829         + (r16.length == 2 ? r16 : '0' + r16)
3830         + (g16.length == 2 ? g16 : '0' + g16)
3831         + (b16.length == 2 ? b16 : '0' + b16);
3832
3833   },
3834
3835   /**
3836    * getCSSIntegerRGB
3837    * @return {String} a string representing this Color as a CSS integer RGB Color
3838    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3839    * are integers in the range [0,255].
3840    */
3841   getCSSIntegerRGB : function(){
3842
3843     // get the integer RGB components
3844     var rgb = this.getIntegerRGB();
3845
3846     // return the CSS RGB Color value
3847     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3848
3849   },
3850
3851   /**
3852    * getCSSIntegerRGBA
3853    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3854    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3855    * b are integers in the range [0,255] and a is in the range [0,1].
3856    */
3857   getCSSIntegerRGBA : function(){
3858
3859     // get the integer RGB components
3860     var rgb = this.getIntegerRGB();
3861
3862     // return the CSS integer RGBA Color value
3863     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3864
3865   },
3866
3867   /**
3868    * getCSSPercentageRGB
3869    * @return {String} a string representing this Color as a CSS percentage RGB Color
3870    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3871    * b are in the range [0,100].
3872    */
3873   getCSSPercentageRGB : function(){
3874
3875     // get the percentage RGB components
3876     var rgb = this.getPercentageRGB();
3877
3878     // return the CSS RGB Color value
3879     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3880
3881   },
3882
3883   /**
3884    * getCSSPercentageRGBA
3885    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3886    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3887    * and b are in the range [0,100] and a is in the range [0,1].
3888    */
3889   getCSSPercentageRGBA : function(){
3890
3891     // get the percentage RGB components
3892     var rgb = this.getPercentageRGB();
3893
3894     // return the CSS percentage RGBA Color value
3895     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3896
3897   },
3898
3899   /**
3900    * getCSSHSL
3901    * @return {String} a string representing this Color as a CSS HSL Color value - that
3902    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3903    * s and l are in the range [0,100].
3904    */
3905   getCSSHSL : function(){
3906
3907     // get the HSL components
3908     var hsl = this.getHSL();
3909
3910     // return the CSS HSL Color value
3911     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3912
3913   },
3914
3915   /**
3916    * getCSSHSLA
3917    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3918    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3919    * s and l are in the range [0,100], and a is in the range [0,1].
3920    */
3921   getCSSHSLA : function(){
3922
3923     // get the HSL components
3924     var hsl = this.getHSL();
3925
3926     // return the CSS HSL Color value
3927     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3928
3929   },
3930
3931   /**
3932    * Sets the Color of the specified node to this Color. This functions sets
3933    * the CSS 'color' property for the node. The parameter is:
3934    * 
3935    * @param {DomElement} node - the node whose Color should be set
3936    */
3937   setNodeColor : function(node){
3938
3939     // set the Color of the node
3940     node.style.color = this.getCSSHexadecimalRGB();
3941
3942   },
3943
3944   /**
3945    * Sets the background Color of the specified node to this Color. This
3946    * functions sets the CSS 'background-color' property for the node. The
3947    * parameter is:
3948    *
3949    * @param {DomElement} node - the node whose background Color should be set
3950    */
3951   setNodeBackgroundColor : function(node){
3952
3953     // set the background Color of the node
3954     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3955
3956   },
3957   // convert between formats..
3958   toRGB: function()
3959   {
3960     var r = this.getIntegerRGB();
3961     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3962     
3963   },
3964   toHSL : function()
3965   {
3966      var hsl = this.getHSL();
3967   // return the CSS HSL Color value
3968     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3969     
3970   },
3971   
3972   toHSV : function()
3973   {
3974     var rgb = this.toRGB();
3975     var hsv = rgb.getHSV();
3976    // return the CSS HSL Color value
3977     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3978     
3979   },
3980   
3981   // modify  v = 0 ... 1 (eg. 0.5)
3982   saturate : function(v)
3983   {
3984       var rgb = this.toRGB();
3985       var hsv = rgb.getHSV();
3986       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3987       
3988   
3989   },
3990   
3991    
3992   /**
3993    * getRGB
3994    * @return {Object} the RGB and alpha components of this Color as an object with r,
3995    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3996    * the range [0,1].
3997    */
3998   getRGB: function(){
3999    
4000     // return the RGB components
4001     return {
4002       'r' : this.rgb.r,
4003       'g' : this.rgb.g,
4004       'b' : this.rgb.b,
4005       'a' : this.alpha
4006     };
4007
4008   },
4009
4010   /**
4011    * getHSV
4012    * @return {Object} the HSV and alpha components of this Color as an object with h,
4013    * s, v, and a properties. h is in the range [0,360), s and v are in the range
4014    * [0,100], and a is in the range [0,1].
4015    */
4016   getHSV : function()
4017   {
4018     
4019     // calculate the HSV components if necessary
4020     if (this.hsv == null) {
4021       this.calculateHSV();
4022     }
4023
4024     // return the HSV components
4025     return {
4026       'h' : this.hsv.h,
4027       's' : this.hsv.s,
4028       'v' : this.hsv.v,
4029       'a' : this.alpha
4030     };
4031
4032   },
4033
4034   /**
4035    * getHSL
4036    * @return {Object} the HSL and alpha components of this Color as an object with h,
4037    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4038    * [0,100], and a is in the range [0,1].
4039    */
4040   getHSL : function(){
4041     
4042      
4043     // calculate the HSV components if necessary
4044     if (this.hsl == null) { this.calculateHSL(); }
4045
4046     // return the HSL components
4047     return {
4048       'h' : this.hsl.h,
4049       's' : this.hsl.s,
4050       'l' : this.hsl.l,
4051       'a' : this.alpha
4052     };
4053
4054   }
4055   
4056
4057 });
4058
4059
4060 /**
4061  * @class Roo.lib.RGBColor
4062  * @extends Roo.lib.Color
4063  * Creates a Color specified in the RGB Color space, with an optional alpha
4064  * component. The parameters are:
4065  * @constructor
4066  * 
4067
4068  * @param {Number} r - the red component, clipped to the range [0,255]
4069  * @param {Number} g - the green component, clipped to the range [0,255]
4070  * @param {Number} b - the blue component, clipped to the range [0,255]
4071  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4072  *     optional and defaults to 1
4073  */
4074 Roo.lib.RGBColor = function (r, g, b, a){
4075
4076   // store the alpha component after clipping it if necessary
4077   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4078
4079   // store the RGB components after clipping them if necessary
4080   this.rgb =
4081       {
4082         'r' : Math.max(0, Math.min(255, r)),
4083         'g' : Math.max(0, Math.min(255, g)),
4084         'b' : Math.max(0, Math.min(255, b))
4085       };
4086
4087   // initialise the HSV and HSL components to null
4088   
4089
4090   /* 
4091    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4092    * range [0,360). The parameters are:
4093    *
4094    * maximum - the maximum of the RGB component values
4095    * range   - the range of the RGB component values
4096    */
4097    
4098
4099 }
4100 // this does an 'exteds'
4101 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4102
4103   
4104     getHue  : function(maximum, range)
4105     {
4106       var rgb = this.rgb;
4107        
4108       // check whether the range is zero
4109       if (range == 0){
4110   
4111         // set the hue to zero (any hue is acceptable as the Color is grey)
4112         var hue = 0;
4113   
4114       }else{
4115   
4116         // determine which of the components has the highest value and set the hue
4117         switch (maximum){
4118   
4119           // red has the highest value
4120           case rgb.r:
4121             var hue = (rgb.g - rgb.b) / range * 60;
4122             if (hue < 0) { hue += 360; }
4123             break;
4124   
4125           // green has the highest value
4126           case rgb.g:
4127             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4128             break;
4129   
4130           // blue has the highest value
4131           case rgb.b:
4132             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4133             break;
4134   
4135         }
4136   
4137       }
4138   
4139       // return the hue
4140       return hue;
4141   
4142     },
4143
4144   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4145    * be returned be the getHSV function.
4146    */
4147    calculateHSV : function(){
4148     var rgb = this.rgb;
4149     // get the maximum and range of the RGB component values
4150     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4151     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4152
4153     // store the HSV components
4154     this.hsv =
4155         {
4156           'h' : this.getHue(maximum, range),
4157           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4158           'v' : maximum / 2.55
4159         };
4160
4161   },
4162
4163   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4164    * be returned be the getHSL function.
4165    */
4166    calculateHSL : function(){
4167     var rgb = this.rgb;
4168     // get the maximum and range of the RGB component values
4169     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4170     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4171
4172     // determine the lightness in the range [0,1]
4173     var l = maximum / 255 - range / 510;
4174
4175     // store the HSL components
4176     this.hsl =
4177         {
4178           'h' : this.getHue(maximum, range),
4179           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4180           'l' : 100 * l
4181         };
4182
4183   }
4184
4185 });
4186
4187 /**
4188  * @class Roo.lib.HSVColor
4189  * @extends Roo.lib.Color
4190  * Creates a Color specified in the HSV Color space, with an optional alpha
4191  * component. The parameters are:
4192  * @constructor
4193  *
4194  * @param {Number} h - the hue component, wrapped to the range [0,360)
4195  * @param {Number} s - the saturation component, clipped to the range [0,100]
4196  * @param {Number} v - the value component, clipped to the range [0,100]
4197  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4198  *     optional and defaults to 1
4199  */
4200 Roo.lib.HSVColor = function (h, s, v, a){
4201
4202   // store the alpha component after clipping it if necessary
4203   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4204
4205   // store the HSV components after clipping or wrapping them if necessary
4206   this.hsv =
4207       {
4208         'h' : (h % 360 + 360) % 360,
4209         's' : Math.max(0, Math.min(100, s)),
4210         'v' : Math.max(0, Math.min(100, v))
4211       };
4212
4213   // initialise the RGB and HSL components to null
4214   this.rgb = null;
4215   this.hsl = null;
4216 }
4217
4218 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4219   /* Calculates and stores the RGB components of this HSVColor so that they can
4220    * be returned be the getRGB function.
4221    */
4222   calculateRGB: function ()
4223   {
4224     var hsv = this.hsv;
4225     // check whether the saturation is zero
4226     if (hsv.s == 0){
4227
4228       // set the Color to the appropriate shade of grey
4229       var r = hsv.v;
4230       var g = hsv.v;
4231       var b = hsv.v;
4232
4233     }else{
4234
4235       // set some temporary values
4236       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4237       var p  = hsv.v * (1 - hsv.s / 100);
4238       var q  = hsv.v * (1 - hsv.s / 100 * f);
4239       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4240
4241       // set the RGB Color components to their temporary values
4242       switch (Math.floor(hsv.h / 60)){
4243         case 0: var r = hsv.v; var g = t; var b = p; break;
4244         case 1: var r = q; var g = hsv.v; var b = p; break;
4245         case 2: var r = p; var g = hsv.v; var b = t; break;
4246         case 3: var r = p; var g = q; var b = hsv.v; break;
4247         case 4: var r = t; var g = p; var b = hsv.v; break;
4248         case 5: var r = hsv.v; var g = p; var b = q; break;
4249       }
4250
4251     }
4252
4253     // store the RGB components
4254     this.rgb =
4255         {
4256           'r' : r * 2.55,
4257           'g' : g * 2.55,
4258           'b' : b * 2.55
4259         };
4260
4261   },
4262
4263   /* Calculates and stores the HSL components of this HSVColor so that they can
4264    * be returned be the getHSL function.
4265    */
4266   calculateHSL : function (){
4267
4268     var hsv = this.hsv;
4269     // determine the lightness in the range [0,100]
4270     var l = (2 - hsv.s / 100) * hsv.v / 2;
4271
4272     // store the HSL components
4273     this.hsl =
4274         {
4275           'h' : hsv.h,
4276           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4277           'l' : l
4278         };
4279
4280     // correct a division-by-zero error
4281     if (isNaN(hsl.s)) { hsl.s = 0; }
4282
4283   } 
4284  
4285
4286 });
4287  
4288
4289 /**
4290  * @class Roo.lib.HSLColor
4291  * @extends Roo.lib.Color
4292  *
4293  * @constructor
4294  * Creates a Color specified in the HSL Color space, with an optional alpha
4295  * component. The parameters are:
4296  *
4297  * @param {Number} h - the hue component, wrapped to the range [0,360)
4298  * @param {Number} s - the saturation component, clipped to the range [0,100]
4299  * @param {Number} l - the lightness component, clipped to the range [0,100]
4300  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4301  *     optional and defaults to 1
4302  */
4303
4304 Roo.lib.HSLColor = function(h, s, l, a){
4305
4306   // store the alpha component after clipping it if necessary
4307   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4308
4309   // store the HSL components after clipping or wrapping them if necessary
4310   this.hsl =
4311       {
4312         'h' : (h % 360 + 360) % 360,
4313         's' : Math.max(0, Math.min(100, s)),
4314         'l' : Math.max(0, Math.min(100, l))
4315       };
4316
4317   // initialise the RGB and HSV components to null
4318 }
4319
4320 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4321
4322   /* Calculates and stores the RGB components of this HSLColor so that they can
4323    * be returned be the getRGB function.
4324    */
4325   calculateRGB: function (){
4326
4327     // check whether the saturation is zero
4328     if (this.hsl.s == 0){
4329
4330       // store the RGB components representing the appropriate shade of grey
4331       this.rgb =
4332           {
4333             'r' : this.hsl.l * 2.55,
4334             'g' : this.hsl.l * 2.55,
4335             'b' : this.hsl.l * 2.55
4336           };
4337
4338     }else{
4339
4340       // set some temporary values
4341       var p = this.hsl.l < 50
4342             ? this.hsl.l * (1 + hsl.s / 100)
4343             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4344       var q = 2 * hsl.l - p;
4345
4346       // initialise the RGB components
4347       this.rgb =
4348           {
4349             'r' : (h + 120) / 60 % 6,
4350             'g' : h / 60,
4351             'b' : (h + 240) / 60 % 6
4352           };
4353
4354       // loop over the RGB components
4355       for (var key in this.rgb){
4356
4357         // ensure that the property is not inherited from the root object
4358         if (this.rgb.hasOwnProperty(key)){
4359
4360           // set the component to its value in the range [0,100]
4361           if (this.rgb[key] < 1){
4362             this.rgb[key] = q + (p - q) * this.rgb[key];
4363           }else if (this.rgb[key] < 3){
4364             this.rgb[key] = p;
4365           }else if (this.rgb[key] < 4){
4366             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4367           }else{
4368             this.rgb[key] = q;
4369           }
4370
4371           // set the component to its value in the range [0,255]
4372           this.rgb[key] *= 2.55;
4373
4374         }
4375
4376       }
4377
4378     }
4379
4380   },
4381
4382   /* Calculates and stores the HSV components of this HSLColor so that they can
4383    * be returned be the getHSL function.
4384    */
4385    calculateHSV : function(){
4386
4387     // set a temporary value
4388     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4389
4390     // store the HSV components
4391     this.hsv =
4392         {
4393           'h' : this.hsl.h,
4394           's' : 200 * t / (this.hsl.l + t),
4395           'v' : t + this.hsl.l
4396         };
4397
4398     // correct a division-by-zero error
4399     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4400
4401   }
4402  
4403
4404 });
4405 /*
4406  * Portions of this file are based on pieces of Yahoo User Interface Library
4407  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4408  * YUI licensed under the BSD License:
4409  * http://developer.yahoo.net/yui/license.txt
4410  * <script type="text/javascript">
4411  *
4412  */
4413 (function() {
4414
4415     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4416         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4417     };
4418
4419     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4420
4421     var fly = Roo.lib.AnimBase.fly;
4422     var Y = Roo.lib;
4423     var superclass = Y.ColorAnim.superclass;
4424     var proto = Y.ColorAnim.prototype;
4425
4426     proto.toString = function() {
4427         var el = this.getEl();
4428         var id = el.id || el.tagName;
4429         return ("ColorAnim " + id);
4430     };
4431
4432     proto.patterns.color = /color$/i;
4433     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4434     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4435     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4436     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4437
4438
4439     proto.parseColor = function(s) {
4440         if (s.length == 3) {
4441             return s;
4442         }
4443
4444         var c = this.patterns.hex.exec(s);
4445         if (c && c.length == 4) {
4446             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4447         }
4448
4449         c = this.patterns.rgb.exec(s);
4450         if (c && c.length == 4) {
4451             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4452         }
4453
4454         c = this.patterns.hex3.exec(s);
4455         if (c && c.length == 4) {
4456             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4457         }
4458
4459         return null;
4460     };
4461     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4462     proto.getAttribute = function(attr) {
4463         var el = this.getEl();
4464         if (this.patterns.color.test(attr)) {
4465             var val = fly(el).getStyle(attr);
4466
4467             if (this.patterns.transparent.test(val)) {
4468                 var parent = el.parentNode;
4469                 val = fly(parent).getStyle(attr);
4470
4471                 while (parent && this.patterns.transparent.test(val)) {
4472                     parent = parent.parentNode;
4473                     val = fly(parent).getStyle(attr);
4474                     if (parent.tagName.toUpperCase() == 'HTML') {
4475                         val = '#fff';
4476                     }
4477                 }
4478             }
4479         } else {
4480             val = superclass.getAttribute.call(this, attr);
4481         }
4482
4483         return val;
4484     };
4485     proto.getAttribute = function(attr) {
4486         var el = this.getEl();
4487         if (this.patterns.color.test(attr)) {
4488             var val = fly(el).getStyle(attr);
4489
4490             if (this.patterns.transparent.test(val)) {
4491                 var parent = el.parentNode;
4492                 val = fly(parent).getStyle(attr);
4493
4494                 while (parent && this.patterns.transparent.test(val)) {
4495                     parent = parent.parentNode;
4496                     val = fly(parent).getStyle(attr);
4497                     if (parent.tagName.toUpperCase() == 'HTML') {
4498                         val = '#fff';
4499                     }
4500                 }
4501             }
4502         } else {
4503             val = superclass.getAttribute.call(this, attr);
4504         }
4505
4506         return val;
4507     };
4508
4509     proto.doMethod = function(attr, start, end) {
4510         var val;
4511
4512         if (this.patterns.color.test(attr)) {
4513             val = [];
4514             for (var i = 0, len = start.length; i < len; ++i) {
4515                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4516             }
4517
4518             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4519         }
4520         else {
4521             val = superclass.doMethod.call(this, attr, start, end);
4522         }
4523
4524         return val;
4525     };
4526
4527     proto.setRuntimeAttribute = function(attr) {
4528         superclass.setRuntimeAttribute.call(this, attr);
4529
4530         if (this.patterns.color.test(attr)) {
4531             var attributes = this.attributes;
4532             var start = this.parseColor(this.runtimeAttributes[attr].start);
4533             var end = this.parseColor(this.runtimeAttributes[attr].end);
4534
4535             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4536                 end = this.parseColor(attributes[attr].by);
4537
4538                 for (var i = 0, len = start.length; i < len; ++i) {
4539                     end[i] = start[i] + end[i];
4540                 }
4541             }
4542
4543             this.runtimeAttributes[attr].start = start;
4544             this.runtimeAttributes[attr].end = end;
4545         }
4546     };
4547 })();
4548
4549 /*
4550  * Portions of this file are based on pieces of Yahoo User Interface Library
4551  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4552  * YUI licensed under the BSD License:
4553  * http://developer.yahoo.net/yui/license.txt
4554  * <script type="text/javascript">
4555  *
4556  */
4557 Roo.lib.Easing = {
4558
4559
4560     easeNone: function (t, b, c, d) {
4561         return c * t / d + b;
4562     },
4563
4564
4565     easeIn: function (t, b, c, d) {
4566         return c * (t /= d) * t + b;
4567     },
4568
4569
4570     easeOut: function (t, b, c, d) {
4571         return -c * (t /= d) * (t - 2) + b;
4572     },
4573
4574
4575     easeBoth: function (t, b, c, d) {
4576         if ((t /= d / 2) < 1) {
4577             return c / 2 * t * t + b;
4578         }
4579
4580         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4581     },
4582
4583
4584     easeInStrong: function (t, b, c, d) {
4585         return c * (t /= d) * t * t * t + b;
4586     },
4587
4588
4589     easeOutStrong: function (t, b, c, d) {
4590         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4591     },
4592
4593
4594     easeBothStrong: function (t, b, c, d) {
4595         if ((t /= d / 2) < 1) {
4596             return c / 2 * t * t * t * t + b;
4597         }
4598
4599         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4600     },
4601
4602
4603
4604     elasticIn: function (t, b, c, d, a, p) {
4605         if (t == 0) {
4606             return b;
4607         }
4608         if ((t /= d) == 1) {
4609             return b + c;
4610         }
4611         if (!p) {
4612             p = d * .3;
4613         }
4614
4615         if (!a || a < Math.abs(c)) {
4616             a = c;
4617             var s = p / 4;
4618         }
4619         else {
4620             var s = p / (2 * Math.PI) * Math.asin(c / a);
4621         }
4622
4623         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4624     },
4625
4626
4627     elasticOut: function (t, b, c, d, a, p) {
4628         if (t == 0) {
4629             return b;
4630         }
4631         if ((t /= d) == 1) {
4632             return b + c;
4633         }
4634         if (!p) {
4635             p = d * .3;
4636         }
4637
4638         if (!a || a < Math.abs(c)) {
4639             a = c;
4640             var s = p / 4;
4641         }
4642         else {
4643             var s = p / (2 * Math.PI) * Math.asin(c / a);
4644         }
4645
4646         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4647     },
4648
4649
4650     elasticBoth: function (t, b, c, d, a, p) {
4651         if (t == 0) {
4652             return b;
4653         }
4654
4655         if ((t /= d / 2) == 2) {
4656             return b + c;
4657         }
4658
4659         if (!p) {
4660             p = d * (.3 * 1.5);
4661         }
4662
4663         if (!a || a < Math.abs(c)) {
4664             a = c;
4665             var s = p / 4;
4666         }
4667         else {
4668             var s = p / (2 * Math.PI) * Math.asin(c / a);
4669         }
4670
4671         if (t < 1) {
4672             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4673                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4674         }
4675         return a * Math.pow(2, -10 * (t -= 1)) *
4676                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4677     },
4678
4679
4680
4681     backIn: function (t, b, c, d, s) {
4682         if (typeof s == 'undefined') {
4683             s = 1.70158;
4684         }
4685         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4686     },
4687
4688
4689     backOut: function (t, b, c, d, s) {
4690         if (typeof s == 'undefined') {
4691             s = 1.70158;
4692         }
4693         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4694     },
4695
4696
4697     backBoth: function (t, b, c, d, s) {
4698         if (typeof s == 'undefined') {
4699             s = 1.70158;
4700         }
4701
4702         if ((t /= d / 2 ) < 1) {
4703             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4704         }
4705         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4706     },
4707
4708
4709     bounceIn: function (t, b, c, d) {
4710         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4711     },
4712
4713
4714     bounceOut: function (t, b, c, d) {
4715         if ((t /= d) < (1 / 2.75)) {
4716             return c * (7.5625 * t * t) + b;
4717         } else if (t < (2 / 2.75)) {
4718             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4719         } else if (t < (2.5 / 2.75)) {
4720             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4721         }
4722         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4723     },
4724
4725
4726     bounceBoth: function (t, b, c, d) {
4727         if (t < d / 2) {
4728             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4729         }
4730         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4731     }
4732 };/*
4733  * Portions of this file are based on pieces of Yahoo User Interface Library
4734  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4735  * YUI licensed under the BSD License:
4736  * http://developer.yahoo.net/yui/license.txt
4737  * <script type="text/javascript">
4738  *
4739  */
4740     (function() {
4741         Roo.lib.Motion = function(el, attributes, duration, method) {
4742             if (el) {
4743                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4744             }
4745         };
4746
4747         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4748
4749
4750         var Y = Roo.lib;
4751         var superclass = Y.Motion.superclass;
4752         var proto = Y.Motion.prototype;
4753
4754         proto.toString = function() {
4755             var el = this.getEl();
4756             var id = el.id || el.tagName;
4757             return ("Motion " + id);
4758         };
4759
4760         proto.patterns.points = /^points$/i;
4761
4762         proto.setAttribute = function(attr, val, unit) {
4763             if (this.patterns.points.test(attr)) {
4764                 unit = unit || 'px';
4765                 superclass.setAttribute.call(this, 'left', val[0], unit);
4766                 superclass.setAttribute.call(this, 'top', val[1], unit);
4767             } else {
4768                 superclass.setAttribute.call(this, attr, val, unit);
4769             }
4770         };
4771
4772         proto.getAttribute = function(attr) {
4773             if (this.patterns.points.test(attr)) {
4774                 var val = [
4775                         superclass.getAttribute.call(this, 'left'),
4776                         superclass.getAttribute.call(this, 'top')
4777                         ];
4778             } else {
4779                 val = superclass.getAttribute.call(this, attr);
4780             }
4781
4782             return val;
4783         };
4784
4785         proto.doMethod = function(attr, start, end) {
4786             var val = null;
4787
4788             if (this.patterns.points.test(attr)) {
4789                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4790                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4791             } else {
4792                 val = superclass.doMethod.call(this, attr, start, end);
4793             }
4794             return val;
4795         };
4796
4797         proto.setRuntimeAttribute = function(attr) {
4798             if (this.patterns.points.test(attr)) {
4799                 var el = this.getEl();
4800                 var attributes = this.attributes;
4801                 var start;
4802                 var control = attributes['points']['control'] || [];
4803                 var end;
4804                 var i, len;
4805
4806                 if (control.length > 0 && !(control[0] instanceof Array)) {
4807                     control = [control];
4808                 } else {
4809                     var tmp = [];
4810                     for (i = 0,len = control.length; i < len; ++i) {
4811                         tmp[i] = control[i];
4812                     }
4813                     control = tmp;
4814                 }
4815
4816                 Roo.fly(el).position();
4817
4818                 if (isset(attributes['points']['from'])) {
4819                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4820                 }
4821                 else {
4822                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4823                 }
4824
4825                 start = this.getAttribute('points');
4826
4827
4828                 if (isset(attributes['points']['to'])) {
4829                     end = translateValues.call(this, attributes['points']['to'], start);
4830
4831                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4832                     for (i = 0,len = control.length; i < len; ++i) {
4833                         control[i] = translateValues.call(this, control[i], start);
4834                     }
4835
4836
4837                 } else if (isset(attributes['points']['by'])) {
4838                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4839
4840                     for (i = 0,len = control.length; i < len; ++i) {
4841                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4842                     }
4843                 }
4844
4845                 this.runtimeAttributes[attr] = [start];
4846
4847                 if (control.length > 0) {
4848                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4849                 }
4850
4851                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4852             }
4853             else {
4854                 superclass.setRuntimeAttribute.call(this, attr);
4855             }
4856         };
4857
4858         var translateValues = function(val, start) {
4859             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4860             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4861
4862             return val;
4863         };
4864
4865         var isset = function(prop) {
4866             return (typeof prop !== 'undefined');
4867         };
4868     })();
4869 /*
4870  * Portions of this file are based on pieces of Yahoo User Interface Library
4871  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4872  * YUI licensed under the BSD License:
4873  * http://developer.yahoo.net/yui/license.txt
4874  * <script type="text/javascript">
4875  *
4876  */
4877     (function() {
4878         Roo.lib.Scroll = function(el, attributes, duration, method) {
4879             if (el) {
4880                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4881             }
4882         };
4883
4884         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4885
4886
4887         var Y = Roo.lib;
4888         var superclass = Y.Scroll.superclass;
4889         var proto = Y.Scroll.prototype;
4890
4891         proto.toString = function() {
4892             var el = this.getEl();
4893             var id = el.id || el.tagName;
4894             return ("Scroll " + id);
4895         };
4896
4897         proto.doMethod = function(attr, start, end) {
4898             var val = null;
4899
4900             if (attr == 'scroll') {
4901                 val = [
4902                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4903                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4904                         ];
4905
4906             } else {
4907                 val = superclass.doMethod.call(this, attr, start, end);
4908             }
4909             return val;
4910         };
4911
4912         proto.getAttribute = function(attr) {
4913             var val = null;
4914             var el = this.getEl();
4915
4916             if (attr == 'scroll') {
4917                 val = [ el.scrollLeft, el.scrollTop ];
4918             } else {
4919                 val = superclass.getAttribute.call(this, attr);
4920             }
4921
4922             return val;
4923         };
4924
4925         proto.setAttribute = function(attr, val, unit) {
4926             var el = this.getEl();
4927
4928             if (attr == 'scroll') {
4929                 el.scrollLeft = val[0];
4930                 el.scrollTop = val[1];
4931             } else {
4932                 superclass.setAttribute.call(this, attr, val, unit);
4933             }
4934         };
4935     })();
4936 /**
4937  * Originally based of this code... - refactored for Roo...
4938  * https://github.com/aaalsaleh/undo-manager
4939  
4940  * undo-manager.js
4941  * @author  Abdulrahman Alsaleh 
4942  * @copyright 2015 Abdulrahman Alsaleh 
4943  * @license  MIT License (c) 
4944  *
4945  * Hackily modifyed by alan@roojs.com
4946  *
4947  *
4948  *  
4949  *
4950  *  TOTALLY UNTESTED...
4951  *
4952  *  Documentation to be done....
4953  */
4954  
4955
4956 /**
4957 * @class Roo.lib.UndoManager
4958 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4959 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4960
4961  * Usage:
4962  * <pre><code>
4963
4964
4965 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4966  
4967 </code></pre>
4968
4969 * For more information see this blog post with examples:
4970 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4971      - Create Elements using DOM, HTML fragments and Templates</a>. 
4972 * @constructor
4973 * @param {Number} limit how far back to go ... use 1000?
4974 * @param {Object} scope usually use document..
4975 */
4976
4977 Roo.lib.UndoManager = function (limit, undoScopeHost)
4978 {
4979     this.stack = [];
4980     this.limit = limit;
4981     this.scope = undoScopeHost;
4982     this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4983     if (this.fireEvent) {
4984         this.bindEvents();
4985     }
4986     this.reset();
4987     
4988 };
4989         
4990 Roo.lib.UndoManager.prototype = {
4991     
4992     limit : false,
4993     stack : false,
4994     scope :  false,
4995     fireEvent : false,
4996     position : 0,
4997     length : 0,
4998     
4999     
5000      /**
5001      * To push and execute a transaction, the method undoManager.transact
5002      * must be called by passing a transaction object as the first argument, and a merge
5003      * flag as the second argument. A transaction object has the following properties:
5004      *
5005      * Usage:
5006 <pre><code>
5007 undoManager.transact({
5008     label: 'Typing',
5009     execute: function() { ... },
5010     undo: function() { ... },
5011     // redo same as execute
5012     redo: function() { this.execute(); }
5013 }, false);
5014
5015 // merge transaction
5016 undoManager.transact({
5017     label: 'Typing',
5018     execute: function() { ... },  // this will be run...
5019     undo: function() { ... }, // what to do when undo is run.
5020     // redo same as execute
5021     redo: function() { this.execute(); }
5022 }, true); 
5023 </code></pre> 
5024      *
5025      * 
5026      * @param {Object} transaction The transaction to add to the stack.
5027      * @return {String} The HTML fragment
5028      */
5029     
5030     
5031     transact : function (transaction, merge)
5032     {
5033         if (arguments.length < 2) {
5034             throw new TypeError('Not enough arguments to UndoManager.transact.');
5035         }
5036
5037         transaction.execute();
5038
5039         this.stack.splice(0, this.position);
5040         if (merge && this.length) {
5041             this.stack[0].push(transaction);
5042         } else {
5043             this.stack.unshift([transaction]);
5044         }
5045     
5046         this.position = 0;
5047
5048         if (this.limit && this.stack.length > this.limit) {
5049             this.length = this.stack.length = this.limit;
5050         } else {
5051             this.length = this.stack.length;
5052         }
5053
5054         if (this.fireEvent) {
5055             this.scope.dispatchEvent(
5056                 new CustomEvent('DOMTransaction', {
5057                     detail: {
5058                         transactions: this.stack[0].slice()
5059                     },
5060                     bubbles: true,
5061                     cancelable: false
5062                 })
5063             );
5064         }
5065         
5066         //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5067       
5068         
5069     },
5070
5071     undo : function ()
5072     {
5073         //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5074         
5075         if (this.position < this.length) {
5076             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5077                 this.stack[this.position][i].undo();
5078             }
5079             this.position++;
5080
5081             if (this.fireEvent) {
5082                 this.scope.dispatchEvent(
5083                     new CustomEvent('undo', {
5084                         detail: {
5085                             transactions: this.stack[this.position - 1].slice()
5086                         },
5087                         bubbles: true,
5088                         cancelable: false
5089                     })
5090                 );
5091             }
5092         }
5093     },
5094
5095     redo : function ()
5096     {
5097         if (this.position > 0) {
5098             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5099                 this.stack[this.position - 1][i].redo();
5100             }
5101             this.position--;
5102
5103             if (this.fireEvent) {
5104                 this.scope.dispatchEvent(
5105                     new CustomEvent('redo', {
5106                         detail: {
5107                             transactions: this.stack[this.position].slice()
5108                         },
5109                         bubbles: true,
5110                         cancelable: false
5111                     })
5112                 );
5113             }
5114         }
5115     },
5116
5117     item : function (index)
5118     {
5119         if (index >= 0 && index < this.length) {
5120             return this.stack[index].slice();
5121         }
5122         return null;
5123     },
5124
5125     clearUndo : function () {
5126         this.stack.length = this.length = this.position;
5127     },
5128
5129     clearRedo : function () {
5130         this.stack.splice(0, this.position);
5131         this.position = 0;
5132         this.length = this.stack.length;
5133     },
5134     /**
5135      * Reset the undo - probaly done on load to clear all history.
5136      */
5137     reset : function()
5138     {
5139         this.stack = [];
5140         this.position = 0;
5141         this.length = 0;
5142         this.current_html = this.scope.innerHTML;
5143         if (this.timer !== false) {
5144             clearTimeout(this.timer);
5145         }
5146         this.timer = false;
5147         this.merge = false;
5148         this.addEvent();
5149         
5150     },
5151     current_html : '',
5152     timer : false,
5153     merge : false,
5154     
5155     
5156     // this will handle the undo/redo on the element.?
5157     bindEvents : function()
5158     {
5159         var el  = this.scope;
5160         el.undoManager = this;
5161         
5162         
5163         this.scope.addEventListener('keydown', function(e) {
5164             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5165                 if (e.shiftKey) {
5166                     el.undoManager.redo(); // Ctrl/Command + Shift + Z
5167                 } else {
5168                     el.undoManager.undo(); // Ctrl/Command + Z
5169                 }
5170         
5171                 e.preventDefault();
5172             }
5173         });
5174         /// ignore keyup..
5175         this.scope.addEventListener('keyup', function(e) {
5176             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5177                 e.preventDefault();
5178             }
5179         });
5180         
5181         
5182         
5183         var t = this;
5184         
5185         el.addEventListener('input', function(e) {
5186             if(el.innerHTML == t.current_html) {
5187                 return;
5188             }
5189             // only record events every second.
5190             if (t.timer !== false) {
5191                clearTimeout(t.timer);
5192                t.timer = false;
5193             }
5194             t.timer = setTimeout(function() { t.merge = false; }, 1000);
5195             
5196             t.addEvent(t.merge);
5197             t.merge = true; // ignore changes happening every second..
5198         });
5199         },
5200     /**
5201      * Manually add an event.
5202      * Normall called without arguements - and it will just get added to the stack.
5203      * 
5204      */
5205     
5206     addEvent : function(merge)
5207     {
5208         //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5209         // not sure if this should clear the timer 
5210         merge = typeof(merge) == 'undefined' ? false : merge; 
5211         
5212         this.scope.undoManager.transact({
5213             scope : this.scope,
5214             oldHTML: this.current_html,
5215             newHTML: this.scope.innerHTML,
5216             // nothing to execute (content already changed when input is fired)
5217             execute: function() { },
5218             undo: function() {
5219                 this.scope.innerHTML = this.current_html = this.oldHTML;
5220             },
5221             redo: function() {
5222                 this.scope.innerHTML = this.current_html = this.newHTML;
5223             }
5224         }, false); //merge);
5225         
5226         this.merge = merge;
5227         
5228         this.current_html = this.scope.innerHTML;
5229     }
5230     
5231     
5232      
5233     
5234     
5235     
5236 };
5237 /**
5238  * @class Roo.lib.Range
5239  * @constructor
5240  * This is a toolkit, normally used to copy features into a Dom Range element
5241  * Roo.lib.Range.wrap(x);
5242  *
5243  *
5244  *
5245  */
5246 Roo.lib.Range = function() { };
5247
5248 /**
5249  * Wrap a Dom Range object, to give it new features...
5250  * @static
5251  * @param {Range} the range to wrap
5252  */
5253 Roo.lib.Range.wrap = function(r) {
5254     return Roo.apply(r, Roo.lib.Range.prototype);
5255 };
5256 /**
5257  * find a parent node eg. LI / OL
5258  * @param {string|Array} node name or array of nodenames
5259  * @return {DomElement|false}
5260  */
5261 Roo.apply(Roo.lib.Range.prototype,
5262 {
5263     
5264     closest : function(str)
5265     {
5266         if (typeof(str) != 'string') {
5267             // assume it's a array.
5268             for(var i = 0;i < str.length;i++) {
5269                 var r = this.closest(str[i]);
5270                 if (r !== false) {
5271                     return r;
5272                 }
5273                 
5274             }
5275             return false;
5276         }
5277         str = str.toLowerCase();
5278         var n = this.commonAncestorContainer; // might not be a node
5279         while (n.nodeType != 1) {
5280             n = n.parentNode;
5281         }
5282         
5283         if (n.nodeName.toLowerCase() == str ) {
5284             return n;
5285         }
5286         if (n.nodeName.toLowerCase() == 'body') {
5287             return false;
5288         }
5289             
5290         return n.closest(str) || false;
5291         
5292     },
5293     cloneRange : function()
5294     {
5295         return Roo.lib.Range.wrap(Range.prototype.cloneRange.call(this));
5296     }
5297 });/**
5298  * @class Roo.lib.Selection
5299  * @constructor
5300  * This is a toolkit, normally used to copy features into a Dom Selection element
5301  * Roo.lib.Selection.wrap(x);
5302  *
5303  *
5304  *
5305  */
5306 Roo.lib.Selection = function() { };
5307
5308 /**
5309  * Wrap a Dom Range object, to give it new features...
5310  * @static
5311  * @param {Range} the range to wrap
5312  */
5313 Roo.lib.Selection.wrap = function(r, doc) {
5314     Roo.apply(r, Roo.lib.Selection.prototype);
5315     r.ownerDocument = doc; // usefull so we dont have to keep referening to it.
5316     return r;
5317 };
5318 /**
5319  * find a parent node eg. LI / OL
5320  * @param {string|Array} node name or array of nodenames
5321  * @return {DomElement|false}
5322  */
5323 Roo.apply(Roo.lib.Selection.prototype,
5324 {
5325     /**
5326      * the owner document
5327      */
5328     ownerDocument : false,
5329     
5330     getRangeAt : function(n)
5331     {
5332         return Roo.lib.Range.wrap(Selection.prototype.getRangeAt.call(this,n));
5333     },
5334     
5335     /**
5336      * insert node at selection 
5337      * @param {DomElement|string} node
5338      * @param {string} cursor (after|in|none) where to place the cursor after inserting.
5339      */
5340     insertNode: function(node, cursor)
5341     {
5342         if (typeof(node) == 'string') {
5343             node = this.ownerDocument.createElement(node);
5344             if (cursor == 'in') {
5345                 node.innerHTML = '&nbsp;';
5346             }
5347         }
5348         
5349         var range = this.getRangeAt(0);
5350         
5351         if (this.type != 'Caret') {
5352             range.deleteContents();
5353         }
5354         var sn = node.childNodes[0]; // select the contents.
5355
5356         
5357         
5358         range.insertNode(node);
5359         if (cursor == 'after') {
5360             node.insertAdjacentHTML('afterend', '&nbsp;');
5361             sn = node.nextSibling;
5362         }
5363         
5364         if (cursor == 'none') {
5365             return;
5366         }
5367         
5368         this.cursorText(sn);
5369     },
5370     
5371     cursorText : function(n)
5372     {
5373        
5374         //var range = this.getRangeAt(0);
5375         range = Roo.lib.Range.wrap(new Range());
5376         //range.selectNode(n);
5377         
5378         var ix = Array.from(n.parentNode.childNodes).indexOf(n);
5379         range.setStart(n.parentNode,ix);
5380         range.setEnd(n.parentNode,ix+1);
5381         //range.collapse(false);
5382          
5383         this.removeAllRanges();
5384         this.addRange(range);
5385         
5386         Roo.log([n, range, this,this.baseOffset,this.extentOffset, this.type]);
5387     },
5388     cursorAfter : function(n)
5389     {
5390         if (!n.nextSibling || n.nextSibling.nodeValue != '&nbsp;') {
5391             n.insertAdjacentHTML('afterend', '&nbsp;');
5392         }
5393         this.cursorText (n.nextSibling);
5394     }
5395         
5396     
5397 });/*
5398  * Based on:
5399  * Ext JS Library 1.1.1
5400  * Copyright(c) 2006-2007, Ext JS, LLC.
5401  *
5402  * Originally Released Under LGPL - original licence link has changed is not relivant.
5403  *
5404  * Fork - LGPL
5405  * <script type="text/javascript">
5406  */
5407
5408
5409 // nasty IE9 hack - what a pile of crap that is..
5410
5411  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5412     Range.prototype.createContextualFragment = function (html) {
5413         var doc = window.document;
5414         var container = doc.createElement("div");
5415         container.innerHTML = html;
5416         var frag = doc.createDocumentFragment(), n;
5417         while ((n = container.firstChild)) {
5418             frag.appendChild(n);
5419         }
5420         return frag;
5421     };
5422 }
5423
5424 /**
5425  * @class Roo.DomHelper
5426  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5427  * 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>.
5428  * @static
5429  */
5430 Roo.DomHelper = function(){
5431     var tempTableEl = null;
5432     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5433     var tableRe = /^table|tbody|tr|td$/i;
5434     var xmlns = {};
5435     // build as innerHTML where available
5436     /** @ignore */
5437     var createHtml = function(o){
5438         if(typeof o == 'string'){
5439             return o;
5440         }
5441         var b = "";
5442         if(!o.tag){
5443             o.tag = "div";
5444         }
5445         b += "<" + o.tag;
5446         for(var attr in o){
5447             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5448             if(attr == "style"){
5449                 var s = o["style"];
5450                 if(typeof s == "function"){
5451                     s = s.call();
5452                 }
5453                 if(typeof s == "string"){
5454                     b += ' style="' + s + '"';
5455                 }else if(typeof s == "object"){
5456                     b += ' style="';
5457                     for(var key in s){
5458                         if(typeof s[key] != "function"){
5459                             b += key + ":" + s[key] + ";";
5460                         }
5461                     }
5462                     b += '"';
5463                 }
5464             }else{
5465                 if(attr == "cls"){
5466                     b += ' class="' + o["cls"] + '"';
5467                 }else if(attr == "htmlFor"){
5468                     b += ' for="' + o["htmlFor"] + '"';
5469                 }else{
5470                     b += " " + attr + '="' + o[attr] + '"';
5471                 }
5472             }
5473         }
5474         if(emptyTags.test(o.tag)){
5475             b += "/>";
5476         }else{
5477             b += ">";
5478             var cn = o.children || o.cn;
5479             if(cn){
5480                 //http://bugs.kde.org/show_bug.cgi?id=71506
5481                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5482                     for(var i = 0, len = cn.length; i < len; i++) {
5483                         b += createHtml(cn[i], b);
5484                     }
5485                 }else{
5486                     b += createHtml(cn, b);
5487                 }
5488             }
5489             if(o.html){
5490                 b += o.html;
5491             }
5492             b += "</" + o.tag + ">";
5493         }
5494         return b;
5495     };
5496
5497     // build as dom
5498     /** @ignore */
5499     var createDom = function(o, parentNode){
5500          
5501         // defininition craeted..
5502         var ns = false;
5503         if (o.ns && o.ns != 'html') {
5504                
5505             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5506                 xmlns[o.ns] = o.xmlns;
5507                 ns = o.xmlns;
5508             }
5509             if (typeof(xmlns[o.ns]) == 'undefined') {
5510                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5511             }
5512             ns = xmlns[o.ns];
5513         }
5514         
5515         
5516         if (typeof(o) == 'string') {
5517             return parentNode.appendChild(document.createTextNode(o));
5518         }
5519         o.tag = o.tag || 'div';
5520         if (o.ns && Roo.isIE) {
5521             ns = false;
5522             o.tag = o.ns + ':' + o.tag;
5523             
5524         }
5525         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5526         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5527         for(var attr in o){
5528             
5529             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5530                     attr == "style" || typeof o[attr] == "function") { continue; }
5531                     
5532             if(attr=="cls" && Roo.isIE){
5533                 el.className = o["cls"];
5534             }else{
5535                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5536                 else { 
5537                     el[attr] = o[attr];
5538                 }
5539             }
5540         }
5541         Roo.DomHelper.applyStyles(el, o.style);
5542         var cn = o.children || o.cn;
5543         if(cn){
5544             //http://bugs.kde.org/show_bug.cgi?id=71506
5545              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5546                 for(var i = 0, len = cn.length; i < len; i++) {
5547                     createDom(cn[i], el);
5548                 }
5549             }else{
5550                 createDom(cn, el);
5551             }
5552         }
5553         if(o.html){
5554             el.innerHTML = o.html;
5555         }
5556         if(parentNode){
5557            parentNode.appendChild(el);
5558         }
5559         return el;
5560     };
5561
5562     var ieTable = function(depth, s, h, e){
5563         tempTableEl.innerHTML = [s, h, e].join('');
5564         var i = -1, el = tempTableEl;
5565         while(++i < depth && el.firstChild){
5566             el = el.firstChild;
5567         }
5568         return el;
5569     };
5570
5571     // kill repeat to save bytes
5572     var ts = '<table>',
5573         te = '</table>',
5574         tbs = ts+'<tbody>',
5575         tbe = '</tbody>'+te,
5576         trs = tbs + '<tr>',
5577         tre = '</tr>'+tbe;
5578
5579     /**
5580      * @ignore
5581      * Nasty code for IE's broken table implementation
5582      */
5583     var insertIntoTable = function(tag, where, el, html){
5584         if(!tempTableEl){
5585             tempTableEl = document.createElement('div');
5586         }
5587         var node;
5588         var before = null;
5589         if(tag == 'td'){
5590             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5591                 return;
5592             }
5593             if(where == 'beforebegin'){
5594                 before = el;
5595                 el = el.parentNode;
5596             } else{
5597                 before = el.nextSibling;
5598                 el = el.parentNode;
5599             }
5600             node = ieTable(4, trs, html, tre);
5601         }
5602         else if(tag == 'tr'){
5603             if(where == 'beforebegin'){
5604                 before = el;
5605                 el = el.parentNode;
5606                 node = ieTable(3, tbs, html, tbe);
5607             } else if(where == 'afterend'){
5608                 before = el.nextSibling;
5609                 el = el.parentNode;
5610                 node = ieTable(3, tbs, html, tbe);
5611             } else{ // INTO a TR
5612                 if(where == 'afterbegin'){
5613                     before = el.firstChild;
5614                 }
5615                 node = ieTable(4, trs, html, tre);
5616             }
5617         } else if(tag == 'tbody'){
5618             if(where == 'beforebegin'){
5619                 before = el;
5620                 el = el.parentNode;
5621                 node = ieTable(2, ts, html, te);
5622             } else if(where == 'afterend'){
5623                 before = el.nextSibling;
5624                 el = el.parentNode;
5625                 node = ieTable(2, ts, html, te);
5626             } else{
5627                 if(where == 'afterbegin'){
5628                     before = el.firstChild;
5629                 }
5630                 node = ieTable(3, tbs, html, tbe);
5631             }
5632         } else{ // TABLE
5633             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5634                 return;
5635             }
5636             if(where == 'afterbegin'){
5637                 before = el.firstChild;
5638             }
5639             node = ieTable(2, ts, html, te);
5640         }
5641         el.insertBefore(node, before);
5642         return node;
5643     };
5644     
5645     // this is a bit like the react update code...
5646     // 
5647     
5648     var updateNode = function(from, to)
5649     {
5650         // should we handle non-standard elements?
5651         Roo.log(["UpdateNode" , from, to]);
5652         if (from.nodeType != to.nodeType) {
5653             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5654             from.parentNode.replaceChild(to, from);
5655         }
5656         
5657         if (from.nodeType == 3) {
5658             // assume it's text?!
5659             if (from.data == to.data) {
5660                 return;
5661             }
5662             from.data = to.data;
5663             return;
5664         }
5665         if (!from.parentNode) {
5666             // not sure why this is happening?
5667             return;
5668         }
5669         // assume 'to' doesnt have '1/3 nodetypes!
5670         // not sure why, by from, parent node might not exist?
5671         if (from.nodeType !=1 || from.tagName != to.tagName) {
5672             Roo.log(["ReplaceChild" , from, to ]);
5673             
5674             from.parentNode.replaceChild(to, from);
5675             return;
5676         }
5677         // compare attributes
5678         var ar = Array.from(from.attributes);
5679         for(var i = 0; i< ar.length;i++) {
5680             if (to.hasAttribute(ar[i].name)) {
5681                 continue;
5682             }
5683             if (ar[i].name == 'id') { // always keep ids?
5684                continue;
5685             }
5686             //if (ar[i].name == 'style') {
5687             //   throw "style removed?";
5688             //}
5689             Roo.log("removeAttribute" + ar[i].name);
5690             from.removeAttribute(ar[i].name);
5691         }
5692         ar = to.attributes;
5693         for(var i = 0; i< ar.length;i++) {
5694             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5695                 Roo.log("skipAttribute " + ar[i].name  + '=' + to.getAttribute(ar[i].name));
5696                 continue;
5697             }
5698             Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5699             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5700         }
5701         // children
5702         var far = Array.from(from.childNodes);
5703         var tar = Array.from(to.childNodes);
5704         // if the lengths are different.. then it's probably a editable content change, rather than
5705         // a change of the block definition..
5706         
5707         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5708          /*if (from.innerHTML == to.innerHTML) {
5709             return;
5710         }
5711         if (far.length != tar.length) {
5712             from.innerHTML = to.innerHTML;
5713             return;
5714         }
5715         */
5716         
5717         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5718             if (i >= far.length) {
5719                 from.appendChild(tar[i]);
5720                 Roo.log(["add", tar[i]]);
5721                 
5722             } else if ( i  >= tar.length) {
5723                 from.removeChild(far[i]);
5724                 Roo.log(["remove", far[i]]);
5725             } else {
5726                 
5727                 updateNode(far[i], tar[i]);
5728             }    
5729         }
5730         
5731         
5732         
5733         
5734     };
5735     
5736     
5737
5738     return {
5739         /** True to force the use of DOM instead of html fragments @type Boolean */
5740         useDom : false,
5741     
5742         /**
5743          * Returns the markup for the passed Element(s) config
5744          * @param {Object} o The Dom object spec (and children)
5745          * @return {String}
5746          */
5747         markup : function(o){
5748             return createHtml(o);
5749         },
5750     
5751         /**
5752          * Applies a style specification to an element
5753          * @param {String/HTMLElement} el The element to apply styles to
5754          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5755          * a function which returns such a specification.
5756          */
5757         applyStyles : function(el, styles){
5758             if(styles){
5759                el = Roo.fly(el);
5760                if(typeof styles == "string"){
5761                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5762                    var matches;
5763                    while ((matches = re.exec(styles)) != null){
5764                        el.setStyle(matches[1], matches[2]);
5765                    }
5766                }else if (typeof styles == "object"){
5767                    for (var style in styles){
5768                       el.setStyle(style, styles[style]);
5769                    }
5770                }else if (typeof styles == "function"){
5771                     Roo.DomHelper.applyStyles(el, styles.call());
5772                }
5773             }
5774         },
5775     
5776         /**
5777          * Inserts an HTML fragment into the Dom
5778          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5779          * @param {HTMLElement} el The context element
5780          * @param {String} html The HTML fragmenet
5781          * @return {HTMLElement} The new node
5782          */
5783         insertHtml : function(where, el, html){
5784             where = where.toLowerCase();
5785             if(el.insertAdjacentHTML){
5786                 if(tableRe.test(el.tagName)){
5787                     var rs;
5788                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5789                         return rs;
5790                     }
5791                 }
5792                 switch(where){
5793                     case "beforebegin":
5794                         el.insertAdjacentHTML('BeforeBegin', html);
5795                         return el.previousSibling;
5796                     case "afterbegin":
5797                         el.insertAdjacentHTML('AfterBegin', html);
5798                         return el.firstChild;
5799                     case "beforeend":
5800                         el.insertAdjacentHTML('BeforeEnd', html);
5801                         return el.lastChild;
5802                     case "afterend":
5803                         el.insertAdjacentHTML('AfterEnd', html);
5804                         return el.nextSibling;
5805                 }
5806                 throw 'Illegal insertion point -> "' + where + '"';
5807             }
5808             var range = el.ownerDocument.createRange();
5809             var frag;
5810             switch(where){
5811                  case "beforebegin":
5812                     range.setStartBefore(el);
5813                     frag = range.createContextualFragment(html);
5814                     el.parentNode.insertBefore(frag, el);
5815                     return el.previousSibling;
5816                  case "afterbegin":
5817                     if(el.firstChild){
5818                         range.setStartBefore(el.firstChild);
5819                         frag = range.createContextualFragment(html);
5820                         el.insertBefore(frag, el.firstChild);
5821                         return el.firstChild;
5822                     }else{
5823                         el.innerHTML = html;
5824                         return el.firstChild;
5825                     }
5826                 case "beforeend":
5827                     if(el.lastChild){
5828                         range.setStartAfter(el.lastChild);
5829                         frag = range.createContextualFragment(html);
5830                         el.appendChild(frag);
5831                         return el.lastChild;
5832                     }else{
5833                         el.innerHTML = html;
5834                         return el.lastChild;
5835                     }
5836                 case "afterend":
5837                     range.setStartAfter(el);
5838                     frag = range.createContextualFragment(html);
5839                     el.parentNode.insertBefore(frag, el.nextSibling);
5840                     return el.nextSibling;
5841                 }
5842                 throw 'Illegal insertion point -> "' + where + '"';
5843         },
5844     
5845         /**
5846          * Creates new Dom element(s) and inserts them before el
5847          * @param {String/HTMLElement/Element} el The context element
5848          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5849          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5850          * @return {HTMLElement/Roo.Element} The new node
5851          */
5852         insertBefore : function(el, o, returnElement){
5853             return this.doInsert(el, o, returnElement, "beforeBegin");
5854         },
5855     
5856         /**
5857          * Creates new Dom element(s) and inserts them after el
5858          * @param {String/HTMLElement/Element} el The context element
5859          * @param {Object} o The Dom object spec (and children)
5860          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5861          * @return {HTMLElement/Roo.Element} The new node
5862          */
5863         insertAfter : function(el, o, returnElement){
5864             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5865         },
5866     
5867         /**
5868          * Creates new Dom element(s) and inserts them as the first child of el
5869          * @param {String/HTMLElement/Element} el The context element
5870          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5871          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5872          * @return {HTMLElement/Roo.Element} The new node
5873          */
5874         insertFirst : function(el, o, returnElement){
5875             return this.doInsert(el, o, returnElement, "afterBegin");
5876         },
5877     
5878         // private
5879         doInsert : function(el, o, returnElement, pos, sibling){
5880             el = Roo.getDom(el);
5881             var newNode;
5882             if(this.useDom || o.ns){
5883                 newNode = createDom(o, null);
5884                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5885             }else{
5886                 var html = createHtml(o);
5887                 newNode = this.insertHtml(pos, el, html);
5888             }
5889             return returnElement ? Roo.get(newNode, true) : newNode;
5890         },
5891     
5892         /**
5893          * Creates new Dom element(s) and appends them to el
5894          * @param {String/HTMLElement/Element} el The context element
5895          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5896          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5897          * @return {HTMLElement/Roo.Element} The new node
5898          */
5899         append : function(el, o, returnElement){
5900             el = Roo.getDom(el);
5901             var newNode;
5902             if(this.useDom || o.ns){
5903                 newNode = createDom(o, null);
5904                 el.appendChild(newNode);
5905             }else{
5906                 var html = createHtml(o);
5907                 newNode = this.insertHtml("beforeEnd", el, html);
5908             }
5909             return returnElement ? Roo.get(newNode, true) : newNode;
5910         },
5911     
5912         /**
5913          * Creates new Dom element(s) and overwrites the contents of el with them
5914          * @param {String/HTMLElement/Element} el The context element
5915          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5916          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5917          * @return {HTMLElement/Roo.Element} The new node
5918          */
5919         overwrite : function(el, o, returnElement)
5920         {
5921             el = Roo.getDom(el);
5922             if (o.ns) {
5923               
5924                 while (el.childNodes.length) {
5925                     el.removeChild(el.firstChild);
5926                 }
5927                 createDom(o, el);
5928             } else {
5929                 el.innerHTML = createHtml(o);   
5930             }
5931             
5932             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5933         },
5934     
5935         /**
5936          * Creates a new Roo.DomHelper.Template from the Dom object spec
5937          * @param {Object} o The Dom object spec (and children)
5938          * @return {Roo.DomHelper.Template} The new template
5939          */
5940         createTemplate : function(o){
5941             var html = createHtml(o);
5942             return new Roo.Template(html);
5943         },
5944          /**
5945          * Updates the first element with the spec from the o (replacing if necessary)
5946          * This iterates through the children, and updates attributes / children etc..
5947          * @param {String/HTMLElement/Element} el The context element
5948          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5949          */
5950         
5951         update : function(el, o)
5952         {
5953             updateNode(Roo.getDom(el), createDom(o));
5954             
5955         }
5956         
5957         
5958     };
5959 }();
5960 /*
5961  * Based on:
5962  * Ext JS Library 1.1.1
5963  * Copyright(c) 2006-2007, Ext JS, LLC.
5964  *
5965  * Originally Released Under LGPL - original licence link has changed is not relivant.
5966  *
5967  * Fork - LGPL
5968  * <script type="text/javascript">
5969  */
5970  
5971 /**
5972 * @class Roo.Template
5973 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5974 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5975 * Usage:
5976 <pre><code>
5977 var t = new Roo.Template({
5978     html :  '&lt;div name="{id}"&gt;' + 
5979         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5980         '&lt;/div&gt;',
5981     myformat: function (value, allValues) {
5982         return 'XX' + value;
5983     }
5984 });
5985 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5986 </code></pre>
5987 * For more information see this blog post with examples:
5988 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5989      - Create Elements using DOM, HTML fragments and Templates</a>. 
5990 * @constructor
5991 * @param {Object} cfg - Configuration object.
5992 */
5993 Roo.Template = function(cfg){
5994     // BC!
5995     if(cfg instanceof Array){
5996         cfg = cfg.join("");
5997     }else if(arguments.length > 1){
5998         cfg = Array.prototype.join.call(arguments, "");
5999     }
6000     
6001     
6002     if (typeof(cfg) == 'object') {
6003         Roo.apply(this,cfg)
6004     } else {
6005         // bc
6006         this.html = cfg;
6007     }
6008     if (this.url) {
6009         this.load();
6010     }
6011     
6012 };
6013 Roo.Template.prototype = {
6014     
6015     /**
6016      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
6017      */
6018     onLoad : false,
6019     
6020     
6021     /**
6022      * @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..
6023      *                    it should be fixed so that template is observable...
6024      */
6025     url : false,
6026     /**
6027      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
6028      */
6029     html : '',
6030     
6031     
6032     compiled : false,
6033     loaded : false,
6034     /**
6035      * Returns an HTML fragment of this template with the specified values applied.
6036      * @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'})
6037      * @return {String} The HTML fragment
6038      */
6039     
6040    
6041     
6042     applyTemplate : function(values){
6043         //Roo.log(["applyTemplate", values]);
6044         try {
6045            
6046             if(this.compiled){
6047                 return this.compiled(values);
6048             }
6049             var useF = this.disableFormats !== true;
6050             var fm = Roo.util.Format, tpl = this;
6051             var fn = function(m, name, format, args){
6052                 if(format && useF){
6053                     if(format.substr(0, 5) == "this."){
6054                         return tpl.call(format.substr(5), values[name], values);
6055                     }else{
6056                         if(args){
6057                             // quoted values are required for strings in compiled templates, 
6058                             // but for non compiled we need to strip them
6059                             // quoted reversed for jsmin
6060                             var re = /^\s*['"](.*)["']\s*$/;
6061                             args = args.split(',');
6062                             for(var i = 0, len = args.length; i < len; i++){
6063                                 args[i] = args[i].replace(re, "$1");
6064                             }
6065                             args = [values[name]].concat(args);
6066                         }else{
6067                             args = [values[name]];
6068                         }
6069                         return fm[format].apply(fm, args);
6070                     }
6071                 }else{
6072                     return values[name] !== undefined ? values[name] : "";
6073                 }
6074             };
6075             return this.html.replace(this.re, fn);
6076         } catch (e) {
6077             Roo.log(e);
6078             throw e;
6079         }
6080          
6081     },
6082     
6083     loading : false,
6084       
6085     load : function ()
6086     {
6087          
6088         if (this.loading) {
6089             return;
6090         }
6091         var _t = this;
6092         
6093         this.loading = true;
6094         this.compiled = false;
6095         
6096         var cx = new Roo.data.Connection();
6097         cx.request({
6098             url : this.url,
6099             method : 'GET',
6100             success : function (response) {
6101                 _t.loading = false;
6102                 _t.url = false;
6103                 
6104                 _t.set(response.responseText,true);
6105                 _t.loaded = true;
6106                 if (_t.onLoad) {
6107                     _t.onLoad();
6108                 }
6109              },
6110             failure : function(response) {
6111                 Roo.log("Template failed to load from " + _t.url);
6112                 _t.loading = false;
6113             }
6114         });
6115     },
6116
6117     /**
6118      * Sets the HTML used as the template and optionally compiles it.
6119      * @param {String} html
6120      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
6121      * @return {Roo.Template} this
6122      */
6123     set : function(html, compile){
6124         this.html = html;
6125         this.compiled = false;
6126         if(compile){
6127             this.compile();
6128         }
6129         return this;
6130     },
6131     
6132     /**
6133      * True to disable format functions (defaults to false)
6134      * @type Boolean
6135      */
6136     disableFormats : false,
6137     
6138     /**
6139     * The regular expression used to match template variables 
6140     * @type RegExp
6141     * @property 
6142     */
6143     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
6144     
6145     /**
6146      * Compiles the template into an internal function, eliminating the RegEx overhead.
6147      * @return {Roo.Template} this
6148      */
6149     compile : function(){
6150         var fm = Roo.util.Format;
6151         var useF = this.disableFormats !== true;
6152         var sep = Roo.isGecko ? "+" : ",";
6153         var fn = function(m, name, format, args){
6154             if(format && useF){
6155                 args = args ? ',' + args : "";
6156                 if(format.substr(0, 5) != "this."){
6157                     format = "fm." + format + '(';
6158                 }else{
6159                     format = 'this.call("'+ format.substr(5) + '", ';
6160                     args = ", values";
6161                 }
6162             }else{
6163                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
6164             }
6165             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
6166         };
6167         var body;
6168         // branched to use + in gecko and [].join() in others
6169         if(Roo.isGecko){
6170             body = "this.compiled = function(values){ return '" +
6171                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
6172                     "';};";
6173         }else{
6174             body = ["this.compiled = function(values){ return ['"];
6175             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
6176             body.push("'].join('');};");
6177             body = body.join('');
6178         }
6179         /**
6180          * eval:var:values
6181          * eval:var:fm
6182          */
6183         eval(body);
6184         return this;
6185     },
6186     
6187     // private function used to call members
6188     call : function(fnName, value, allValues){
6189         return this[fnName](value, allValues);
6190     },
6191     
6192     /**
6193      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
6194      * @param {String/HTMLElement/Roo.Element} el The context element
6195      * @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'})
6196      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6197      * @return {HTMLElement/Roo.Element} The new node or Element
6198      */
6199     insertFirst: function(el, values, returnElement){
6200         return this.doInsert('afterBegin', el, values, returnElement);
6201     },
6202
6203     /**
6204      * Applies the supplied values to the template and inserts the new node(s) before el.
6205      * @param {String/HTMLElement/Roo.Element} el The context element
6206      * @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'})
6207      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6208      * @return {HTMLElement/Roo.Element} The new node or Element
6209      */
6210     insertBefore: function(el, values, returnElement){
6211         return this.doInsert('beforeBegin', el, values, returnElement);
6212     },
6213
6214     /**
6215      * Applies the supplied values to the template and inserts the new node(s) after el.
6216      * @param {String/HTMLElement/Roo.Element} el The context element
6217      * @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'})
6218      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6219      * @return {HTMLElement/Roo.Element} The new node or Element
6220      */
6221     insertAfter : function(el, values, returnElement){
6222         return this.doInsert('afterEnd', el, values, returnElement);
6223     },
6224     
6225     /**
6226      * Applies the supplied values to the template and appends the new node(s) to el.
6227      * @param {String/HTMLElement/Roo.Element} el The context element
6228      * @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'})
6229      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6230      * @return {HTMLElement/Roo.Element} The new node or Element
6231      */
6232     append : function(el, values, returnElement){
6233         return this.doInsert('beforeEnd', el, values, returnElement);
6234     },
6235
6236     doInsert : function(where, el, values, returnEl){
6237         el = Roo.getDom(el);
6238         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6239         return returnEl ? Roo.get(newNode, true) : newNode;
6240     },
6241
6242     /**
6243      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6244      * @param {String/HTMLElement/Roo.Element} el The context element
6245      * @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'})
6246      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6247      * @return {HTMLElement/Roo.Element} The new node or Element
6248      */
6249     overwrite : function(el, values, returnElement){
6250         el = Roo.getDom(el);
6251         el.innerHTML = this.applyTemplate(values);
6252         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6253     }
6254 };
6255 /**
6256  * Alias for {@link #applyTemplate}
6257  * @method
6258  */
6259 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6260
6261 // backwards compat
6262 Roo.DomHelper.Template = Roo.Template;
6263
6264 /**
6265  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6266  * @param {String/HTMLElement} el A DOM element or its id
6267  * @returns {Roo.Template} The created template
6268  * @static
6269  */
6270 Roo.Template.from = function(el){
6271     el = Roo.getDom(el);
6272     return new Roo.Template(el.value || el.innerHTML);
6273 };/*
6274  * Based on:
6275  * Ext JS Library 1.1.1
6276  * Copyright(c) 2006-2007, Ext JS, LLC.
6277  *
6278  * Originally Released Under LGPL - original licence link has changed is not relivant.
6279  *
6280  * Fork - LGPL
6281  * <script type="text/javascript">
6282  */
6283  
6284
6285 /*
6286  * This is code is also distributed under MIT license for use
6287  * with jQuery and prototype JavaScript libraries.
6288  */
6289 /**
6290  * @class Roo.DomQuery
6291 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).
6292 <p>
6293 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>
6294
6295 <p>
6296 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.
6297 </p>
6298 <h4>Element Selectors:</h4>
6299 <ul class="list">
6300     <li> <b>*</b> any element</li>
6301     <li> <b>E</b> an element with the tag E</li>
6302     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6303     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6304     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6305     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6306 </ul>
6307 <h4>Attribute Selectors:</h4>
6308 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6309 <ul class="list">
6310     <li> <b>E[foo]</b> has an attribute "foo"</li>
6311     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6312     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6313     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6314     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6315     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6316     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6317 </ul>
6318 <h4>Pseudo Classes:</h4>
6319 <ul class="list">
6320     <li> <b>E:first-child</b> E is the first child of its parent</li>
6321     <li> <b>E:last-child</b> E is the last child of its parent</li>
6322     <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>
6323     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6324     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6325     <li> <b>E:only-child</b> E is the only child of its parent</li>
6326     <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>
6327     <li> <b>E:first</b> the first E in the resultset</li>
6328     <li> <b>E:last</b> the last E in the resultset</li>
6329     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6330     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6331     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6332     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6333     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6334     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6335     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6336     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6337     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6338 </ul>
6339 <h4>CSS Value Selectors:</h4>
6340 <ul class="list">
6341     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6342     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6343     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6344     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6345     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6346     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6347 </ul>
6348  * @static
6349  */
6350 Roo.DomQuery = function(){
6351     var cache = {}, simpleCache = {}, valueCache = {};
6352     var nonSpace = /\S/;
6353     var trimRe = /^\s+|\s+$/g;
6354     var tplRe = /\{(\d+)\}/g;
6355     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6356     var tagTokenRe = /^(#)?([\w-\*]+)/;
6357     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6358
6359     function child(p, index){
6360         var i = 0;
6361         var n = p.firstChild;
6362         while(n){
6363             if(n.nodeType == 1){
6364                if(++i == index){
6365                    return n;
6366                }
6367             }
6368             n = n.nextSibling;
6369         }
6370         return null;
6371     };
6372
6373     function next(n){
6374         while((n = n.nextSibling) && n.nodeType != 1);
6375         return n;
6376     };
6377
6378     function prev(n){
6379         while((n = n.previousSibling) && n.nodeType != 1);
6380         return n;
6381     };
6382
6383     function children(d){
6384         var n = d.firstChild, ni = -1;
6385             while(n){
6386                 var nx = n.nextSibling;
6387                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6388                     d.removeChild(n);
6389                 }else{
6390                     n.nodeIndex = ++ni;
6391                 }
6392                 n = nx;
6393             }
6394             return this;
6395         };
6396
6397     function byClassName(c, a, v){
6398         if(!v){
6399             return c;
6400         }
6401         var r = [], ri = -1, cn;
6402         for(var i = 0, ci; ci = c[i]; i++){
6403             
6404             
6405             if((' '+
6406                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6407                  +' ').indexOf(v) != -1){
6408                 r[++ri] = ci;
6409             }
6410         }
6411         return r;
6412     };
6413
6414     function attrValue(n, attr){
6415         if(!n.tagName && typeof n.length != "undefined"){
6416             n = n[0];
6417         }
6418         if(!n){
6419             return null;
6420         }
6421         if(attr == "for"){
6422             return n.htmlFor;
6423         }
6424         if(attr == "class" || attr == "className"){
6425             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6426         }
6427         return n.getAttribute(attr) || n[attr];
6428
6429     };
6430
6431     function getNodes(ns, mode, tagName){
6432         var result = [], ri = -1, cs;
6433         if(!ns){
6434             return result;
6435         }
6436         tagName = tagName || "*";
6437         if(typeof ns.getElementsByTagName != "undefined"){
6438             ns = [ns];
6439         }
6440         if(!mode){
6441             for(var i = 0, ni; ni = ns[i]; i++){
6442                 cs = ni.getElementsByTagName(tagName);
6443                 for(var j = 0, ci; ci = cs[j]; j++){
6444                     result[++ri] = ci;
6445                 }
6446             }
6447         }else if(mode == "/" || mode == ">"){
6448             var utag = tagName.toUpperCase();
6449             for(var i = 0, ni, cn; ni = ns[i]; i++){
6450                 cn = ni.children || ni.childNodes;
6451                 for(var j = 0, cj; cj = cn[j]; j++){
6452                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6453                         result[++ri] = cj;
6454                     }
6455                 }
6456             }
6457         }else if(mode == "+"){
6458             var utag = tagName.toUpperCase();
6459             for(var i = 0, n; n = ns[i]; i++){
6460                 while((n = n.nextSibling) && n.nodeType != 1);
6461                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6462                     result[++ri] = n;
6463                 }
6464             }
6465         }else if(mode == "~"){
6466             for(var i = 0, n; n = ns[i]; i++){
6467                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6468                 if(n){
6469                     result[++ri] = n;
6470                 }
6471             }
6472         }
6473         return result;
6474     };
6475
6476     function concat(a, b){
6477         if(b.slice){
6478             return a.concat(b);
6479         }
6480         for(var i = 0, l = b.length; i < l; i++){
6481             a[a.length] = b[i];
6482         }
6483         return a;
6484     }
6485
6486     function byTag(cs, tagName){
6487         if(cs.tagName || cs == document){
6488             cs = [cs];
6489         }
6490         if(!tagName){
6491             return cs;
6492         }
6493         var r = [], ri = -1;
6494         tagName = tagName.toLowerCase();
6495         for(var i = 0, ci; ci = cs[i]; i++){
6496             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6497                 r[++ri] = ci;
6498             }
6499         }
6500         return r;
6501     };
6502
6503     function byId(cs, attr, id){
6504         if(cs.tagName || cs == document){
6505             cs = [cs];
6506         }
6507         if(!id){
6508             return cs;
6509         }
6510         var r = [], ri = -1;
6511         for(var i = 0,ci; ci = cs[i]; i++){
6512             if(ci && ci.id == id){
6513                 r[++ri] = ci;
6514                 return r;
6515             }
6516         }
6517         return r;
6518     };
6519
6520     function byAttribute(cs, attr, value, op, custom){
6521         var r = [], ri = -1, st = custom=="{";
6522         var f = Roo.DomQuery.operators[op];
6523         for(var i = 0, ci; ci = cs[i]; i++){
6524             var a;
6525             if(st){
6526                 a = Roo.DomQuery.getStyle(ci, attr);
6527             }
6528             else if(attr == "class" || attr == "className"){
6529                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6530             }else if(attr == "for"){
6531                 a = ci.htmlFor;
6532             }else if(attr == "href"){
6533                 a = ci.getAttribute("href", 2);
6534             }else{
6535                 a = ci.getAttribute(attr);
6536             }
6537             if((f && f(a, value)) || (!f && a)){
6538                 r[++ri] = ci;
6539             }
6540         }
6541         return r;
6542     };
6543
6544     function byPseudo(cs, name, value){
6545         return Roo.DomQuery.pseudos[name](cs, value);
6546     };
6547
6548     // This is for IE MSXML which does not support expandos.
6549     // IE runs the same speed using setAttribute, however FF slows way down
6550     // and Safari completely fails so they need to continue to use expandos.
6551     var isIE = window.ActiveXObject ? true : false;
6552
6553     // this eval is stop the compressor from
6554     // renaming the variable to something shorter
6555     
6556     /** eval:var:batch */
6557     var batch = 30803; 
6558
6559     var key = 30803;
6560
6561     function nodupIEXml(cs){
6562         var d = ++key;
6563         cs[0].setAttribute("_nodup", d);
6564         var r = [cs[0]];
6565         for(var i = 1, len = cs.length; i < len; i++){
6566             var c = cs[i];
6567             if(!c.getAttribute("_nodup") != d){
6568                 c.setAttribute("_nodup", d);
6569                 r[r.length] = c;
6570             }
6571         }
6572         for(var i = 0, len = cs.length; i < len; i++){
6573             cs[i].removeAttribute("_nodup");
6574         }
6575         return r;
6576     }
6577
6578     function nodup(cs){
6579         if(!cs){
6580             return [];
6581         }
6582         var len = cs.length, c, i, r = cs, cj, ri = -1;
6583         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6584             return cs;
6585         }
6586         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6587             return nodupIEXml(cs);
6588         }
6589         var d = ++key;
6590         cs[0]._nodup = d;
6591         for(i = 1; c = cs[i]; i++){
6592             if(c._nodup != d){
6593                 c._nodup = d;
6594             }else{
6595                 r = [];
6596                 for(var j = 0; j < i; j++){
6597                     r[++ri] = cs[j];
6598                 }
6599                 for(j = i+1; cj = cs[j]; j++){
6600                     if(cj._nodup != d){
6601                         cj._nodup = d;
6602                         r[++ri] = cj;
6603                     }
6604                 }
6605                 return r;
6606             }
6607         }
6608         return r;
6609     }
6610
6611     function quickDiffIEXml(c1, c2){
6612         var d = ++key;
6613         for(var i = 0, len = c1.length; i < len; i++){
6614             c1[i].setAttribute("_qdiff", d);
6615         }
6616         var r = [];
6617         for(var i = 0, len = c2.length; i < len; i++){
6618             if(c2[i].getAttribute("_qdiff") != d){
6619                 r[r.length] = c2[i];
6620             }
6621         }
6622         for(var i = 0, len = c1.length; i < len; i++){
6623            c1[i].removeAttribute("_qdiff");
6624         }
6625         return r;
6626     }
6627
6628     function quickDiff(c1, c2){
6629         var len1 = c1.length;
6630         if(!len1){
6631             return c2;
6632         }
6633         if(isIE && c1[0].selectSingleNode){
6634             return quickDiffIEXml(c1, c2);
6635         }
6636         var d = ++key;
6637         for(var i = 0; i < len1; i++){
6638             c1[i]._qdiff = d;
6639         }
6640         var r = [];
6641         for(var i = 0, len = c2.length; i < len; i++){
6642             if(c2[i]._qdiff != d){
6643                 r[r.length] = c2[i];
6644             }
6645         }
6646         return r;
6647     }
6648
6649     function quickId(ns, mode, root, id){
6650         if(ns == root){
6651            var d = root.ownerDocument || root;
6652            return d.getElementById(id);
6653         }
6654         ns = getNodes(ns, mode, "*");
6655         return byId(ns, null, id);
6656     }
6657
6658     return {
6659         getStyle : function(el, name){
6660             return Roo.fly(el).getStyle(name);
6661         },
6662         /**
6663          * Compiles a selector/xpath query into a reusable function. The returned function
6664          * takes one parameter "root" (optional), which is the context node from where the query should start.
6665          * @param {String} selector The selector/xpath query
6666          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6667          * @return {Function}
6668          */
6669         compile : function(path, type){
6670             type = type || "select";
6671             
6672             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6673             var q = path, mode, lq;
6674             var tk = Roo.DomQuery.matchers;
6675             var tklen = tk.length;
6676             var mm;
6677
6678             // accept leading mode switch
6679             var lmode = q.match(modeRe);
6680             if(lmode && lmode[1]){
6681                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6682                 q = q.replace(lmode[1], "");
6683             }
6684             // strip leading slashes
6685             while(path.substr(0, 1)=="/"){
6686                 path = path.substr(1);
6687             }
6688
6689             while(q && lq != q){
6690                 lq = q;
6691                 var tm = q.match(tagTokenRe);
6692                 if(type == "select"){
6693                     if(tm){
6694                         if(tm[1] == "#"){
6695                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6696                         }else{
6697                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6698                         }
6699                         q = q.replace(tm[0], "");
6700                     }else if(q.substr(0, 1) != '@'){
6701                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6702                     }
6703                 }else{
6704                     if(tm){
6705                         if(tm[1] == "#"){
6706                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6707                         }else{
6708                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6709                         }
6710                         q = q.replace(tm[0], "");
6711                     }
6712                 }
6713                 while(!(mm = q.match(modeRe))){
6714                     var matched = false;
6715                     for(var j = 0; j < tklen; j++){
6716                         var t = tk[j];
6717                         var m = q.match(t.re);
6718                         if(m){
6719                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6720                                                     return m[i];
6721                                                 });
6722                             q = q.replace(m[0], "");
6723                             matched = true;
6724                             break;
6725                         }
6726                     }
6727                     // prevent infinite loop on bad selector
6728                     if(!matched){
6729                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6730                     }
6731                 }
6732                 if(mm[1]){
6733                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6734                     q = q.replace(mm[1], "");
6735                 }
6736             }
6737             fn[fn.length] = "return nodup(n);\n}";
6738             
6739              /** 
6740               * list of variables that need from compression as they are used by eval.
6741              *  eval:var:batch 
6742              *  eval:var:nodup
6743              *  eval:var:byTag
6744              *  eval:var:ById
6745              *  eval:var:getNodes
6746              *  eval:var:quickId
6747              *  eval:var:mode
6748              *  eval:var:root
6749              *  eval:var:n
6750              *  eval:var:byClassName
6751              *  eval:var:byPseudo
6752              *  eval:var:byAttribute
6753              *  eval:var:attrValue
6754              * 
6755              **/ 
6756             eval(fn.join(""));
6757             return f;
6758         },
6759
6760         /**
6761          * Selects a group of elements.
6762          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6763          * @param {Node} root (optional) The start of the query (defaults to document).
6764          * @return {Array}
6765          */
6766         select : function(path, root, type){
6767             if(!root || root == document){
6768                 root = document;
6769             }
6770             if(typeof root == "string"){
6771                 root = document.getElementById(root);
6772             }
6773             var paths = path.split(",");
6774             var results = [];
6775             for(var i = 0, len = paths.length; i < len; i++){
6776                 var p = paths[i].replace(trimRe, "");
6777                 if(!cache[p]){
6778                     cache[p] = Roo.DomQuery.compile(p);
6779                     if(!cache[p]){
6780                         throw p + " is not a valid selector";
6781                     }
6782                 }
6783                 var result = cache[p](root);
6784                 if(result && result != document){
6785                     results = results.concat(result);
6786                 }
6787             }
6788             if(paths.length > 1){
6789                 return nodup(results);
6790             }
6791             return results;
6792         },
6793
6794         /**
6795          * Selects a single element.
6796          * @param {String} selector The selector/xpath query
6797          * @param {Node} root (optional) The start of the query (defaults to document).
6798          * @return {Element}
6799          */
6800         selectNode : function(path, root){
6801             return Roo.DomQuery.select(path, root)[0];
6802         },
6803
6804         /**
6805          * Selects the value of a node, optionally replacing null with the defaultValue.
6806          * @param {String} selector The selector/xpath query
6807          * @param {Node} root (optional) The start of the query (defaults to document).
6808          * @param {String} defaultValue
6809          */
6810         selectValue : function(path, root, defaultValue){
6811             path = path.replace(trimRe, "");
6812             if(!valueCache[path]){
6813                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6814             }
6815             var n = valueCache[path](root);
6816             n = n[0] ? n[0] : n;
6817             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6818             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6819         },
6820
6821         /**
6822          * Selects the value of a node, parsing integers and floats.
6823          * @param {String} selector The selector/xpath query
6824          * @param {Node} root (optional) The start of the query (defaults to document).
6825          * @param {Number} defaultValue
6826          * @return {Number}
6827          */
6828         selectNumber : function(path, root, defaultValue){
6829             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6830             return parseFloat(v);
6831         },
6832
6833         /**
6834          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6835          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6836          * @param {String} selector The simple selector to test
6837          * @return {Boolean}
6838          */
6839         is : function(el, ss){
6840             if(typeof el == "string"){
6841                 el = document.getElementById(el);
6842             }
6843             var isArray = (el instanceof Array);
6844             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6845             return isArray ? (result.length == el.length) : (result.length > 0);
6846         },
6847
6848         /**
6849          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6850          * @param {Array} el An array of elements to filter
6851          * @param {String} selector The simple selector to test
6852          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6853          * the selector instead of the ones that match
6854          * @return {Array}
6855          */
6856         filter : function(els, ss, nonMatches){
6857             ss = ss.replace(trimRe, "");
6858             if(!simpleCache[ss]){
6859                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6860             }
6861             var result = simpleCache[ss](els);
6862             return nonMatches ? quickDiff(result, els) : result;
6863         },
6864
6865         /**
6866          * Collection of matching regular expressions and code snippets.
6867          */
6868         matchers : [{
6869                 re: /^\.([\w-]+)/,
6870                 select: 'n = byClassName(n, null, " {1} ");'
6871             }, {
6872                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6873                 select: 'n = byPseudo(n, "{1}", "{2}");'
6874             },{
6875                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6876                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6877             }, {
6878                 re: /^#([\w-]+)/,
6879                 select: 'n = byId(n, null, "{1}");'
6880             },{
6881                 re: /^@([\w-]+)/,
6882                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6883             }
6884         ],
6885
6886         /**
6887          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6888          * 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;.
6889          */
6890         operators : {
6891             "=" : function(a, v){
6892                 return a == v;
6893             },
6894             "!=" : function(a, v){
6895                 return a != v;
6896             },
6897             "^=" : function(a, v){
6898                 return a && a.substr(0, v.length) == v;
6899             },
6900             "$=" : function(a, v){
6901                 return a && a.substr(a.length-v.length) == v;
6902             },
6903             "*=" : function(a, v){
6904                 return a && a.indexOf(v) !== -1;
6905             },
6906             "%=" : function(a, v){
6907                 return (a % v) == 0;
6908             },
6909             "|=" : function(a, v){
6910                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6911             },
6912             "~=" : function(a, v){
6913                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6914             }
6915         },
6916
6917         /**
6918          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6919          * and the argument (if any) supplied in the selector.
6920          */
6921         pseudos : {
6922             "first-child" : function(c){
6923                 var r = [], ri = -1, n;
6924                 for(var i = 0, ci; ci = n = c[i]; i++){
6925                     while((n = n.previousSibling) && n.nodeType != 1);
6926                     if(!n){
6927                         r[++ri] = ci;
6928                     }
6929                 }
6930                 return r;
6931             },
6932
6933             "last-child" : function(c){
6934                 var r = [], ri = -1, n;
6935                 for(var i = 0, ci; ci = n = c[i]; i++){
6936                     while((n = n.nextSibling) && n.nodeType != 1);
6937                     if(!n){
6938                         r[++ri] = ci;
6939                     }
6940                 }
6941                 return r;
6942             },
6943
6944             "nth-child" : function(c, a) {
6945                 var r = [], ri = -1;
6946                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6947                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6948                 for(var i = 0, n; n = c[i]; i++){
6949                     var pn = n.parentNode;
6950                     if (batch != pn._batch) {
6951                         var j = 0;
6952                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6953                             if(cn.nodeType == 1){
6954                                cn.nodeIndex = ++j;
6955                             }
6956                         }
6957                         pn._batch = batch;
6958                     }
6959                     if (f == 1) {
6960                         if (l == 0 || n.nodeIndex == l){
6961                             r[++ri] = n;
6962                         }
6963                     } else if ((n.nodeIndex + l) % f == 0){
6964                         r[++ri] = n;
6965                     }
6966                 }
6967
6968                 return r;
6969             },
6970
6971             "only-child" : function(c){
6972                 var r = [], ri = -1;;
6973                 for(var i = 0, ci; ci = c[i]; i++){
6974                     if(!prev(ci) && !next(ci)){
6975                         r[++ri] = ci;
6976                     }
6977                 }
6978                 return r;
6979             },
6980
6981             "empty" : function(c){
6982                 var r = [], ri = -1;
6983                 for(var i = 0, ci; ci = c[i]; i++){
6984                     var cns = ci.childNodes, j = 0, cn, empty = true;
6985                     while(cn = cns[j]){
6986                         ++j;
6987                         if(cn.nodeType == 1 || cn.nodeType == 3){
6988                             empty = false;
6989                             break;
6990                         }
6991                     }
6992                     if(empty){
6993                         r[++ri] = ci;
6994                     }
6995                 }
6996                 return r;
6997             },
6998
6999             "contains" : function(c, v){
7000                 var r = [], ri = -1;
7001                 for(var i = 0, ci; ci = c[i]; i++){
7002                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
7003                         r[++ri] = ci;
7004                     }
7005                 }
7006                 return r;
7007             },
7008
7009             "nodeValue" : function(c, v){
7010                 var r = [], ri = -1;
7011                 for(var i = 0, ci; ci = c[i]; i++){
7012                     if(ci.firstChild && ci.firstChild.nodeValue == v){
7013                         r[++ri] = ci;
7014                     }
7015                 }
7016                 return r;
7017             },
7018
7019             "checked" : function(c){
7020                 var r = [], ri = -1;
7021                 for(var i = 0, ci; ci = c[i]; i++){
7022                     if(ci.checked == true){
7023                         r[++ri] = ci;
7024                     }
7025                 }
7026                 return r;
7027             },
7028
7029             "not" : function(c, ss){
7030                 return Roo.DomQuery.filter(c, ss, true);
7031             },
7032
7033             "odd" : function(c){
7034                 return this["nth-child"](c, "odd");
7035             },
7036
7037             "even" : function(c){
7038                 return this["nth-child"](c, "even");
7039             },
7040
7041             "nth" : function(c, a){
7042                 return c[a-1] || [];
7043             },
7044
7045             "first" : function(c){
7046                 return c[0] || [];
7047             },
7048
7049             "last" : function(c){
7050                 return c[c.length-1] || [];
7051             },
7052
7053             "has" : function(c, ss){
7054                 var s = Roo.DomQuery.select;
7055                 var r = [], ri = -1;
7056                 for(var i = 0, ci; ci = c[i]; i++){
7057                     if(s(ss, ci).length > 0){
7058                         r[++ri] = ci;
7059                     }
7060                 }
7061                 return r;
7062             },
7063
7064             "next" : function(c, ss){
7065                 var is = Roo.DomQuery.is;
7066                 var r = [], ri = -1;
7067                 for(var i = 0, ci; ci = c[i]; i++){
7068                     var n = next(ci);
7069                     if(n && is(n, ss)){
7070                         r[++ri] = ci;
7071                     }
7072                 }
7073                 return r;
7074             },
7075
7076             "prev" : function(c, ss){
7077                 var is = Roo.DomQuery.is;
7078                 var r = [], ri = -1;
7079                 for(var i = 0, ci; ci = c[i]; i++){
7080                     var n = prev(ci);
7081                     if(n && is(n, ss)){
7082                         r[++ri] = ci;
7083                     }
7084                 }
7085                 return r;
7086             }
7087         }
7088     };
7089 }();
7090
7091 /**
7092  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
7093  * @param {String} path The selector/xpath query
7094  * @param {Node} root (optional) The start of the query (defaults to document).
7095  * @return {Array}
7096  * @member Roo
7097  * @method query
7098  */
7099 Roo.query = Roo.DomQuery.select;
7100 /*
7101  * Based on:
7102  * Ext JS Library 1.1.1
7103  * Copyright(c) 2006-2007, Ext JS, LLC.
7104  *
7105  * Originally Released Under LGPL - original licence link has changed is not relivant.
7106  *
7107  * Fork - LGPL
7108  * <script type="text/javascript">
7109  */
7110
7111 /**
7112  * @class Roo.util.Observable
7113  * Base class that provides a common interface for publishing events. Subclasses are expected to
7114  * to have a property "events" with all the events defined.<br>
7115  * For example:
7116  * <pre><code>
7117  Employee = function(name){
7118     this.name = name;
7119     this.addEvents({
7120         "fired" : true,
7121         "quit" : true
7122     });
7123  }
7124  Roo.extend(Employee, Roo.util.Observable);
7125 </code></pre>
7126  * @param {Object} config properties to use (incuding events / listeners)
7127  */
7128
7129 Roo.util.Observable = function(cfg){
7130     
7131     cfg = cfg|| {};
7132     this.addEvents(cfg.events || {});
7133     if (cfg.events) {
7134         delete cfg.events; // make sure
7135     }
7136      
7137     Roo.apply(this, cfg);
7138     
7139     if(this.listeners){
7140         this.on(this.listeners);
7141         delete this.listeners;
7142     }
7143 };
7144 Roo.util.Observable.prototype = {
7145     /** 
7146  * @cfg {Object} listeners  list of events and functions to call for this object, 
7147  * For example :
7148  * <pre><code>
7149     listeners :  { 
7150        'click' : function(e) {
7151            ..... 
7152         } ,
7153         .... 
7154     } 
7155   </code></pre>
7156  */
7157     
7158     
7159     /**
7160      * Fires the specified event with the passed parameters (minus the event name).
7161      * @param {String} eventName
7162      * @param {Object...} args Variable number of parameters are passed to handlers
7163      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
7164      */
7165     fireEvent : function(){
7166         var ce = this.events[arguments[0].toLowerCase()];
7167         if(typeof ce == "object"){
7168             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
7169         }else{
7170             return true;
7171         }
7172     },
7173
7174     // private
7175     filterOptRe : /^(?:scope|delay|buffer|single)$/,
7176
7177     /**
7178      * Appends an event handler to this component
7179      * @param {String}   eventName The type of event to listen for
7180      * @param {Function} handler The method the event invokes
7181      * @param {Object}   scope (optional) The scope in which to execute the handler
7182      * function. The handler function's "this" context.
7183      * @param {Object}   options (optional) An object containing handler configuration
7184      * properties. This may contain any of the following properties:<ul>
7185      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7186      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7187      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7188      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7189      * by the specified number of milliseconds. If the event fires again within that time, the original
7190      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7191      * </ul><br>
7192      * <p>
7193      * <b>Combining Options</b><br>
7194      * Using the options argument, it is possible to combine different types of listeners:<br>
7195      * <br>
7196      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
7197                 <pre><code>
7198                 el.on('click', this.onClick, this, {
7199                         single: true,
7200                 delay: 100,
7201                 forumId: 4
7202                 });
7203                 </code></pre>
7204      * <p>
7205      * <b>Attaching multiple handlers in 1 call</b><br>
7206      * The method also allows for a single argument to be passed which is a config object containing properties
7207      * which specify multiple handlers.
7208      * <pre><code>
7209                 el.on({
7210                         'click': {
7211                         fn: this.onClick,
7212                         scope: this,
7213                         delay: 100
7214                 }, 
7215                 'mouseover': {
7216                         fn: this.onMouseOver,
7217                         scope: this
7218                 },
7219                 'mouseout': {
7220                         fn: this.onMouseOut,
7221                         scope: this
7222                 }
7223                 });
7224                 </code></pre>
7225      * <p>
7226      * Or a shorthand syntax which passes the same scope object to all handlers:
7227         <pre><code>
7228                 el.on({
7229                         'click': this.onClick,
7230                 'mouseover': this.onMouseOver,
7231                 'mouseout': this.onMouseOut,
7232                 scope: this
7233                 });
7234                 </code></pre>
7235      */
7236     addListener : function(eventName, fn, scope, o){
7237         if(typeof eventName == "object"){
7238             o = eventName;
7239             for(var e in o){
7240                 if(this.filterOptRe.test(e)){
7241                     continue;
7242                 }
7243                 if(typeof o[e] == "function"){
7244                     // shared options
7245                     this.addListener(e, o[e], o.scope,  o);
7246                 }else{
7247                     // individual options
7248                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
7249                 }
7250             }
7251             return;
7252         }
7253         o = (!o || typeof o == "boolean") ? {} : o;
7254         eventName = eventName.toLowerCase();
7255         var ce = this.events[eventName] || true;
7256         if(typeof ce == "boolean"){
7257             ce = new Roo.util.Event(this, eventName);
7258             this.events[eventName] = ce;
7259         }
7260         ce.addListener(fn, scope, o);
7261     },
7262
7263     /**
7264      * Removes a listener
7265      * @param {String}   eventName     The type of event to listen for
7266      * @param {Function} handler        The handler to remove
7267      * @param {Object}   scope  (optional) The scope (this object) for the handler
7268      */
7269     removeListener : function(eventName, fn, scope){
7270         var ce = this.events[eventName.toLowerCase()];
7271         if(typeof ce == "object"){
7272             ce.removeListener(fn, scope);
7273         }
7274     },
7275
7276     /**
7277      * Removes all listeners for this object
7278      */
7279     purgeListeners : function(){
7280         for(var evt in this.events){
7281             if(typeof this.events[evt] == "object"){
7282                  this.events[evt].clearListeners();
7283             }
7284         }
7285     },
7286
7287     relayEvents : function(o, events){
7288         var createHandler = function(ename){
7289             return function(){
7290                  
7291                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7292             };
7293         };
7294         for(var i = 0, len = events.length; i < len; i++){
7295             var ename = events[i];
7296             if(!this.events[ename]){
7297                 this.events[ename] = true;
7298             };
7299             o.on(ename, createHandler(ename), this);
7300         }
7301     },
7302
7303     /**
7304      * Used to define events on this Observable
7305      * @param {Object} object The object with the events defined
7306      */
7307     addEvents : function(o){
7308         if(!this.events){
7309             this.events = {};
7310         }
7311         Roo.applyIf(this.events, o);
7312     },
7313
7314     /**
7315      * Checks to see if this object has any listeners for a specified event
7316      * @param {String} eventName The name of the event to check for
7317      * @return {Boolean} True if the event is being listened for, else false
7318      */
7319     hasListener : function(eventName){
7320         var e = this.events[eventName];
7321         return typeof e == "object" && e.listeners.length > 0;
7322     }
7323 };
7324 /**
7325  * Appends an event handler to this element (shorthand for addListener)
7326  * @param {String}   eventName     The type of event to listen for
7327  * @param {Function} handler        The method the event invokes
7328  * @param {Object}   scope (optional) The scope in which to execute the handler
7329  * function. The handler function's "this" context.
7330  * @param {Object}   options  (optional)
7331  * @method
7332  */
7333 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7334 /**
7335  * Removes a listener (shorthand for removeListener)
7336  * @param {String}   eventName     The type of event to listen for
7337  * @param {Function} handler        The handler to remove
7338  * @param {Object}   scope  (optional) The scope (this object) for the handler
7339  * @method
7340  */
7341 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7342
7343 /**
7344  * Starts capture on the specified Observable. All events will be passed
7345  * to the supplied function with the event name + standard signature of the event
7346  * <b>before</b> the event is fired. If the supplied function returns false,
7347  * the event will not fire.
7348  * @param {Observable} o The Observable to capture
7349  * @param {Function} fn The function to call
7350  * @param {Object} scope (optional) The scope (this object) for the fn
7351  * @static
7352  */
7353 Roo.util.Observable.capture = function(o, fn, scope){
7354     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7355 };
7356
7357 /**
7358  * Removes <b>all</b> added captures from the Observable.
7359  * @param {Observable} o The Observable to release
7360  * @static
7361  */
7362 Roo.util.Observable.releaseCapture = function(o){
7363     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7364 };
7365
7366 (function(){
7367
7368     var createBuffered = function(h, o, scope){
7369         var task = new Roo.util.DelayedTask();
7370         return function(){
7371             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7372         };
7373     };
7374
7375     var createSingle = function(h, e, fn, scope){
7376         return function(){
7377             e.removeListener(fn, scope);
7378             return h.apply(scope, arguments);
7379         };
7380     };
7381
7382     var createDelayed = function(h, o, scope){
7383         return function(){
7384             var args = Array.prototype.slice.call(arguments, 0);
7385             setTimeout(function(){
7386                 h.apply(scope, args);
7387             }, o.delay || 10);
7388         };
7389     };
7390
7391     Roo.util.Event = function(obj, name){
7392         this.name = name;
7393         this.obj = obj;
7394         this.listeners = [];
7395     };
7396
7397     Roo.util.Event.prototype = {
7398         addListener : function(fn, scope, options){
7399             var o = options || {};
7400             scope = scope || this.obj;
7401             if(!this.isListening(fn, scope)){
7402                 var l = {fn: fn, scope: scope, options: o};
7403                 var h = fn;
7404                 if(o.delay){
7405                     h = createDelayed(h, o, scope);
7406                 }
7407                 if(o.single){
7408                     h = createSingle(h, this, fn, scope);
7409                 }
7410                 if(o.buffer){
7411                     h = createBuffered(h, o, scope);
7412                 }
7413                 l.fireFn = h;
7414                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7415                     this.listeners.push(l);
7416                 }else{
7417                     this.listeners = this.listeners.slice(0);
7418                     this.listeners.push(l);
7419                 }
7420             }
7421         },
7422
7423         findListener : function(fn, scope){
7424             scope = scope || this.obj;
7425             var ls = this.listeners;
7426             for(var i = 0, len = ls.length; i < len; i++){
7427                 var l = ls[i];
7428                 if(l.fn == fn && l.scope == scope){
7429                     return i;
7430                 }
7431             }
7432             return -1;
7433         },
7434
7435         isListening : function(fn, scope){
7436             return this.findListener(fn, scope) != -1;
7437         },
7438
7439         removeListener : function(fn, scope){
7440             var index;
7441             if((index = this.findListener(fn, scope)) != -1){
7442                 if(!this.firing){
7443                     this.listeners.splice(index, 1);
7444                 }else{
7445                     this.listeners = this.listeners.slice(0);
7446                     this.listeners.splice(index, 1);
7447                 }
7448                 return true;
7449             }
7450             return false;
7451         },
7452
7453         clearListeners : function(){
7454             this.listeners = [];
7455         },
7456
7457         fire : function(){
7458             var ls = this.listeners, scope, len = ls.length;
7459             if(len > 0){
7460                 this.firing = true;
7461                 var args = Array.prototype.slice.call(arguments, 0);                
7462                 for(var i = 0; i < len; i++){
7463                     var l = ls[i];
7464                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7465                         this.firing = false;
7466                         return false;
7467                     }
7468                 }
7469                 this.firing = false;
7470             }
7471             return true;
7472         }
7473     };
7474 })();/*
7475  * RooJS Library 
7476  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7477  *
7478  * Licence LGPL 
7479  *
7480  */
7481  
7482 /**
7483  * @class Roo.Document
7484  * @extends Roo.util.Observable
7485  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7486  * 
7487  * @param {Object} config the methods and properties of the 'base' class for the application.
7488  * 
7489  *  Generic Page handler - implement this to start your app..
7490  * 
7491  * eg.
7492  *  MyProject = new Roo.Document({
7493         events : {
7494             'load' : true // your events..
7495         },
7496         listeners : {
7497             'ready' : function() {
7498                 // fired on Roo.onReady()
7499             }
7500         }
7501  * 
7502  */
7503 Roo.Document = function(cfg) {
7504      
7505     this.addEvents({ 
7506         'ready' : true
7507     });
7508     Roo.util.Observable.call(this,cfg);
7509     
7510     var _this = this;
7511     
7512     Roo.onReady(function() {
7513         _this.fireEvent('ready');
7514     },null,false);
7515     
7516     
7517 }
7518
7519 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7520  * Based on:
7521  * Ext JS Library 1.1.1
7522  * Copyright(c) 2006-2007, Ext JS, LLC.
7523  *
7524  * Originally Released Under LGPL - original licence link has changed is not relivant.
7525  *
7526  * Fork - LGPL
7527  * <script type="text/javascript">
7528  */
7529
7530 /**
7531  * @class Roo.EventManager
7532  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7533  * several useful events directly.
7534  * See {@link Roo.EventObject} for more details on normalized event objects.
7535  * @static
7536  */
7537 Roo.EventManager = function(){
7538     var docReadyEvent, docReadyProcId, docReadyState = false;
7539     var resizeEvent, resizeTask, textEvent, textSize;
7540     var E = Roo.lib.Event;
7541     var D = Roo.lib.Dom;
7542
7543     
7544     
7545
7546     var fireDocReady = function(){
7547         if(!docReadyState){
7548             docReadyState = true;
7549             Roo.isReady = true;
7550             if(docReadyProcId){
7551                 clearInterval(docReadyProcId);
7552             }
7553             if(Roo.isGecko || Roo.isOpera) {
7554                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7555             }
7556             if(Roo.isIE){
7557                 var defer = document.getElementById("ie-deferred-loader");
7558                 if(defer){
7559                     defer.onreadystatechange = null;
7560                     defer.parentNode.removeChild(defer);
7561                 }
7562             }
7563             if(docReadyEvent){
7564                 docReadyEvent.fire();
7565                 docReadyEvent.clearListeners();
7566             }
7567         }
7568     };
7569     
7570     var initDocReady = function(){
7571         docReadyEvent = new Roo.util.Event();
7572         if(Roo.isGecko || Roo.isOpera) {
7573             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7574         }else if(Roo.isIE){
7575             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7576             var defer = document.getElementById("ie-deferred-loader");
7577             defer.onreadystatechange = function(){
7578                 if(this.readyState == "complete"){
7579                     fireDocReady();
7580                 }
7581             };
7582         }else if(Roo.isSafari){ 
7583             docReadyProcId = setInterval(function(){
7584                 var rs = document.readyState;
7585                 if(rs == "complete") {
7586                     fireDocReady();     
7587                  }
7588             }, 10);
7589         }
7590         // no matter what, make sure it fires on load
7591         E.on(window, "load", fireDocReady);
7592     };
7593
7594     var createBuffered = function(h, o){
7595         var task = new Roo.util.DelayedTask(h);
7596         return function(e){
7597             // create new event object impl so new events don't wipe out properties
7598             e = new Roo.EventObjectImpl(e);
7599             task.delay(o.buffer, h, null, [e]);
7600         };
7601     };
7602
7603     var createSingle = function(h, el, ename, fn){
7604         return function(e){
7605             Roo.EventManager.removeListener(el, ename, fn);
7606             h(e);
7607         };
7608     };
7609
7610     var createDelayed = function(h, o){
7611         return function(e){
7612             // create new event object impl so new events don't wipe out properties
7613             e = new Roo.EventObjectImpl(e);
7614             setTimeout(function(){
7615                 h(e);
7616             }, o.delay || 10);
7617         };
7618     };
7619     var transitionEndVal = false;
7620     
7621     var transitionEnd = function()
7622     {
7623         if (transitionEndVal) {
7624             return transitionEndVal;
7625         }
7626         var el = document.createElement('div');
7627
7628         var transEndEventNames = {
7629             WebkitTransition : 'webkitTransitionEnd',
7630             MozTransition    : 'transitionend',
7631             OTransition      : 'oTransitionEnd otransitionend',
7632             transition       : 'transitionend'
7633         };
7634     
7635         for (var name in transEndEventNames) {
7636             if (el.style[name] !== undefined) {
7637                 transitionEndVal = transEndEventNames[name];
7638                 return  transitionEndVal ;
7639             }
7640         }
7641     }
7642     
7643   
7644
7645     var listen = function(element, ename, opt, fn, scope)
7646     {
7647         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7648         fn = fn || o.fn; scope = scope || o.scope;
7649         var el = Roo.getDom(element);
7650         
7651         
7652         if(!el){
7653             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7654         }
7655         
7656         if (ename == 'transitionend') {
7657             ename = transitionEnd();
7658         }
7659         var h = function(e){
7660             e = Roo.EventObject.setEvent(e);
7661             var t;
7662             if(o.delegate){
7663                 t = e.getTarget(o.delegate, el);
7664                 if(!t){
7665                     return;
7666                 }
7667             }else{
7668                 t = e.target;
7669             }
7670             if(o.stopEvent === true){
7671                 e.stopEvent();
7672             }
7673             if(o.preventDefault === true){
7674                e.preventDefault();
7675             }
7676             if(o.stopPropagation === true){
7677                 e.stopPropagation();
7678             }
7679
7680             if(o.normalized === false){
7681                 e = e.browserEvent;
7682             }
7683
7684             fn.call(scope || el, e, t, o);
7685         };
7686         if(o.delay){
7687             h = createDelayed(h, o);
7688         }
7689         if(o.single){
7690             h = createSingle(h, el, ename, fn);
7691         }
7692         if(o.buffer){
7693             h = createBuffered(h, o);
7694         }
7695         
7696         fn._handlers = fn._handlers || [];
7697         
7698         
7699         fn._handlers.push([Roo.id(el), ename, h]);
7700         
7701         
7702          
7703         E.on(el, ename, h); // this adds the actuall listener to the object..
7704         
7705         
7706         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7707             el.addEventListener("DOMMouseScroll", h, false);
7708             E.on(window, 'unload', function(){
7709                 el.removeEventListener("DOMMouseScroll", h, false);
7710             });
7711         }
7712         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7713             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7714         }
7715         return h;
7716     };
7717
7718     var stopListening = function(el, ename, fn){
7719         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7720         if(hds){
7721             for(var i = 0, len = hds.length; i < len; i++){
7722                 var h = hds[i];
7723                 if(h[0] == id && h[1] == ename){
7724                     hd = h[2];
7725                     hds.splice(i, 1);
7726                     break;
7727                 }
7728             }
7729         }
7730         E.un(el, ename, hd);
7731         el = Roo.getDom(el);
7732         if(ename == "mousewheel" && el.addEventListener){
7733             el.removeEventListener("DOMMouseScroll", hd, false);
7734         }
7735         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7736             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7737         }
7738     };
7739
7740     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7741     
7742     var pub = {
7743         
7744         
7745         /** 
7746          * Fix for doc tools
7747          * @scope Roo.EventManager
7748          */
7749         
7750         
7751         /** 
7752          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7753          * object with a Roo.EventObject
7754          * @param {Function} fn        The method the event invokes
7755          * @param {Object}   scope    An object that becomes the scope of the handler
7756          * @param {boolean}  override If true, the obj passed in becomes
7757          *                             the execution scope of the listener
7758          * @return {Function} The wrapped function
7759          * @deprecated
7760          */
7761         wrap : function(fn, scope, override){
7762             return function(e){
7763                 Roo.EventObject.setEvent(e);
7764                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7765             };
7766         },
7767         
7768         /**
7769      * Appends an event handler to an element (shorthand for addListener)
7770      * @param {String/HTMLElement}   element        The html element or id to assign the
7771      * @param {String}   eventName The type of event to listen for
7772      * @param {Function} handler The method the event invokes
7773      * @param {Object}   scope (optional) The scope in which to execute the handler
7774      * function. The handler function's "this" context.
7775      * @param {Object}   options (optional) An object containing handler configuration
7776      * properties. This may contain any of the following properties:<ul>
7777      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7778      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7779      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7780      * <li>preventDefault {Boolean} True to prevent the default action</li>
7781      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7782      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7783      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7784      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7785      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7786      * by the specified number of milliseconds. If the event fires again within that time, the original
7787      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7788      * </ul><br>
7789      * <p>
7790      * <b>Combining Options</b><br>
7791      * Using the options argument, it is possible to combine different types of listeners:<br>
7792      * <br>
7793      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7794      * Code:<pre><code>
7795 el.on('click', this.onClick, this, {
7796     single: true,
7797     delay: 100,
7798     stopEvent : true,
7799     forumId: 4
7800 });</code></pre>
7801      * <p>
7802      * <b>Attaching multiple handlers in 1 call</b><br>
7803       * The method also allows for a single argument to be passed which is a config object containing properties
7804      * which specify multiple handlers.
7805      * <p>
7806      * Code:<pre><code>
7807 el.on({
7808     'click' : {
7809         fn: this.onClick
7810         scope: this,
7811         delay: 100
7812     },
7813     'mouseover' : {
7814         fn: this.onMouseOver
7815         scope: this
7816     },
7817     'mouseout' : {
7818         fn: this.onMouseOut
7819         scope: this
7820     }
7821 });</code></pre>
7822      * <p>
7823      * Or a shorthand syntax:<br>
7824      * Code:<pre><code>
7825 el.on({
7826     'click' : this.onClick,
7827     'mouseover' : this.onMouseOver,
7828     'mouseout' : this.onMouseOut
7829     scope: this
7830 });</code></pre>
7831      */
7832         addListener : function(element, eventName, fn, scope, options){
7833             if(typeof eventName == "object"){
7834                 var o = eventName;
7835                 for(var e in o){
7836                     if(propRe.test(e)){
7837                         continue;
7838                     }
7839                     if(typeof o[e] == "function"){
7840                         // shared options
7841                         listen(element, e, o, o[e], o.scope);
7842                     }else{
7843                         // individual options
7844                         listen(element, e, o[e]);
7845                     }
7846                 }
7847                 return;
7848             }
7849             return listen(element, eventName, options, fn, scope);
7850         },
7851         
7852         /**
7853          * Removes an event handler
7854          *
7855          * @param {String/HTMLElement}   element        The id or html element to remove the 
7856          *                             event from
7857          * @param {String}   eventName     The type of event
7858          * @param {Function} fn
7859          * @return {Boolean} True if a listener was actually removed
7860          */
7861         removeListener : function(element, eventName, fn){
7862             return stopListening(element, eventName, fn);
7863         },
7864         
7865         /**
7866          * Fires when the document is ready (before onload and before images are loaded). Can be 
7867          * accessed shorthanded Roo.onReady().
7868          * @param {Function} fn        The method the event invokes
7869          * @param {Object}   scope    An  object that becomes the scope of the handler
7870          * @param {boolean}  options
7871          */
7872         onDocumentReady : function(fn, scope, options){
7873             if(docReadyState){ // if it already fired
7874                 docReadyEvent.addListener(fn, scope, options);
7875                 docReadyEvent.fire();
7876                 docReadyEvent.clearListeners();
7877                 return;
7878             }
7879             if(!docReadyEvent){
7880                 initDocReady();
7881             }
7882             docReadyEvent.addListener(fn, scope, options);
7883         },
7884         
7885         /**
7886          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7887          * @param {Function} fn        The method the event invokes
7888          * @param {Object}   scope    An object that becomes the scope of the handler
7889          * @param {boolean}  options
7890          */
7891         onWindowResize : function(fn, scope, options)
7892         {
7893             if(!resizeEvent){
7894                 resizeEvent = new Roo.util.Event();
7895                 resizeTask = new Roo.util.DelayedTask(function(){
7896                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7897                 });
7898                 E.on(window, "resize", function()
7899                 {
7900                     if (Roo.isIE) {
7901                         resizeTask.delay(50);
7902                     } else {
7903                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7904                     }
7905                 });
7906             }
7907             resizeEvent.addListener(fn, scope, options);
7908         },
7909
7910         /**
7911          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7912          * @param {Function} fn        The method the event invokes
7913          * @param {Object}   scope    An object that becomes the scope of the handler
7914          * @param {boolean}  options
7915          */
7916         onTextResize : function(fn, scope, options){
7917             if(!textEvent){
7918                 textEvent = new Roo.util.Event();
7919                 var textEl = new Roo.Element(document.createElement('div'));
7920                 textEl.dom.className = 'x-text-resize';
7921                 textEl.dom.innerHTML = 'X';
7922                 textEl.appendTo(document.body);
7923                 textSize = textEl.dom.offsetHeight;
7924                 setInterval(function(){
7925                     if(textEl.dom.offsetHeight != textSize){
7926                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7927                     }
7928                 }, this.textResizeInterval);
7929             }
7930             textEvent.addListener(fn, scope, options);
7931         },
7932
7933         /**
7934          * Removes the passed window resize listener.
7935          * @param {Function} fn        The method the event invokes
7936          * @param {Object}   scope    The scope of handler
7937          */
7938         removeResizeListener : function(fn, scope){
7939             if(resizeEvent){
7940                 resizeEvent.removeListener(fn, scope);
7941             }
7942         },
7943
7944         // private
7945         fireResize : function(){
7946             if(resizeEvent){
7947                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7948             }   
7949         },
7950         /**
7951          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7952          */
7953         ieDeferSrc : false,
7954         /**
7955          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7956          */
7957         textResizeInterval : 50
7958     };
7959     
7960     /**
7961      * Fix for doc tools
7962      * @scopeAlias pub=Roo.EventManager
7963      */
7964     
7965      /**
7966      * Appends an event handler to an element (shorthand for addListener)
7967      * @param {String/HTMLElement}   element        The html element or id to assign the
7968      * @param {String}   eventName The type of event to listen for
7969      * @param {Function} handler The method the event invokes
7970      * @param {Object}   scope (optional) The scope in which to execute the handler
7971      * function. The handler function's "this" context.
7972      * @param {Object}   options (optional) An object containing handler configuration
7973      * properties. This may contain any of the following properties:<ul>
7974      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7975      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7976      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7977      * <li>preventDefault {Boolean} True to prevent the default action</li>
7978      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7979      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7980      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7981      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7982      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7983      * by the specified number of milliseconds. If the event fires again within that time, the original
7984      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7985      * </ul><br>
7986      * <p>
7987      * <b>Combining Options</b><br>
7988      * Using the options argument, it is possible to combine different types of listeners:<br>
7989      * <br>
7990      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7991      * Code:<pre><code>
7992 el.on('click', this.onClick, this, {
7993     single: true,
7994     delay: 100,
7995     stopEvent : true,
7996     forumId: 4
7997 });</code></pre>
7998      * <p>
7999      * <b>Attaching multiple handlers in 1 call</b><br>
8000       * The method also allows for a single argument to be passed which is a config object containing properties
8001      * which specify multiple handlers.
8002      * <p>
8003      * Code:<pre><code>
8004 el.on({
8005     'click' : {
8006         fn: this.onClick
8007         scope: this,
8008         delay: 100
8009     },
8010     'mouseover' : {
8011         fn: this.onMouseOver
8012         scope: this
8013     },
8014     'mouseout' : {
8015         fn: this.onMouseOut
8016         scope: this
8017     }
8018 });</code></pre>
8019      * <p>
8020      * Or a shorthand syntax:<br>
8021      * Code:<pre><code>
8022 el.on({
8023     'click' : this.onClick,
8024     'mouseover' : this.onMouseOver,
8025     'mouseout' : this.onMouseOut
8026     scope: this
8027 });</code></pre>
8028      */
8029     pub.on = pub.addListener;
8030     pub.un = pub.removeListener;
8031
8032     pub.stoppedMouseDownEvent = new Roo.util.Event();
8033     return pub;
8034 }();
8035 /**
8036   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
8037   * @param {Function} fn        The method the event invokes
8038   * @param {Object}   scope    An  object that becomes the scope of the handler
8039   * @param {boolean}  override If true, the obj passed in becomes
8040   *                             the execution scope of the listener
8041   * @member Roo
8042   * @method onReady
8043  */
8044 Roo.onReady = Roo.EventManager.onDocumentReady;
8045
8046 Roo.onReady(function(){
8047     var bd = Roo.get(document.body);
8048     if(!bd){ return; }
8049
8050     var cls = [
8051             Roo.isIE ? "roo-ie"
8052             : Roo.isIE11 ? "roo-ie11"
8053             : Roo.isEdge ? "roo-edge"
8054             : Roo.isGecko ? "roo-gecko"
8055             : Roo.isOpera ? "roo-opera"
8056             : Roo.isSafari ? "roo-safari" : ""];
8057
8058     if(Roo.isMac){
8059         cls.push("roo-mac");
8060     }
8061     if(Roo.isLinux){
8062         cls.push("roo-linux");
8063     }
8064     if(Roo.isIOS){
8065         cls.push("roo-ios");
8066     }
8067     if(Roo.isTouch){
8068         cls.push("roo-touch");
8069     }
8070     if(Roo.isBorderBox){
8071         cls.push('roo-border-box');
8072     }
8073     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
8074         var p = bd.dom.parentNode;
8075         if(p){
8076             p.className += ' roo-strict';
8077         }
8078     }
8079     bd.addClass(cls.join(' '));
8080 });
8081
8082 /**
8083  * @class Roo.EventObject
8084  * EventObject exposes the Yahoo! UI Event functionality directly on the object
8085  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
8086  * Example:
8087  * <pre><code>
8088  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
8089     e.preventDefault();
8090     var target = e.getTarget();
8091     ...
8092  }
8093  var myDiv = Roo.get("myDiv");
8094  myDiv.on("click", handleClick);
8095  //or
8096  Roo.EventManager.on("myDiv", 'click', handleClick);
8097  Roo.EventManager.addListener("myDiv", 'click', handleClick);
8098  </code></pre>
8099  * @static
8100  */
8101 Roo.EventObject = function(){
8102     
8103     var E = Roo.lib.Event;
8104     
8105     // safari keypress events for special keys return bad keycodes
8106     var safariKeys = {
8107         63234 : 37, // left
8108         63235 : 39, // right
8109         63232 : 38, // up
8110         63233 : 40, // down
8111         63276 : 33, // page up
8112         63277 : 34, // page down
8113         63272 : 46, // delete
8114         63273 : 36, // home
8115         63275 : 35  // end
8116     };
8117
8118     // normalize button clicks
8119     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
8120                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
8121
8122     Roo.EventObjectImpl = function(e){
8123         if(e){
8124             this.setEvent(e.browserEvent || e);
8125         }
8126     };
8127     Roo.EventObjectImpl.prototype = {
8128         /**
8129          * Used to fix doc tools.
8130          * @scope Roo.EventObject.prototype
8131          */
8132             
8133
8134         
8135         
8136         /** The normal browser event */
8137         browserEvent : null,
8138         /** The button pressed in a mouse event */
8139         button : -1,
8140         /** True if the shift key was down during the event */
8141         shiftKey : false,
8142         /** True if the control key was down during the event */
8143         ctrlKey : false,
8144         /** True if the alt key was down during the event */
8145         altKey : false,
8146
8147         /** Key constant 
8148         * @type Number */
8149         BACKSPACE : 8,
8150         /** Key constant 
8151         * @type Number */
8152         TAB : 9,
8153         /** Key constant 
8154         * @type Number */
8155         RETURN : 13,
8156         /** Key constant 
8157         * @type Number */
8158         ENTER : 13,
8159         /** Key constant 
8160         * @type Number */
8161         SHIFT : 16,
8162         /** Key constant 
8163         * @type Number */
8164         CONTROL : 17,
8165         /** Key constant 
8166         * @type Number */
8167         ESC : 27,
8168         /** Key constant 
8169         * @type Number */
8170         SPACE : 32,
8171         /** Key constant 
8172         * @type Number */
8173         PAGEUP : 33,
8174         /** Key constant 
8175         * @type Number */
8176         PAGEDOWN : 34,
8177         /** Key constant 
8178         * @type Number */
8179         END : 35,
8180         /** Key constant 
8181         * @type Number */
8182         HOME : 36,
8183         /** Key constant 
8184         * @type Number */
8185         LEFT : 37,
8186         /** Key constant 
8187         * @type Number */
8188         UP : 38,
8189         /** Key constant 
8190         * @type Number */
8191         RIGHT : 39,
8192         /** Key constant 
8193         * @type Number */
8194         DOWN : 40,
8195         /** Key constant 
8196         * @type Number */
8197         DELETE : 46,
8198         /** Key constant 
8199         * @type Number */
8200         F5 : 116,
8201
8202            /** @private */
8203         setEvent : function(e){
8204             if(e == this || (e && e.browserEvent)){ // already wrapped
8205                 return e;
8206             }
8207             this.browserEvent = e;
8208             if(e){
8209                 // normalize buttons
8210                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8211                 if(e.type == 'click' && this.button == -1){
8212                     this.button = 0;
8213                 }
8214                 this.type = e.type;
8215                 this.shiftKey = e.shiftKey;
8216                 // mac metaKey behaves like ctrlKey
8217                 this.ctrlKey = e.ctrlKey || e.metaKey;
8218                 this.altKey = e.altKey;
8219                 // in getKey these will be normalized for the mac
8220                 this.keyCode = e.keyCode;
8221                 // keyup warnings on firefox.
8222                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8223                 // cache the target for the delayed and or buffered events
8224                 this.target = E.getTarget(e);
8225                 // same for XY
8226                 this.xy = E.getXY(e);
8227             }else{
8228                 this.button = -1;
8229                 this.shiftKey = false;
8230                 this.ctrlKey = false;
8231                 this.altKey = false;
8232                 this.keyCode = 0;
8233                 this.charCode =0;
8234                 this.target = null;
8235                 this.xy = [0, 0];
8236             }
8237             return this;
8238         },
8239
8240         /**
8241          * Stop the event (preventDefault and stopPropagation)
8242          */
8243         stopEvent : function(){
8244             if(this.browserEvent){
8245                 if(this.browserEvent.type == 'mousedown'){
8246                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8247                 }
8248                 E.stopEvent(this.browserEvent);
8249             }
8250         },
8251
8252         /**
8253          * Prevents the browsers default handling of the event.
8254          */
8255         preventDefault : function(){
8256             if(this.browserEvent){
8257                 E.preventDefault(this.browserEvent);
8258             }
8259         },
8260
8261         /** @private */
8262         isNavKeyPress : function(){
8263             var k = this.keyCode;
8264             k = Roo.isSafari ? (safariKeys[k] || k) : k;
8265             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8266         },
8267
8268         isSpecialKey : function(){
8269             var k = this.keyCode;
8270             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
8271             (k == 16) || (k == 17) ||
8272             (k >= 18 && k <= 20) ||
8273             (k >= 33 && k <= 35) ||
8274             (k >= 36 && k <= 39) ||
8275             (k >= 44 && k <= 45);
8276         },
8277         /**
8278          * Cancels bubbling of the event.
8279          */
8280         stopPropagation : function(){
8281             if(this.browserEvent){
8282                 if(this.type == 'mousedown'){
8283                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8284                 }
8285                 E.stopPropagation(this.browserEvent);
8286             }
8287         },
8288
8289         /**
8290          * Gets the key code for the event.
8291          * @return {Number}
8292          */
8293         getCharCode : function(){
8294             return this.charCode || this.keyCode;
8295         },
8296
8297         /**
8298          * Returns a normalized keyCode for the event.
8299          * @return {Number} The key code
8300          */
8301         getKey : function(){
8302             var k = this.keyCode || this.charCode;
8303             return Roo.isSafari ? (safariKeys[k] || k) : k;
8304         },
8305
8306         /**
8307          * Gets the x coordinate of the event.
8308          * @return {Number}
8309          */
8310         getPageX : function(){
8311             return this.xy[0];
8312         },
8313
8314         /**
8315          * Gets the y coordinate of the event.
8316          * @return {Number}
8317          */
8318         getPageY : function(){
8319             return this.xy[1];
8320         },
8321
8322         /**
8323          * Gets the time of the event.
8324          * @return {Number}
8325          */
8326         getTime : function(){
8327             if(this.browserEvent){
8328                 return E.getTime(this.browserEvent);
8329             }
8330             return null;
8331         },
8332
8333         /**
8334          * Gets the page coordinates of the event.
8335          * @return {Array} The xy values like [x, y]
8336          */
8337         getXY : function(){
8338             return this.xy;
8339         },
8340
8341         /**
8342          * Gets the target for the event.
8343          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8344          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8345                 search as a number or element (defaults to 10 || document.body)
8346          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8347          * @return {HTMLelement}
8348          */
8349         getTarget : function(selector, maxDepth, returnEl){
8350             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8351         },
8352         /**
8353          * Gets the related target.
8354          * @return {HTMLElement}
8355          */
8356         getRelatedTarget : function(){
8357             if(this.browserEvent){
8358                 return E.getRelatedTarget(this.browserEvent);
8359             }
8360             return null;
8361         },
8362
8363         /**
8364          * Normalizes mouse wheel delta across browsers
8365          * @return {Number} The delta
8366          */
8367         getWheelDelta : function(){
8368             var e = this.browserEvent;
8369             var delta = 0;
8370             if(e.wheelDelta){ /* IE/Opera. */
8371                 delta = e.wheelDelta/120;
8372             }else if(e.detail){ /* Mozilla case. */
8373                 delta = -e.detail/3;
8374             }
8375             return delta;
8376         },
8377
8378         /**
8379          * Returns true if the control, meta, shift or alt key was pressed during this event.
8380          * @return {Boolean}
8381          */
8382         hasModifier : function(){
8383             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8384         },
8385
8386         /**
8387          * Returns true if the target of this event equals el or is a child of el
8388          * @param {String/HTMLElement/Element} el
8389          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8390          * @return {Boolean}
8391          */
8392         within : function(el, related){
8393             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8394             return t && Roo.fly(el).contains(t);
8395         },
8396
8397         getPoint : function(){
8398             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8399         }
8400     };
8401
8402     return new Roo.EventObjectImpl();
8403 }();
8404             
8405     /*
8406  * Based on:
8407  * Ext JS Library 1.1.1
8408  * Copyright(c) 2006-2007, Ext JS, LLC.
8409  *
8410  * Originally Released Under LGPL - original licence link has changed is not relivant.
8411  *
8412  * Fork - LGPL
8413  * <script type="text/javascript">
8414  */
8415
8416  
8417 // was in Composite Element!??!?!
8418  
8419 (function(){
8420     var D = Roo.lib.Dom;
8421     var E = Roo.lib.Event;
8422     var A = Roo.lib.Anim;
8423
8424     // local style camelizing for speed
8425     var propCache = {};
8426     var camelRe = /(-[a-z])/gi;
8427     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8428     var view = document.defaultView;
8429
8430 /**
8431  * @class Roo.Element
8432  * Represents an Element in the DOM.<br><br>
8433  * Usage:<br>
8434 <pre><code>
8435 var el = Roo.get("my-div");
8436
8437 // or with getEl
8438 var el = getEl("my-div");
8439
8440 // or with a DOM element
8441 var el = Roo.get(myDivElement);
8442 </code></pre>
8443  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8444  * each call instead of constructing a new one.<br><br>
8445  * <b>Animations</b><br />
8446  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8447  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8448 <pre>
8449 Option    Default   Description
8450 --------- --------  ---------------------------------------------
8451 duration  .35       The duration of the animation in seconds
8452 easing    easeOut   The YUI easing method
8453 callback  none      A function to execute when the anim completes
8454 scope     this      The scope (this) of the callback function
8455 </pre>
8456 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8457 * manipulate the animation. Here's an example:
8458 <pre><code>
8459 var el = Roo.get("my-div");
8460
8461 // no animation
8462 el.setWidth(100);
8463
8464 // default animation
8465 el.setWidth(100, true);
8466
8467 // animation with some options set
8468 el.setWidth(100, {
8469     duration: 1,
8470     callback: this.foo,
8471     scope: this
8472 });
8473
8474 // using the "anim" property to get the Anim object
8475 var opt = {
8476     duration: 1,
8477     callback: this.foo,
8478     scope: this
8479 };
8480 el.setWidth(100, opt);
8481 ...
8482 if(opt.anim.isAnimated()){
8483     opt.anim.stop();
8484 }
8485 </code></pre>
8486 * <b> Composite (Collections of) Elements</b><br />
8487  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8488  * @constructor Create a new Element directly.
8489  * @param {String/HTMLElement} element
8490  * @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).
8491  */
8492     Roo.Element = function(element, forceNew)
8493     {
8494         var dom = typeof element == "string" ?
8495                 document.getElementById(element) : element;
8496         
8497         this.listeners = {};
8498         
8499         if(!dom){ // invalid id/element
8500             return null;
8501         }
8502         var id = dom.id;
8503         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8504             return Roo.Element.cache[id];
8505         }
8506
8507         /**
8508          * The DOM element
8509          * @type HTMLElement
8510          */
8511         this.dom = dom;
8512
8513         /**
8514          * The DOM element ID
8515          * @type String
8516          */
8517         this.id = id || Roo.id(dom);
8518         
8519         return this; // assumed for cctor?
8520     };
8521
8522     var El = Roo.Element;
8523
8524     El.prototype = {
8525         /**
8526          * The element's default display mode  (defaults to "") 
8527          * @type String
8528          */
8529         originalDisplay : "",
8530
8531         
8532         // note this is overridden in BS version..
8533         visibilityMode : 1, 
8534         /**
8535          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8536          * @type String
8537          */
8538         defaultUnit : "px",
8539         
8540         /**
8541          * Sets the element's visibility mode. When setVisible() is called it
8542          * will use this to determine whether to set the visibility or the display property.
8543          * @param visMode Element.VISIBILITY or Element.DISPLAY
8544          * @return {Roo.Element} this
8545          */
8546         setVisibilityMode : function(visMode){
8547             this.visibilityMode = visMode;
8548             return this;
8549         },
8550         /**
8551          * Convenience method for setVisibilityMode(Element.DISPLAY)
8552          * @param {String} display (optional) What to set display to when visible
8553          * @return {Roo.Element} this
8554          */
8555         enableDisplayMode : function(display){
8556             this.setVisibilityMode(El.DISPLAY);
8557             if(typeof display != "undefined") { this.originalDisplay = display; }
8558             return this;
8559         },
8560
8561         /**
8562          * 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)
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         findParent : function(simpleSelector, maxDepth, returnEl){
8570             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8571             maxDepth = maxDepth || 50;
8572             if(typeof maxDepth != "number"){
8573                 stopEl = Roo.getDom(maxDepth);
8574                 maxDepth = 10;
8575             }
8576             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8577                 if(dq.is(p, simpleSelector)){
8578                     return returnEl ? Roo.get(p) : p;
8579                 }
8580                 depth++;
8581                 p = p.parentNode;
8582             }
8583             return null;
8584         },
8585
8586
8587         /**
8588          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8589          * @param {String} selector The simple selector to test
8590          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8591                 search as a number or element (defaults to 10 || document.body)
8592          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8593          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8594          */
8595         findParentNode : function(simpleSelector, maxDepth, returnEl){
8596             var p = Roo.fly(this.dom.parentNode, '_internal');
8597             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8598         },
8599         
8600         /**
8601          * Looks at  the scrollable parent element
8602          */
8603         findScrollableParent : function()
8604         {
8605             var overflowRegex = /(auto|scroll)/;
8606             
8607             if(this.getStyle('position') === 'fixed'){
8608                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8609             }
8610             
8611             var excludeStaticParent = this.getStyle('position') === "absolute";
8612             
8613             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8614                 
8615                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8616                     continue;
8617                 }
8618                 
8619                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8620                     return parent;
8621                 }
8622                 
8623                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8624                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8625                 }
8626             }
8627             
8628             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8629         },
8630
8631         /**
8632          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8633          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8634          * @param {String} selector The simple selector to test
8635          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8636                 search as a number or element (defaults to 10 || document.body)
8637          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8638          */
8639         up : function(simpleSelector, maxDepth){
8640             return this.findParentNode(simpleSelector, maxDepth, true);
8641         },
8642
8643
8644
8645         /**
8646          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8647          * @param {String} selector The simple selector to test
8648          * @return {Boolean} True if this element matches the selector, else false
8649          */
8650         is : function(simpleSelector){
8651             return Roo.DomQuery.is(this.dom, simpleSelector);
8652         },
8653
8654         /**
8655          * Perform animation on this element.
8656          * @param {Object} args The YUI animation control args
8657          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8658          * @param {Function} onComplete (optional) Function to call when animation completes
8659          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8660          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8661          * @return {Roo.Element} this
8662          */
8663         animate : function(args, duration, onComplete, easing, animType){
8664             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8665             return this;
8666         },
8667
8668         /*
8669          * @private Internal animation call
8670          */
8671         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8672             animType = animType || 'run';
8673             opt = opt || {};
8674             var anim = Roo.lib.Anim[animType](
8675                 this.dom, args,
8676                 (opt.duration || defaultDur) || .35,
8677                 (opt.easing || defaultEase) || 'easeOut',
8678                 function(){
8679                     Roo.callback(cb, this);
8680                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8681                 },
8682                 this
8683             );
8684             opt.anim = anim;
8685             return anim;
8686         },
8687
8688         // private legacy anim prep
8689         preanim : function(a, i){
8690             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8691         },
8692
8693         /**
8694          * Removes worthless text nodes
8695          * @param {Boolean} forceReclean (optional) By default the element
8696          * keeps track if it has been cleaned already so
8697          * you can call this over and over. However, if you update the element and
8698          * need to force a reclean, you can pass true.
8699          */
8700         clean : function(forceReclean){
8701             if(this.isCleaned && forceReclean !== true){
8702                 return this;
8703             }
8704             var ns = /\S/;
8705             var d = this.dom, n = d.firstChild, ni = -1;
8706             while(n){
8707                 var nx = n.nextSibling;
8708                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8709                     d.removeChild(n);
8710                 }else{
8711                     n.nodeIndex = ++ni;
8712                 }
8713                 n = nx;
8714             }
8715             this.isCleaned = true;
8716             return this;
8717         },
8718
8719         // private
8720         calcOffsetsTo : function(el){
8721             el = Roo.get(el);
8722             var d = el.dom;
8723             var restorePos = false;
8724             if(el.getStyle('position') == 'static'){
8725                 el.position('relative');
8726                 restorePos = true;
8727             }
8728             var x = 0, y =0;
8729             var op = this.dom;
8730             while(op && op != d && op.tagName != 'HTML'){
8731                 x+= op.offsetLeft;
8732                 y+= op.offsetTop;
8733                 op = op.offsetParent;
8734             }
8735             if(restorePos){
8736                 el.position('static');
8737             }
8738             return [x, y];
8739         },
8740
8741         /**
8742          * Scrolls this element into view within the passed container.
8743          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8744          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8745          * @return {Roo.Element} this
8746          */
8747         scrollIntoView : function(container, hscroll){
8748             var c = Roo.getDom(container) || document.body;
8749             var el = this.dom;
8750
8751             var o = this.calcOffsetsTo(c),
8752                 l = o[0],
8753                 t = o[1],
8754                 b = t+el.offsetHeight,
8755                 r = l+el.offsetWidth;
8756
8757             var ch = c.clientHeight;
8758             var ct = parseInt(c.scrollTop, 10);
8759             var cl = parseInt(c.scrollLeft, 10);
8760             var cb = ct + ch;
8761             var cr = cl + c.clientWidth;
8762
8763             if(t < ct){
8764                 c.scrollTop = t;
8765             }else if(b > cb){
8766                 c.scrollTop = b-ch;
8767             }
8768
8769             if(hscroll !== false){
8770                 if(l < cl){
8771                     c.scrollLeft = l;
8772                 }else if(r > cr){
8773                     c.scrollLeft = r-c.clientWidth;
8774                 }
8775             }
8776             return this;
8777         },
8778
8779         // private
8780         scrollChildIntoView : function(child, hscroll){
8781             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8782         },
8783
8784         /**
8785          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8786          * the new height may not be available immediately.
8787          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8788          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8789          * @param {Function} onComplete (optional) Function to call when animation completes
8790          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8791          * @return {Roo.Element} this
8792          */
8793         autoHeight : function(animate, duration, onComplete, easing){
8794             var oldHeight = this.getHeight();
8795             this.clip();
8796             this.setHeight(1); // force clipping
8797             setTimeout(function(){
8798                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8799                 if(!animate){
8800                     this.setHeight(height);
8801                     this.unclip();
8802                     if(typeof onComplete == "function"){
8803                         onComplete();
8804                     }
8805                 }else{
8806                     this.setHeight(oldHeight); // restore original height
8807                     this.setHeight(height, animate, duration, function(){
8808                         this.unclip();
8809                         if(typeof onComplete == "function") { onComplete(); }
8810                     }.createDelegate(this), easing);
8811                 }
8812             }.createDelegate(this), 0);
8813             return this;
8814         },
8815
8816         /**
8817          * Returns true if this element is an ancestor of the passed element
8818          * @param {HTMLElement/String} el The element to check
8819          * @return {Boolean} True if this element is an ancestor of el, else false
8820          */
8821         contains : function(el){
8822             if(!el){return false;}
8823             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8824         },
8825
8826         /**
8827          * Checks whether the element is currently visible using both visibility and display properties.
8828          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8829          * @return {Boolean} True if the element is currently visible, else false
8830          */
8831         isVisible : function(deep) {
8832             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8833             if(deep !== true || !vis){
8834                 return vis;
8835             }
8836             var p = this.dom.parentNode;
8837             while(p && p.tagName.toLowerCase() != "body"){
8838                 if(!Roo.fly(p, '_isVisible').isVisible()){
8839                     return false;
8840                 }
8841                 p = p.parentNode;
8842             }
8843             return true;
8844         },
8845
8846         /**
8847          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8848          * @param {String} selector The CSS selector
8849          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8850          * @return {CompositeElement/CompositeElementLite} The composite element
8851          */
8852         select : function(selector, unique){
8853             return El.select(selector, unique, this.dom);
8854         },
8855
8856         /**
8857          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8858          * @param {String} selector The CSS selector
8859          * @return {Array} An array of the matched nodes
8860          */
8861         query : function(selector, unique){
8862             return Roo.DomQuery.select(selector, this.dom);
8863         },
8864
8865         /**
8866          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8867          * @param {String} selector The CSS selector
8868          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8869          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8870          */
8871         child : function(selector, returnDom){
8872             var n = Roo.DomQuery.selectNode(selector, this.dom);
8873             return returnDom ? n : Roo.get(n);
8874         },
8875
8876         /**
8877          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8878          * @param {String} selector The CSS selector
8879          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8880          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8881          */
8882         down : function(selector, returnDom){
8883             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8884             return returnDom ? n : Roo.get(n);
8885         },
8886
8887         /**
8888          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8889          * @param {String} group The group the DD object is member of
8890          * @param {Object} config The DD config object
8891          * @param {Object} overrides An object containing methods to override/implement on the DD object
8892          * @return {Roo.dd.DD} The DD object
8893          */
8894         initDD : function(group, config, overrides){
8895             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8896             return Roo.apply(dd, overrides);
8897         },
8898
8899         /**
8900          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8901          * @param {String} group The group the DDProxy object is member of
8902          * @param {Object} config The DDProxy config object
8903          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8904          * @return {Roo.dd.DDProxy} The DDProxy object
8905          */
8906         initDDProxy : function(group, config, overrides){
8907             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8908             return Roo.apply(dd, overrides);
8909         },
8910
8911         /**
8912          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8913          * @param {String} group The group the DDTarget object is member of
8914          * @param {Object} config The DDTarget config object
8915          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8916          * @return {Roo.dd.DDTarget} The DDTarget object
8917          */
8918         initDDTarget : function(group, config, overrides){
8919             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8920             return Roo.apply(dd, overrides);
8921         },
8922
8923         /**
8924          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8925          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8926          * @param {Boolean} visible Whether the element is visible
8927          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8928          * @return {Roo.Element} this
8929          */
8930          setVisible : function(visible, animate){
8931             if(!animate || !A){
8932                 if(this.visibilityMode == El.DISPLAY){
8933                     this.setDisplayed(visible);
8934                 }else{
8935                     this.fixDisplay();
8936                     this.dom.style.visibility = visible ? "visible" : "hidden";
8937                 }
8938             }else{
8939                 // closure for composites
8940                 var dom = this.dom;
8941                 var visMode = this.visibilityMode;
8942                 if(visible){
8943                     this.setOpacity(.01);
8944                     this.setVisible(true);
8945                 }
8946                 this.anim({opacity: { to: (visible?1:0) }},
8947                       this.preanim(arguments, 1),
8948                       null, .35, 'easeIn', function(){
8949                          if(!visible){
8950                              if(visMode == El.DISPLAY){
8951                                  dom.style.display = "none";
8952                              }else{
8953                                  dom.style.visibility = "hidden";
8954                              }
8955                              Roo.get(dom).setOpacity(1);
8956                          }
8957                      });
8958             }
8959             return this;
8960         },
8961
8962         /**
8963          * Returns true if display is not "none"
8964          * @return {Boolean}
8965          */
8966         isDisplayed : function() {
8967             return this.getStyle("display") != "none";
8968         },
8969
8970         /**
8971          * Toggles the element's visibility or display, depending on visibility mode.
8972          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8973          * @return {Roo.Element} this
8974          */
8975         toggle : function(animate){
8976             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8977             return this;
8978         },
8979
8980         /**
8981          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8982          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8983          * @return {Roo.Element} this
8984          */
8985         setDisplayed : function(value) {
8986             if(typeof value == "boolean"){
8987                value = value ? this.originalDisplay : "none";
8988             }
8989             this.setStyle("display", value);
8990             return this;
8991         },
8992
8993         /**
8994          * Tries to focus the element. Any exceptions are caught and ignored.
8995          * @return {Roo.Element} this
8996          */
8997         focus : function() {
8998             try{
8999                 this.dom.focus();
9000             }catch(e){}
9001             return this;
9002         },
9003
9004         /**
9005          * Tries to blur the element. Any exceptions are caught and ignored.
9006          * @return {Roo.Element} this
9007          */
9008         blur : function() {
9009             try{
9010                 this.dom.blur();
9011             }catch(e){}
9012             return this;
9013         },
9014
9015         /**
9016          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
9017          * @param {String/Array} className The CSS class to add, or an array of classes
9018          * @return {Roo.Element} this
9019          */
9020         addClass : function(className){
9021             if(className instanceof Array){
9022                 for(var i = 0, len = className.length; i < len; i++) {
9023                     this.addClass(className[i]);
9024                 }
9025             }else{
9026                 if(className && !this.hasClass(className)){
9027                     if (this.dom instanceof SVGElement) {
9028                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
9029                     } else {
9030                         this.dom.className = this.dom.className + " " + className;
9031                     }
9032                 }
9033             }
9034             return this;
9035         },
9036
9037         /**
9038          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
9039          * @param {String/Array} className The CSS class to add, or an array of classes
9040          * @return {Roo.Element} this
9041          */
9042         radioClass : function(className){
9043             var siblings = this.dom.parentNode.childNodes;
9044             for(var i = 0; i < siblings.length; i++) {
9045                 var s = siblings[i];
9046                 if(s.nodeType == 1){
9047                     Roo.get(s).removeClass(className);
9048                 }
9049             }
9050             this.addClass(className);
9051             return this;
9052         },
9053
9054         /**
9055          * Removes one or more CSS classes from the element.
9056          * @param {String/Array} className The CSS class to remove, or an array of classes
9057          * @return {Roo.Element} this
9058          */
9059         removeClass : function(className){
9060             
9061             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
9062             if(!className || !cn){
9063                 return this;
9064             }
9065             if(className instanceof Array){
9066                 for(var i = 0, len = className.length; i < len; i++) {
9067                     this.removeClass(className[i]);
9068                 }
9069             }else{
9070                 if(this.hasClass(className)){
9071                     var re = this.classReCache[className];
9072                     if (!re) {
9073                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
9074                        this.classReCache[className] = re;
9075                     }
9076                     if (this.dom instanceof SVGElement) {
9077                         this.dom.className.baseVal = cn.replace(re, " ");
9078                     } else {
9079                         this.dom.className = cn.replace(re, " ");
9080                     }
9081                 }
9082             }
9083             return this;
9084         },
9085
9086         // private
9087         classReCache: {},
9088
9089         /**
9090          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
9091          * @param {String} className The CSS class to toggle
9092          * @return {Roo.Element} this
9093          */
9094         toggleClass : function(className){
9095             if(this.hasClass(className)){
9096                 this.removeClass(className);
9097             }else{
9098                 this.addClass(className);
9099             }
9100             return this;
9101         },
9102
9103         /**
9104          * Checks if the specified CSS class exists on this element's DOM node.
9105          * @param {String} className The CSS class to check for
9106          * @return {Boolean} True if the class exists, else false
9107          */
9108         hasClass : function(className){
9109             if (this.dom instanceof SVGElement) {
9110                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
9111             } 
9112             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
9113         },
9114
9115         /**
9116          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
9117          * @param {String} oldClassName The CSS class to replace
9118          * @param {String} newClassName The replacement CSS class
9119          * @return {Roo.Element} this
9120          */
9121         replaceClass : function(oldClassName, newClassName){
9122             this.removeClass(oldClassName);
9123             this.addClass(newClassName);
9124             return this;
9125         },
9126
9127         /**
9128          * Returns an object with properties matching the styles requested.
9129          * For example, el.getStyles('color', 'font-size', 'width') might return
9130          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
9131          * @param {String} style1 A style name
9132          * @param {String} style2 A style name
9133          * @param {String} etc.
9134          * @return {Object} The style object
9135          */
9136         getStyles : function(){
9137             var a = arguments, len = a.length, r = {};
9138             for(var i = 0; i < len; i++){
9139                 r[a[i]] = this.getStyle(a[i]);
9140             }
9141             return r;
9142         },
9143
9144         /**
9145          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
9146          * @param {String} property The style property whose value is returned.
9147          * @return {String} The current value of the style property for this element.
9148          */
9149         getStyle : function(){
9150             return view && view.getComputedStyle ?
9151                 function(prop){
9152                     var el = this.dom, v, cs, camel;
9153                     if(prop == 'float'){
9154                         prop = "cssFloat";
9155                     }
9156                     if(el.style && (v = el.style[prop])){
9157                         return v;
9158                     }
9159                     if(cs = view.getComputedStyle(el, "")){
9160                         if(!(camel = propCache[prop])){
9161                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
9162                         }
9163                         return cs[camel];
9164                     }
9165                     return null;
9166                 } :
9167                 function(prop){
9168                     var el = this.dom, v, cs, camel;
9169                     if(prop == 'opacity'){
9170                         if(typeof el.style.filter == 'string'){
9171                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
9172                             if(m){
9173                                 var fv = parseFloat(m[1]);
9174                                 if(!isNaN(fv)){
9175                                     return fv ? fv / 100 : 0;
9176                                 }
9177                             }
9178                         }
9179                         return 1;
9180                     }else if(prop == 'float'){
9181                         prop = "styleFloat";
9182                     }
9183                     if(!(camel = propCache[prop])){
9184                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
9185                     }
9186                     if(v = el.style[camel]){
9187                         return v;
9188                     }
9189                     if(cs = el.currentStyle){
9190                         return cs[camel];
9191                     }
9192                     return null;
9193                 };
9194         }(),
9195
9196         /**
9197          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
9198          * @param {String/Object} property The style property to be set, or an object of multiple styles.
9199          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
9200          * @return {Roo.Element} this
9201          */
9202         setStyle : function(prop, value){
9203             if(typeof prop == "string"){
9204                 
9205                 if (prop == 'float') {
9206                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
9207                     return this;
9208                 }
9209                 
9210                 var camel;
9211                 if(!(camel = propCache[prop])){
9212                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
9213                 }
9214                 
9215                 if(camel == 'opacity') {
9216                     this.setOpacity(value);
9217                 }else{
9218                     this.dom.style[camel] = value;
9219                 }
9220             }else{
9221                 for(var style in prop){
9222                     if(typeof prop[style] != "function"){
9223                        this.setStyle(style, prop[style]);
9224                     }
9225                 }
9226             }
9227             return this;
9228         },
9229
9230         /**
9231          * More flexible version of {@link #setStyle} for setting style properties.
9232          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9233          * a function which returns such a specification.
9234          * @return {Roo.Element} this
9235          */
9236         applyStyles : function(style){
9237             Roo.DomHelper.applyStyles(this.dom, style);
9238             return this;
9239         },
9240
9241         /**
9242           * 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).
9243           * @return {Number} The X position of the element
9244           */
9245         getX : function(){
9246             return D.getX(this.dom);
9247         },
9248
9249         /**
9250           * 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).
9251           * @return {Number} The Y position of the element
9252           */
9253         getY : function(){
9254             return D.getY(this.dom);
9255         },
9256
9257         /**
9258           * 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).
9259           * @return {Array} The XY position of the element
9260           */
9261         getXY : function(){
9262             return D.getXY(this.dom);
9263         },
9264
9265         /**
9266          * 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).
9267          * @param {Number} The X position of the element
9268          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9269          * @return {Roo.Element} this
9270          */
9271         setX : function(x, animate){
9272             if(!animate || !A){
9273                 D.setX(this.dom, x);
9274             }else{
9275                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9276             }
9277             return this;
9278         },
9279
9280         /**
9281          * 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).
9282          * @param {Number} The Y position of the element
9283          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9284          * @return {Roo.Element} this
9285          */
9286         setY : function(y, animate){
9287             if(!animate || !A){
9288                 D.setY(this.dom, y);
9289             }else{
9290                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9291             }
9292             return this;
9293         },
9294
9295         /**
9296          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9297          * @param {String} left The left CSS property value
9298          * @return {Roo.Element} this
9299          */
9300         setLeft : function(left){
9301             this.setStyle("left", this.addUnits(left));
9302             return this;
9303         },
9304
9305         /**
9306          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9307          * @param {String} top The top CSS property value
9308          * @return {Roo.Element} this
9309          */
9310         setTop : function(top){
9311             this.setStyle("top", this.addUnits(top));
9312             return this;
9313         },
9314
9315         /**
9316          * Sets the element's CSS right style.
9317          * @param {String} right The right CSS property value
9318          * @return {Roo.Element} this
9319          */
9320         setRight : function(right){
9321             this.setStyle("right", this.addUnits(right));
9322             return this;
9323         },
9324
9325         /**
9326          * Sets the element's CSS bottom style.
9327          * @param {String} bottom The bottom CSS property value
9328          * @return {Roo.Element} this
9329          */
9330         setBottom : function(bottom){
9331             this.setStyle("bottom", this.addUnits(bottom));
9332             return this;
9333         },
9334
9335         /**
9336          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9337          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9338          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9339          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9340          * @return {Roo.Element} this
9341          */
9342         setXY : function(pos, animate){
9343             if(!animate || !A){
9344                 D.setXY(this.dom, pos);
9345             }else{
9346                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9347             }
9348             return this;
9349         },
9350
9351         /**
9352          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9353          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9354          * @param {Number} x X value for new position (coordinates are page-based)
9355          * @param {Number} y Y value for new position (coordinates are page-based)
9356          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9357          * @return {Roo.Element} this
9358          */
9359         setLocation : function(x, y, animate){
9360             this.setXY([x, y], this.preanim(arguments, 2));
9361             return this;
9362         },
9363
9364         /**
9365          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9366          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9367          * @param {Number} x X value for new position (coordinates are page-based)
9368          * @param {Number} y Y value for new position (coordinates are page-based)
9369          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9370          * @return {Roo.Element} this
9371          */
9372         moveTo : function(x, y, animate){
9373             this.setXY([x, y], this.preanim(arguments, 2));
9374             return this;
9375         },
9376
9377         /**
9378          * Returns the region of the given element.
9379          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9380          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9381          */
9382         getRegion : function(){
9383             return D.getRegion(this.dom);
9384         },
9385
9386         /**
9387          * Returns the offset height of the element
9388          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9389          * @return {Number} The element's height
9390          */
9391         getHeight : function(contentHeight){
9392             var h = this.dom.offsetHeight || 0;
9393             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9394         },
9395
9396         /**
9397          * Returns the offset width of the element
9398          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9399          * @return {Number} The element's width
9400          */
9401         getWidth : function(contentWidth){
9402             var w = this.dom.offsetWidth || 0;
9403             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9404         },
9405
9406         /**
9407          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9408          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9409          * if a height has not been set using CSS.
9410          * @return {Number}
9411          */
9412         getComputedHeight : function(){
9413             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9414             if(!h){
9415                 h = parseInt(this.getStyle('height'), 10) || 0;
9416                 if(!this.isBorderBox()){
9417                     h += this.getFrameWidth('tb');
9418                 }
9419             }
9420             return h;
9421         },
9422
9423         /**
9424          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9425          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9426          * if a width has not been set using CSS.
9427          * @return {Number}
9428          */
9429         getComputedWidth : function(){
9430             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9431             if(!w){
9432                 w = parseInt(this.getStyle('width'), 10) || 0;
9433                 if(!this.isBorderBox()){
9434                     w += this.getFrameWidth('lr');
9435                 }
9436             }
9437             return w;
9438         },
9439
9440         /**
9441          * Returns the size of the element.
9442          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9443          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9444          */
9445         getSize : function(contentSize){
9446             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9447         },
9448
9449         /**
9450          * Returns the width and height of the viewport.
9451          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9452          */
9453         getViewSize : function(){
9454             var d = this.dom, doc = document, aw = 0, ah = 0;
9455             if(d == doc || d == doc.body){
9456                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9457             }else{
9458                 return {
9459                     width : d.clientWidth,
9460                     height: d.clientHeight
9461                 };
9462             }
9463         },
9464
9465         /**
9466          * Returns the value of the "value" attribute
9467          * @param {Boolean} asNumber true to parse the value as a number
9468          * @return {String/Number}
9469          */
9470         getValue : function(asNumber){
9471             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9472         },
9473
9474         // private
9475         adjustWidth : function(width){
9476             if(typeof width == "number"){
9477                 if(this.autoBoxAdjust && !this.isBorderBox()){
9478                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9479                 }
9480                 if(width < 0){
9481                     width = 0;
9482                 }
9483             }
9484             return width;
9485         },
9486
9487         // private
9488         adjustHeight : function(height){
9489             if(typeof height == "number"){
9490                if(this.autoBoxAdjust && !this.isBorderBox()){
9491                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9492                }
9493                if(height < 0){
9494                    height = 0;
9495                }
9496             }
9497             return height;
9498         },
9499
9500         /**
9501          * Set the width of the element
9502          * @param {Number} width The new width
9503          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9504          * @return {Roo.Element} this
9505          */
9506         setWidth : function(width, animate){
9507             width = this.adjustWidth(width);
9508             if(!animate || !A){
9509                 this.dom.style.width = this.addUnits(width);
9510             }else{
9511                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9512             }
9513             return this;
9514         },
9515
9516         /**
9517          * Set the height of the element
9518          * @param {Number} height The new height
9519          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9520          * @return {Roo.Element} this
9521          */
9522          setHeight : function(height, animate){
9523             height = this.adjustHeight(height);
9524             if(!animate || !A){
9525                 this.dom.style.height = this.addUnits(height);
9526             }else{
9527                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9528             }
9529             return this;
9530         },
9531
9532         /**
9533          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9534          * @param {Number} width The new width
9535          * @param {Number} height The new height
9536          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9537          * @return {Roo.Element} this
9538          */
9539          setSize : function(width, height, animate){
9540             if(typeof width == "object"){ // in case of object from getSize()
9541                 height = width.height; width = width.width;
9542             }
9543             width = this.adjustWidth(width); height = this.adjustHeight(height);
9544             if(!animate || !A){
9545                 this.dom.style.width = this.addUnits(width);
9546                 this.dom.style.height = this.addUnits(height);
9547             }else{
9548                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9549             }
9550             return this;
9551         },
9552
9553         /**
9554          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9555          * @param {Number} x X value for new position (coordinates are page-based)
9556          * @param {Number} y Y value for new position (coordinates are page-based)
9557          * @param {Number} width The new width
9558          * @param {Number} height The new height
9559          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9560          * @return {Roo.Element} this
9561          */
9562         setBounds : function(x, y, width, height, animate){
9563             if(!animate || !A){
9564                 this.setSize(width, height);
9565                 this.setLocation(x, y);
9566             }else{
9567                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9568                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9569                               this.preanim(arguments, 4), 'motion');
9570             }
9571             return this;
9572         },
9573
9574         /**
9575          * 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.
9576          * @param {Roo.lib.Region} region The region to fill
9577          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9578          * @return {Roo.Element} this
9579          */
9580         setRegion : function(region, animate){
9581             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9582             return this;
9583         },
9584
9585         /**
9586          * Appends an event handler
9587          *
9588          * @param {String}   eventName     The type of event to append
9589          * @param {Function} fn        The method the event invokes
9590          * @param {Object} scope       (optional) The scope (this object) of the fn
9591          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9592          */
9593         addListener : function(eventName, fn, scope, options)
9594         {
9595             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9596                 this.addListener('touchstart', this.onTapHandler, this);
9597             }
9598             
9599             // we need to handle a special case where dom element is a svg element.
9600             // in this case we do not actua
9601             if (!this.dom) {
9602                 return;
9603             }
9604             
9605             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9606                 if (typeof(this.listeners[eventName]) == 'undefined') {
9607                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9608                 }
9609                 this.listeners[eventName].addListener(fn, scope, options);
9610                 return;
9611             }
9612             
9613                 
9614             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9615             
9616             
9617         },
9618         tapedTwice : false,
9619         onTapHandler : function(event)
9620         {
9621             if(!this.tapedTwice) {
9622                 this.tapedTwice = true;
9623                 var s = this;
9624                 setTimeout( function() {
9625                     s.tapedTwice = false;
9626                 }, 300 );
9627                 return;
9628             }
9629             event.preventDefault();
9630             var revent = new MouseEvent('dblclick',  {
9631                 view: window,
9632                 bubbles: true,
9633                 cancelable: true
9634             });
9635              
9636             this.dom.dispatchEvent(revent);
9637             //action on double tap goes below
9638              
9639         }, 
9640  
9641         /**
9642          * Removes an event handler from this element
9643          * @param {String} eventName the type of event to remove
9644          * @param {Function} fn the method the event invokes
9645          * @param {Function} scope (needed for svg fake listeners)
9646          * @return {Roo.Element} this
9647          */
9648         removeListener : function(eventName, fn, scope){
9649             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9650             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9651                 return this;
9652             }
9653             this.listeners[eventName].removeListener(fn, scope);
9654             return this;
9655         },
9656
9657         /**
9658          * Removes all previous added listeners from this element
9659          * @return {Roo.Element} this
9660          */
9661         removeAllListeners : function(){
9662             E.purgeElement(this.dom);
9663             this.listeners = {};
9664             return this;
9665         },
9666
9667         relayEvent : function(eventName, observable){
9668             this.on(eventName, function(e){
9669                 observable.fireEvent(eventName, e);
9670             });
9671         },
9672
9673         
9674         /**
9675          * Set the opacity of the element
9676          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9677          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9678          * @return {Roo.Element} this
9679          */
9680          setOpacity : function(opacity, animate){
9681             if(!animate || !A){
9682                 var s = this.dom.style;
9683                 if(Roo.isIE){
9684                     s.zoom = 1;
9685                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9686                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9687                 }else{
9688                     s.opacity = opacity;
9689                 }
9690             }else{
9691                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9692             }
9693             return this;
9694         },
9695
9696         /**
9697          * Gets the left X coordinate
9698          * @param {Boolean} local True to get the local css position instead of page coordinate
9699          * @return {Number}
9700          */
9701         getLeft : function(local){
9702             if(!local){
9703                 return this.getX();
9704             }else{
9705                 return parseInt(this.getStyle("left"), 10) || 0;
9706             }
9707         },
9708
9709         /**
9710          * Gets the right X coordinate of the element (element X position + element width)
9711          * @param {Boolean} local True to get the local css position instead of page coordinate
9712          * @return {Number}
9713          */
9714         getRight : function(local){
9715             if(!local){
9716                 return this.getX() + this.getWidth();
9717             }else{
9718                 return (this.getLeft(true) + this.getWidth()) || 0;
9719             }
9720         },
9721
9722         /**
9723          * Gets the top Y coordinate
9724          * @param {Boolean} local True to get the local css position instead of page coordinate
9725          * @return {Number}
9726          */
9727         getTop : function(local) {
9728             if(!local){
9729                 return this.getY();
9730             }else{
9731                 return parseInt(this.getStyle("top"), 10) || 0;
9732             }
9733         },
9734
9735         /**
9736          * Gets the bottom Y coordinate of the element (element Y position + element height)
9737          * @param {Boolean} local True to get the local css position instead of page coordinate
9738          * @return {Number}
9739          */
9740         getBottom : function(local){
9741             if(!local){
9742                 return this.getY() + this.getHeight();
9743             }else{
9744                 return (this.getTop(true) + this.getHeight()) || 0;
9745             }
9746         },
9747
9748         /**
9749         * Initializes positioning on this element. If a desired position is not passed, it will make the
9750         * the element positioned relative IF it is not already positioned.
9751         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9752         * @param {Number} zIndex (optional) The zIndex to apply
9753         * @param {Number} x (optional) Set the page X position
9754         * @param {Number} y (optional) Set the page Y position
9755         */
9756         position : function(pos, zIndex, x, y){
9757             if(!pos){
9758                if(this.getStyle('position') == 'static'){
9759                    this.setStyle('position', 'relative');
9760                }
9761             }else{
9762                 this.setStyle("position", pos);
9763             }
9764             if(zIndex){
9765                 this.setStyle("z-index", zIndex);
9766             }
9767             if(x !== undefined && y !== undefined){
9768                 this.setXY([x, y]);
9769             }else if(x !== undefined){
9770                 this.setX(x);
9771             }else if(y !== undefined){
9772                 this.setY(y);
9773             }
9774         },
9775
9776         /**
9777         * Clear positioning back to the default when the document was loaded
9778         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9779         * @return {Roo.Element} this
9780          */
9781         clearPositioning : function(value){
9782             value = value ||'';
9783             this.setStyle({
9784                 "left": value,
9785                 "right": value,
9786                 "top": value,
9787                 "bottom": value,
9788                 "z-index": "",
9789                 "position" : "static"
9790             });
9791             return this;
9792         },
9793
9794         /**
9795         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9796         * snapshot before performing an update and then restoring the element.
9797         * @return {Object}
9798         */
9799         getPositioning : function(){
9800             var l = this.getStyle("left");
9801             var t = this.getStyle("top");
9802             return {
9803                 "position" : this.getStyle("position"),
9804                 "left" : l,
9805                 "right" : l ? "" : this.getStyle("right"),
9806                 "top" : t,
9807                 "bottom" : t ? "" : this.getStyle("bottom"),
9808                 "z-index" : this.getStyle("z-index")
9809             };
9810         },
9811
9812         /**
9813          * Gets the width of the border(s) for the specified side(s)
9814          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9815          * passing lr would get the border (l)eft width + the border (r)ight width.
9816          * @return {Number} The width of the sides passed added together
9817          */
9818         getBorderWidth : function(side){
9819             return this.addStyles(side, El.borders);
9820         },
9821
9822         /**
9823          * Gets the width of the padding(s) for the specified side(s)
9824          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9825          * passing lr would get the padding (l)eft + the padding (r)ight.
9826          * @return {Number} The padding of the sides passed added together
9827          */
9828         getPadding : function(side){
9829             return this.addStyles(side, El.paddings);
9830         },
9831
9832         /**
9833         * Set positioning with an object returned by getPositioning().
9834         * @param {Object} posCfg
9835         * @return {Roo.Element} this
9836          */
9837         setPositioning : function(pc){
9838             this.applyStyles(pc);
9839             if(pc.right == "auto"){
9840                 this.dom.style.right = "";
9841             }
9842             if(pc.bottom == "auto"){
9843                 this.dom.style.bottom = "";
9844             }
9845             return this;
9846         },
9847
9848         // private
9849         fixDisplay : function(){
9850             if(this.getStyle("display") == "none"){
9851                 this.setStyle("visibility", "hidden");
9852                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9853                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9854                     this.setStyle("display", "block");
9855                 }
9856             }
9857         },
9858
9859         /**
9860          * Quick set left and top adding default units
9861          * @param {String} left The left CSS property value
9862          * @param {String} top The top CSS property value
9863          * @return {Roo.Element} this
9864          */
9865          setLeftTop : function(left, top){
9866             this.dom.style.left = this.addUnits(left);
9867             this.dom.style.top = this.addUnits(top);
9868             return this;
9869         },
9870
9871         /**
9872          * Move this element relative to its current position.
9873          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9874          * @param {Number} distance How far to move the element in pixels
9875          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9876          * @return {Roo.Element} this
9877          */
9878          move : function(direction, distance, animate){
9879             var xy = this.getXY();
9880             direction = direction.toLowerCase();
9881             switch(direction){
9882                 case "l":
9883                 case "left":
9884                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9885                     break;
9886                case "r":
9887                case "right":
9888                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9889                     break;
9890                case "t":
9891                case "top":
9892                case "up":
9893                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9894                     break;
9895                case "b":
9896                case "bottom":
9897                case "down":
9898                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9899                     break;
9900             }
9901             return this;
9902         },
9903
9904         /**
9905          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9906          * @return {Roo.Element} this
9907          */
9908         clip : function(){
9909             if(!this.isClipped){
9910                this.isClipped = true;
9911                this.originalClip = {
9912                    "o": this.getStyle("overflow"),
9913                    "x": this.getStyle("overflow-x"),
9914                    "y": this.getStyle("overflow-y")
9915                };
9916                this.setStyle("overflow", "hidden");
9917                this.setStyle("overflow-x", "hidden");
9918                this.setStyle("overflow-y", "hidden");
9919             }
9920             return this;
9921         },
9922
9923         /**
9924          *  Return clipping (overflow) to original clipping before clip() was called
9925          * @return {Roo.Element} this
9926          */
9927         unclip : function(){
9928             if(this.isClipped){
9929                 this.isClipped = false;
9930                 var o = this.originalClip;
9931                 if(o.o){this.setStyle("overflow", o.o);}
9932                 if(o.x){this.setStyle("overflow-x", o.x);}
9933                 if(o.y){this.setStyle("overflow-y", o.y);}
9934             }
9935             return this;
9936         },
9937
9938
9939         /**
9940          * Gets the x,y coordinates specified by the anchor position on the element.
9941          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9942          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9943          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9944          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9945          * @return {Array} [x, y] An array containing the element's x and y coordinates
9946          */
9947         getAnchorXY : function(anchor, local, s){
9948             //Passing a different size is useful for pre-calculating anchors,
9949             //especially for anchored animations that change the el size.
9950
9951             var w, h, vp = false;
9952             if(!s){
9953                 var d = this.dom;
9954                 if(d == document.body || d == document){
9955                     vp = true;
9956                     w = D.getViewWidth(); h = D.getViewHeight();
9957                 }else{
9958                     w = this.getWidth(); h = this.getHeight();
9959                 }
9960             }else{
9961                 w = s.width;  h = s.height;
9962             }
9963             var x = 0, y = 0, r = Math.round;
9964             switch((anchor || "tl").toLowerCase()){
9965                 case "c":
9966                     x = r(w*.5);
9967                     y = r(h*.5);
9968                 break;
9969                 case "t":
9970                     x = r(w*.5);
9971                     y = 0;
9972                 break;
9973                 case "l":
9974                     x = 0;
9975                     y = r(h*.5);
9976                 break;
9977                 case "r":
9978                     x = w;
9979                     y = r(h*.5);
9980                 break;
9981                 case "b":
9982                     x = r(w*.5);
9983                     y = h;
9984                 break;
9985                 case "tl":
9986                     x = 0;
9987                     y = 0;
9988                 break;
9989                 case "bl":
9990                     x = 0;
9991                     y = h;
9992                 break;
9993                 case "br":
9994                     x = w;
9995                     y = h;
9996                 break;
9997                 case "tr":
9998                     x = w;
9999                     y = 0;
10000                 break;
10001             }
10002             if(local === true){
10003                 return [x, y];
10004             }
10005             if(vp){
10006                 var sc = this.getScroll();
10007                 return [x + sc.left, y + sc.top];
10008             }
10009             //Add the element's offset xy
10010             var o = this.getXY();
10011             return [x+o[0], y+o[1]];
10012         },
10013
10014         /**
10015          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
10016          * supported position values.
10017          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10018          * @param {String} position The position to align to.
10019          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10020          * @return {Array} [x, y]
10021          */
10022         getAlignToXY : function(el, p, o)
10023         {
10024             el = Roo.get(el);
10025             var d = this.dom;
10026             if(!el.dom){
10027                 throw "Element.alignTo with an element that doesn't exist";
10028             }
10029             var c = false; //constrain to viewport
10030             var p1 = "", p2 = "";
10031             o = o || [0,0];
10032
10033             if(!p){
10034                 p = "tl-bl";
10035             }else if(p == "?"){
10036                 p = "tl-bl?";
10037             }else if(p.indexOf("-") == -1){
10038                 p = "tl-" + p;
10039             }
10040             p = p.toLowerCase();
10041             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
10042             if(!m){
10043                throw "Element.alignTo with an invalid alignment " + p;
10044             }
10045             p1 = m[1]; p2 = m[2]; c = !!m[3];
10046
10047             //Subtract the aligned el's internal xy from the target's offset xy
10048             //plus custom offset to get the aligned el's new offset xy
10049             var a1 = this.getAnchorXY(p1, true);
10050             var a2 = el.getAnchorXY(p2, false);
10051             var x = a2[0] - a1[0] + o[0];
10052             var y = a2[1] - a1[1] + o[1];
10053             if(c){
10054                 //constrain the aligned el to viewport if necessary
10055                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
10056                 // 5px of margin for ie
10057                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
10058
10059                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
10060                 //perpendicular to the vp border, allow the aligned el to slide on that border,
10061                 //otherwise swap the aligned el to the opposite border of the target.
10062                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
10063                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
10064                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
10065                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
10066
10067                var doc = document;
10068                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
10069                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
10070
10071                if((x+w) > dw + scrollX){
10072                     x = swapX ? r.left-w : dw+scrollX-w;
10073                 }
10074                if(x < scrollX){
10075                    x = swapX ? r.right : scrollX;
10076                }
10077                if((y+h) > dh + scrollY){
10078                     y = swapY ? r.top-h : dh+scrollY-h;
10079                 }
10080                if (y < scrollY){
10081                    y = swapY ? r.bottom : scrollY;
10082                }
10083             }
10084             return [x,y];
10085         },
10086
10087         // private
10088         getConstrainToXY : function(){
10089             var os = {top:0, left:0, bottom:0, right: 0};
10090
10091             return function(el, local, offsets, proposedXY){
10092                 el = Roo.get(el);
10093                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
10094
10095                 var vw, vh, vx = 0, vy = 0;
10096                 if(el.dom == document.body || el.dom == document){
10097                     vw = Roo.lib.Dom.getViewWidth();
10098                     vh = Roo.lib.Dom.getViewHeight();
10099                 }else{
10100                     vw = el.dom.clientWidth;
10101                     vh = el.dom.clientHeight;
10102                     if(!local){
10103                         var vxy = el.getXY();
10104                         vx = vxy[0];
10105                         vy = vxy[1];
10106                     }
10107                 }
10108
10109                 var s = el.getScroll();
10110
10111                 vx += offsets.left + s.left;
10112                 vy += offsets.top + s.top;
10113
10114                 vw -= offsets.right;
10115                 vh -= offsets.bottom;
10116
10117                 var vr = vx+vw;
10118                 var vb = vy+vh;
10119
10120                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
10121                 var x = xy[0], y = xy[1];
10122                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
10123
10124                 // only move it if it needs it
10125                 var moved = false;
10126
10127                 // first validate right/bottom
10128                 if((x + w) > vr){
10129                     x = vr - w;
10130                     moved = true;
10131                 }
10132                 if((y + h) > vb){
10133                     y = vb - h;
10134                     moved = true;
10135                 }
10136                 // then make sure top/left isn't negative
10137                 if(x < vx){
10138                     x = vx;
10139                     moved = true;
10140                 }
10141                 if(y < vy){
10142                     y = vy;
10143                     moved = true;
10144                 }
10145                 return moved ? [x, y] : false;
10146             };
10147         }(),
10148
10149         // private
10150         adjustForConstraints : function(xy, parent, offsets){
10151             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
10152         },
10153
10154         /**
10155          * Aligns this element with another element relative to the specified anchor points. If the other element is the
10156          * document it aligns it to the viewport.
10157          * The position parameter is optional, and can be specified in any one of the following formats:
10158          * <ul>
10159          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
10160          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
10161          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
10162          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
10163          *   <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
10164          *       element's anchor point, and the second value is used as the target's anchor point.</li>
10165          * </ul>
10166          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
10167          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
10168          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
10169          * that specified in order to enforce the viewport constraints.
10170          * Following are all of the supported anchor positions:
10171     <pre>
10172     Value  Description
10173     -----  -----------------------------
10174     tl     The top left corner (default)
10175     t      The center of the top edge
10176     tr     The top right corner
10177     l      The center of the left edge
10178     c      In the center of the element
10179     r      The center of the right edge
10180     bl     The bottom left corner
10181     b      The center of the bottom edge
10182     br     The bottom right corner
10183     </pre>
10184     Example Usage:
10185     <pre><code>
10186     // align el to other-el using the default positioning ("tl-bl", non-constrained)
10187     el.alignTo("other-el");
10188
10189     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
10190     el.alignTo("other-el", "tr?");
10191
10192     // align the bottom right corner of el with the center left edge of other-el
10193     el.alignTo("other-el", "br-l?");
10194
10195     // align the center of el with the bottom left corner of other-el and
10196     // adjust the x position by -6 pixels (and the y position by 0)
10197     el.alignTo("other-el", "c-bl", [-6, 0]);
10198     </code></pre>
10199          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10200          * @param {String} position The position to align to.
10201          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10202          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10203          * @return {Roo.Element} this
10204          */
10205         alignTo : function(element, position, offsets, animate){
10206             var xy = this.getAlignToXY(element, position, offsets);
10207             this.setXY(xy, this.preanim(arguments, 3));
10208             return this;
10209         },
10210
10211         /**
10212          * Anchors an element to another element and realigns it when the window is resized.
10213          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10214          * @param {String} position The position to align to.
10215          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10216          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10217          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10218          * is a number, it is used as the buffer delay (defaults to 50ms).
10219          * @param {Function} callback The function to call after the animation finishes
10220          * @return {Roo.Element} this
10221          */
10222         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10223             var action = function(){
10224                 this.alignTo(el, alignment, offsets, animate);
10225                 Roo.callback(callback, this);
10226             };
10227             Roo.EventManager.onWindowResize(action, this);
10228             var tm = typeof monitorScroll;
10229             if(tm != 'undefined'){
10230                 Roo.EventManager.on(window, 'scroll', action, this,
10231                     {buffer: tm == 'number' ? monitorScroll : 50});
10232             }
10233             action.call(this); // align immediately
10234             return this;
10235         },
10236         /**
10237          * Clears any opacity settings from this element. Required in some cases for IE.
10238          * @return {Roo.Element} this
10239          */
10240         clearOpacity : function(){
10241             if (window.ActiveXObject) {
10242                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10243                     this.dom.style.filter = "";
10244                 }
10245             } else {
10246                 this.dom.style.opacity = "";
10247                 this.dom.style["-moz-opacity"] = "";
10248                 this.dom.style["-khtml-opacity"] = "";
10249             }
10250             return this;
10251         },
10252
10253         /**
10254          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10255          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10256          * @return {Roo.Element} this
10257          */
10258         hide : function(animate){
10259             this.setVisible(false, this.preanim(arguments, 0));
10260             return this;
10261         },
10262
10263         /**
10264         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10265         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10266          * @return {Roo.Element} this
10267          */
10268         show : function(animate){
10269             this.setVisible(true, this.preanim(arguments, 0));
10270             return this;
10271         },
10272
10273         /**
10274          * @private Test if size has a unit, otherwise appends the default
10275          */
10276         addUnits : function(size){
10277             return Roo.Element.addUnits(size, this.defaultUnit);
10278         },
10279
10280         /**
10281          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10282          * @return {Roo.Element} this
10283          */
10284         beginMeasure : function(){
10285             var el = this.dom;
10286             if(el.offsetWidth || el.offsetHeight){
10287                 return this; // offsets work already
10288             }
10289             var changed = [];
10290             var p = this.dom, b = document.body; // start with this element
10291             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10292                 var pe = Roo.get(p);
10293                 if(pe.getStyle('display') == 'none'){
10294                     changed.push({el: p, visibility: pe.getStyle("visibility")});
10295                     p.style.visibility = "hidden";
10296                     p.style.display = "block";
10297                 }
10298                 p = p.parentNode;
10299             }
10300             this._measureChanged = changed;
10301             return this;
10302
10303         },
10304
10305         /**
10306          * Restores displays to before beginMeasure was called
10307          * @return {Roo.Element} this
10308          */
10309         endMeasure : function(){
10310             var changed = this._measureChanged;
10311             if(changed){
10312                 for(var i = 0, len = changed.length; i < len; i++) {
10313                     var r = changed[i];
10314                     r.el.style.visibility = r.visibility;
10315                     r.el.style.display = "none";
10316                 }
10317                 this._measureChanged = null;
10318             }
10319             return this;
10320         },
10321
10322         /**
10323         * Update the innerHTML of this element, optionally searching for and processing scripts
10324         * @param {String} html The new HTML
10325         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10326         * @param {Function} callback For async script loading you can be noticed when the update completes
10327         * @return {Roo.Element} this
10328          */
10329         update : function(html, loadScripts, callback){
10330             if(typeof html == "undefined"){
10331                 html = "";
10332             }
10333             if(loadScripts !== true){
10334                 this.dom.innerHTML = html;
10335                 if(typeof callback == "function"){
10336                     callback();
10337                 }
10338                 return this;
10339             }
10340             var id = Roo.id();
10341             var dom = this.dom;
10342
10343             html += '<span id="' + id + '"></span>';
10344
10345             E.onAvailable(id, function(){
10346                 var hd = document.getElementsByTagName("head")[0];
10347                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10348                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10349                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10350
10351                 var match;
10352                 while(match = re.exec(html)){
10353                     var attrs = match[1];
10354                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10355                     if(srcMatch && srcMatch[2]){
10356                        var s = document.createElement("script");
10357                        s.src = srcMatch[2];
10358                        var typeMatch = attrs.match(typeRe);
10359                        if(typeMatch && typeMatch[2]){
10360                            s.type = typeMatch[2];
10361                        }
10362                        hd.appendChild(s);
10363                     }else if(match[2] && match[2].length > 0){
10364                         if(window.execScript) {
10365                            window.execScript(match[2]);
10366                         } else {
10367                             /**
10368                              * eval:var:id
10369                              * eval:var:dom
10370                              * eval:var:html
10371                              * 
10372                              */
10373                            window.eval(match[2]);
10374                         }
10375                     }
10376                 }
10377                 var el = document.getElementById(id);
10378                 if(el){el.parentNode.removeChild(el);}
10379                 if(typeof callback == "function"){
10380                     callback();
10381                 }
10382             });
10383             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10384             return this;
10385         },
10386
10387         /**
10388          * Direct access to the UpdateManager update() method (takes the same parameters).
10389          * @param {String/Function} url The url for this request or a function to call to get the url
10390          * @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}
10391          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10392          * @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.
10393          * @return {Roo.Element} this
10394          */
10395         load : function(){
10396             var um = this.getUpdateManager();
10397             um.update.apply(um, arguments);
10398             return this;
10399         },
10400
10401         /**
10402         * Gets this element's UpdateManager
10403         * @return {Roo.UpdateManager} The UpdateManager
10404         */
10405         getUpdateManager : function(){
10406             if(!this.updateManager){
10407                 this.updateManager = new Roo.UpdateManager(this);
10408             }
10409             return this.updateManager;
10410         },
10411
10412         /**
10413          * Disables text selection for this element (normalized across browsers)
10414          * @return {Roo.Element} this
10415          */
10416         unselectable : function(){
10417             this.dom.unselectable = "on";
10418             this.swallowEvent("selectstart", true);
10419             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10420             this.addClass("x-unselectable");
10421             return this;
10422         },
10423
10424         /**
10425         * Calculates the x, y to center this element on the screen
10426         * @return {Array} The x, y values [x, y]
10427         */
10428         getCenterXY : function(){
10429             return this.getAlignToXY(document, 'c-c');
10430         },
10431
10432         /**
10433         * Centers the Element in either the viewport, or another Element.
10434         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10435         */
10436         center : function(centerIn){
10437             this.alignTo(centerIn || document, 'c-c');
10438             return this;
10439         },
10440
10441         /**
10442          * Tests various css rules/browsers to determine if this element uses a border box
10443          * @return {Boolean}
10444          */
10445         isBorderBox : function(){
10446             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10447         },
10448
10449         /**
10450          * Return a box {x, y, width, height} that can be used to set another elements
10451          * size/location to match this element.
10452          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10453          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10454          * @return {Object} box An object in the format {x, y, width, height}
10455          */
10456         getBox : function(contentBox, local){
10457             var xy;
10458             if(!local){
10459                 xy = this.getXY();
10460             }else{
10461                 var left = parseInt(this.getStyle("left"), 10) || 0;
10462                 var top = parseInt(this.getStyle("top"), 10) || 0;
10463                 xy = [left, top];
10464             }
10465             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10466             if(!contentBox){
10467                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10468             }else{
10469                 var l = this.getBorderWidth("l")+this.getPadding("l");
10470                 var r = this.getBorderWidth("r")+this.getPadding("r");
10471                 var t = this.getBorderWidth("t")+this.getPadding("t");
10472                 var b = this.getBorderWidth("b")+this.getPadding("b");
10473                 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)};
10474             }
10475             bx.right = bx.x + bx.width;
10476             bx.bottom = bx.y + bx.height;
10477             return bx;
10478         },
10479
10480         /**
10481          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10482          for more information about the sides.
10483          * @param {String} sides
10484          * @return {Number}
10485          */
10486         getFrameWidth : function(sides, onlyContentBox){
10487             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10488         },
10489
10490         /**
10491          * 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.
10492          * @param {Object} box The box to fill {x, y, width, height}
10493          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10494          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10495          * @return {Roo.Element} this
10496          */
10497         setBox : function(box, adjust, animate){
10498             var w = box.width, h = box.height;
10499             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10500                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10501                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10502             }
10503             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10504             return this;
10505         },
10506
10507         /**
10508          * Forces the browser to repaint this element
10509          * @return {Roo.Element} this
10510          */
10511          repaint : function(){
10512             var dom = this.dom;
10513             this.addClass("x-repaint");
10514             setTimeout(function(){
10515                 Roo.get(dom).removeClass("x-repaint");
10516             }, 1);
10517             return this;
10518         },
10519
10520         /**
10521          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10522          * then it returns the calculated width of the sides (see getPadding)
10523          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10524          * @return {Object/Number}
10525          */
10526         getMargins : function(side){
10527             if(!side){
10528                 return {
10529                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10530                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10531                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10532                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10533                 };
10534             }else{
10535                 return this.addStyles(side, El.margins);
10536              }
10537         },
10538
10539         // private
10540         addStyles : function(sides, styles){
10541             var val = 0, v, w;
10542             for(var i = 0, len = sides.length; i < len; i++){
10543                 v = this.getStyle(styles[sides.charAt(i)]);
10544                 if(v){
10545                      w = parseInt(v, 10);
10546                      if(w){ val += w; }
10547                 }
10548             }
10549             return val;
10550         },
10551
10552         /**
10553          * Creates a proxy element of this element
10554          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10555          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10556          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10557          * @return {Roo.Element} The new proxy element
10558          */
10559         createProxy : function(config, renderTo, matchBox){
10560             if(renderTo){
10561                 renderTo = Roo.getDom(renderTo);
10562             }else{
10563                 renderTo = document.body;
10564             }
10565             config = typeof config == "object" ?
10566                 config : {tag : "div", cls: config};
10567             var proxy = Roo.DomHelper.append(renderTo, config, true);
10568             if(matchBox){
10569                proxy.setBox(this.getBox());
10570             }
10571             return proxy;
10572         },
10573
10574         /**
10575          * Puts a mask over this element to disable user interaction. Requires core.css.
10576          * This method can only be applied to elements which accept child nodes.
10577          * @param {String} msg (optional) A message to display in the mask
10578          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10579          * @return {Element} The mask  element
10580          */
10581         mask : function(msg, msgCls)
10582         {
10583             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10584                 this.setStyle("position", "relative");
10585             }
10586             if(!this._mask){
10587                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10588             }
10589             
10590             this.addClass("x-masked");
10591             this._mask.setDisplayed(true);
10592             
10593             // we wander
10594             var z = 0;
10595             var dom = this.dom;
10596             while (dom && dom.style) {
10597                 if (!isNaN(parseInt(dom.style.zIndex))) {
10598                     z = Math.max(z, parseInt(dom.style.zIndex));
10599                 }
10600                 dom = dom.parentNode;
10601             }
10602             // if we are masking the body - then it hides everything..
10603             if (this.dom == document.body) {
10604                 z = 1000000;
10605                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10606                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10607             }
10608            
10609             if(typeof msg == 'string'){
10610                 if(!this._maskMsg){
10611                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10612                         cls: "roo-el-mask-msg", 
10613                         cn: [
10614                             {
10615                                 tag: 'i',
10616                                 cls: 'fa fa-spinner fa-spin'
10617                             },
10618                             {
10619                                 tag: 'div'
10620                             }   
10621                         ]
10622                     }, true);
10623                 }
10624                 var mm = this._maskMsg;
10625                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10626                 if (mm.dom.lastChild) { // weird IE issue?
10627                     mm.dom.lastChild.innerHTML = msg;
10628                 }
10629                 mm.setDisplayed(true);
10630                 mm.center(this);
10631                 mm.setStyle('z-index', z + 102);
10632             }
10633             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10634                 this._mask.setHeight(this.getHeight());
10635             }
10636             this._mask.setStyle('z-index', z + 100);
10637             
10638             return this._mask;
10639         },
10640
10641         /**
10642          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10643          * it is cached for reuse.
10644          */
10645         unmask : function(removeEl){
10646             if(this._mask){
10647                 if(removeEl === true){
10648                     this._mask.remove();
10649                     delete this._mask;
10650                     if(this._maskMsg){
10651                         this._maskMsg.remove();
10652                         delete this._maskMsg;
10653                     }
10654                 }else{
10655                     this._mask.setDisplayed(false);
10656                     if(this._maskMsg){
10657                         this._maskMsg.setDisplayed(false);
10658                     }
10659                 }
10660             }
10661             this.removeClass("x-masked");
10662         },
10663
10664         /**
10665          * Returns true if this element is masked
10666          * @return {Boolean}
10667          */
10668         isMasked : function(){
10669             return this._mask && this._mask.isVisible();
10670         },
10671
10672         /**
10673          * Creates an iframe shim for this element to keep selects and other windowed objects from
10674          * showing through.
10675          * @return {Roo.Element} The new shim element
10676          */
10677         createShim : function(){
10678             var el = document.createElement('iframe');
10679             el.frameBorder = 'no';
10680             el.className = 'roo-shim';
10681             if(Roo.isIE && Roo.isSecure){
10682                 el.src = Roo.SSL_SECURE_URL;
10683             }
10684             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10685             shim.autoBoxAdjust = false;
10686             return shim;
10687         },
10688
10689         /**
10690          * Removes this element from the DOM and deletes it from the cache
10691          */
10692         remove : function(){
10693             if(this.dom.parentNode){
10694                 this.dom.parentNode.removeChild(this.dom);
10695             }
10696             delete El.cache[this.dom.id];
10697         },
10698
10699         /**
10700          * Sets up event handlers to add and remove a css class when the mouse is over this element
10701          * @param {String} className
10702          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10703          * mouseout events for children elements
10704          * @return {Roo.Element} this
10705          */
10706         addClassOnOver : function(className, preventFlicker){
10707             this.on("mouseover", function(){
10708                 Roo.fly(this, '_internal').addClass(className);
10709             }, this.dom);
10710             var removeFn = function(e){
10711                 if(preventFlicker !== true || !e.within(this, true)){
10712                     Roo.fly(this, '_internal').removeClass(className);
10713                 }
10714             };
10715             this.on("mouseout", removeFn, this.dom);
10716             return this;
10717         },
10718
10719         /**
10720          * Sets up event handlers to add and remove a css class when this element has the focus
10721          * @param {String} className
10722          * @return {Roo.Element} this
10723          */
10724         addClassOnFocus : function(className){
10725             this.on("focus", function(){
10726                 Roo.fly(this, '_internal').addClass(className);
10727             }, this.dom);
10728             this.on("blur", function(){
10729                 Roo.fly(this, '_internal').removeClass(className);
10730             }, this.dom);
10731             return this;
10732         },
10733         /**
10734          * 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)
10735          * @param {String} className
10736          * @return {Roo.Element} this
10737          */
10738         addClassOnClick : function(className){
10739             var dom = this.dom;
10740             this.on("mousedown", function(){
10741                 Roo.fly(dom, '_internal').addClass(className);
10742                 var d = Roo.get(document);
10743                 var fn = function(){
10744                     Roo.fly(dom, '_internal').removeClass(className);
10745                     d.removeListener("mouseup", fn);
10746                 };
10747                 d.on("mouseup", fn);
10748             });
10749             return this;
10750         },
10751
10752         /**
10753          * Stops the specified event from bubbling and optionally prevents the default action
10754          * @param {String} eventName
10755          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10756          * @return {Roo.Element} this
10757          */
10758         swallowEvent : function(eventName, preventDefault){
10759             var fn = function(e){
10760                 e.stopPropagation();
10761                 if(preventDefault){
10762                     e.preventDefault();
10763                 }
10764             };
10765             if(eventName instanceof Array){
10766                 for(var i = 0, len = eventName.length; i < len; i++){
10767                      this.on(eventName[i], fn);
10768                 }
10769                 return this;
10770             }
10771             this.on(eventName, fn);
10772             return this;
10773         },
10774
10775         /**
10776          * @private
10777          */
10778         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10779
10780         /**
10781          * Sizes this element to its parent element's dimensions performing
10782          * neccessary box adjustments.
10783          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10784          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10785          * @return {Roo.Element} this
10786          */
10787         fitToParent : function(monitorResize, targetParent) {
10788           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10789           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10790           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10791             return this;
10792           }
10793           var p = Roo.get(targetParent || this.dom.parentNode);
10794           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10795           if (monitorResize === true) {
10796             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10797             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10798           }
10799           return this;
10800         },
10801
10802         /**
10803          * Gets the next sibling, skipping text nodes
10804          * @return {HTMLElement} The next sibling or null
10805          */
10806         getNextSibling : function(){
10807             var n = this.dom.nextSibling;
10808             while(n && n.nodeType != 1){
10809                 n = n.nextSibling;
10810             }
10811             return n;
10812         },
10813
10814         /**
10815          * Gets the previous sibling, skipping text nodes
10816          * @return {HTMLElement} The previous sibling or null
10817          */
10818         getPrevSibling : function(){
10819             var n = this.dom.previousSibling;
10820             while(n && n.nodeType != 1){
10821                 n = n.previousSibling;
10822             }
10823             return n;
10824         },
10825
10826
10827         /**
10828          * Appends the passed element(s) to this element
10829          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10830          * @return {Roo.Element} this
10831          */
10832         appendChild: function(el){
10833             el = Roo.get(el);
10834             el.appendTo(this);
10835             return this;
10836         },
10837
10838         /**
10839          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10840          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10841          * automatically generated with the specified attributes.
10842          * @param {HTMLElement} insertBefore (optional) a child element of this element
10843          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10844          * @return {Roo.Element} The new child element
10845          */
10846         createChild: function(config, insertBefore, returnDom){
10847             config = config || {tag:'div'};
10848             if(insertBefore){
10849                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10850             }
10851             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10852         },
10853
10854         /**
10855          * Appends this element to the passed element
10856          * @param {String/HTMLElement/Element} el The new parent element
10857          * @return {Roo.Element} this
10858          */
10859         appendTo: function(el){
10860             el = Roo.getDom(el);
10861             el.appendChild(this.dom);
10862             return this;
10863         },
10864
10865         /**
10866          * Inserts this element before the passed element in the DOM
10867          * @param {String/HTMLElement/Element} el The element to insert before
10868          * @return {Roo.Element} this
10869          */
10870         insertBefore: function(el){
10871             el = Roo.getDom(el);
10872             el.parentNode.insertBefore(this.dom, el);
10873             return this;
10874         },
10875
10876         /**
10877          * Inserts this element after the passed element in the DOM
10878          * @param {String/HTMLElement/Element} el The element to insert after
10879          * @return {Roo.Element} this
10880          */
10881         insertAfter: function(el){
10882             el = Roo.getDom(el);
10883             el.parentNode.insertBefore(this.dom, el.nextSibling);
10884             return this;
10885         },
10886
10887         /**
10888          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10889          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10890          * @return {Roo.Element} The new child
10891          */
10892         insertFirst: function(el, returnDom){
10893             el = el || {};
10894             if(typeof el == 'object' && !el.nodeType){ // dh config
10895                 return this.createChild(el, this.dom.firstChild, returnDom);
10896             }else{
10897                 el = Roo.getDom(el);
10898                 this.dom.insertBefore(el, this.dom.firstChild);
10899                 return !returnDom ? Roo.get(el) : el;
10900             }
10901         },
10902
10903         /**
10904          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10905          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10906          * @param {String} where (optional) 'before' or 'after' defaults to before
10907          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10908          * @return {Roo.Element} the inserted Element
10909          */
10910         insertSibling: function(el, where, returnDom){
10911             where = where ? where.toLowerCase() : 'before';
10912             el = el || {};
10913             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10914
10915             if(typeof el == 'object' && !el.nodeType){ // dh config
10916                 if(where == 'after' && !this.dom.nextSibling){
10917                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10918                 }else{
10919                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10920                 }
10921
10922             }else{
10923                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10924                             where == 'before' ? this.dom : this.dom.nextSibling);
10925                 if(!returnDom){
10926                     rt = Roo.get(rt);
10927                 }
10928             }
10929             return rt;
10930         },
10931
10932         /**
10933          * Creates and wraps this element with another element
10934          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10935          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10936          * @return {HTMLElement/Element} The newly created wrapper element
10937          */
10938         wrap: function(config, returnDom){
10939             if(!config){
10940                 config = {tag: "div"};
10941             }
10942             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10943             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10944             return newEl;
10945         },
10946
10947         /**
10948          * Replaces the passed element with this element
10949          * @param {String/HTMLElement/Element} el The element to replace
10950          * @return {Roo.Element} this
10951          */
10952         replace: function(el){
10953             el = Roo.get(el);
10954             this.insertBefore(el);
10955             el.remove();
10956             return this;
10957         },
10958
10959         /**
10960          * Inserts an html fragment into this element
10961          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10962          * @param {String} html The HTML fragment
10963          * @param {Boolean} returnEl True to return an Roo.Element
10964          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10965          */
10966         insertHtml : function(where, html, returnEl){
10967             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10968             return returnEl ? Roo.get(el) : el;
10969         },
10970
10971         /**
10972          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10973          * @param {Object} o The object with the attributes
10974          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10975          * @return {Roo.Element} this
10976          */
10977         set : function(o, useSet){
10978             var el = this.dom;
10979             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10980             for(var attr in o){
10981                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10982                 if(attr=="cls"){
10983                     el.className = o["cls"];
10984                 }else{
10985                     if(useSet) {
10986                         el.setAttribute(attr, o[attr]);
10987                     } else {
10988                         el[attr] = o[attr];
10989                     }
10990                 }
10991             }
10992             if(o.style){
10993                 Roo.DomHelper.applyStyles(el, o.style);
10994             }
10995             return this;
10996         },
10997
10998         /**
10999          * Convenience method for constructing a KeyMap
11000          * @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:
11001          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
11002          * @param {Function} fn The function to call
11003          * @param {Object} scope (optional) The scope of the function
11004          * @return {Roo.KeyMap} The KeyMap created
11005          */
11006         addKeyListener : function(key, fn, scope){
11007             var config;
11008             if(typeof key != "object" || key instanceof Array){
11009                 config = {
11010                     key: key,
11011                     fn: fn,
11012                     scope: scope
11013                 };
11014             }else{
11015                 config = {
11016                     key : key.key,
11017                     shift : key.shift,
11018                     ctrl : key.ctrl,
11019                     alt : key.alt,
11020                     fn: fn,
11021                     scope: scope
11022                 };
11023             }
11024             return new Roo.KeyMap(this, config);
11025         },
11026
11027         /**
11028          * Creates a KeyMap for this element
11029          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
11030          * @return {Roo.KeyMap} The KeyMap created
11031          */
11032         addKeyMap : function(config){
11033             return new Roo.KeyMap(this, config);
11034         },
11035
11036         /**
11037          * Returns true if this element is scrollable.
11038          * @return {Boolean}
11039          */
11040          isScrollable : function(){
11041             var dom = this.dom;
11042             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
11043         },
11044
11045         /**
11046          * 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().
11047          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
11048          * @param {Number} value The new scroll value
11049          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11050          * @return {Element} this
11051          */
11052
11053         scrollTo : function(side, value, animate){
11054             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
11055             if(!animate || !A){
11056                 this.dom[prop] = value;
11057             }else{
11058                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
11059                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
11060             }
11061             return this;
11062         },
11063
11064         /**
11065          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
11066          * within this element's scrollable range.
11067          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
11068          * @param {Number} distance How far to scroll the element in pixels
11069          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
11070          * @return {Boolean} Returns true if a scroll was triggered or false if the element
11071          * was scrolled as far as it could go.
11072          */
11073          scroll : function(direction, distance, animate){
11074              if(!this.isScrollable()){
11075                  return;
11076              }
11077              var el = this.dom;
11078              var l = el.scrollLeft, t = el.scrollTop;
11079              var w = el.scrollWidth, h = el.scrollHeight;
11080              var cw = el.clientWidth, ch = el.clientHeight;
11081              direction = direction.toLowerCase();
11082              var scrolled = false;
11083              var a = this.preanim(arguments, 2);
11084              switch(direction){
11085                  case "l":
11086                  case "left":
11087                      if(w - l > cw){
11088                          var v = Math.min(l + distance, w-cw);
11089                          this.scrollTo("left", v, a);
11090                          scrolled = true;
11091                      }
11092                      break;
11093                 case "r":
11094                 case "right":
11095                      if(l > 0){
11096                          var v = Math.max(l - distance, 0);
11097                          this.scrollTo("left", v, a);
11098                          scrolled = true;
11099                      }
11100                      break;
11101                 case "t":
11102                 case "top":
11103                 case "up":
11104                      if(t > 0){
11105                          var v = Math.max(t - distance, 0);
11106                          this.scrollTo("top", v, a);
11107                          scrolled = true;
11108                      }
11109                      break;
11110                 case "b":
11111                 case "bottom":
11112                 case "down":
11113                      if(h - t > ch){
11114                          var v = Math.min(t + distance, h-ch);
11115                          this.scrollTo("top", v, a);
11116                          scrolled = true;
11117                      }
11118                      break;
11119              }
11120              return scrolled;
11121         },
11122
11123         /**
11124          * Translates the passed page coordinates into left/top css values for this element
11125          * @param {Number/Array} x The page x or an array containing [x, y]
11126          * @param {Number} y The page y
11127          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
11128          */
11129         translatePoints : function(x, y){
11130             if(typeof x == 'object' || x instanceof Array){
11131                 y = x[1]; x = x[0];
11132             }
11133             var p = this.getStyle('position');
11134             var o = this.getXY();
11135
11136             var l = parseInt(this.getStyle('left'), 10);
11137             var t = parseInt(this.getStyle('top'), 10);
11138
11139             if(isNaN(l)){
11140                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
11141             }
11142             if(isNaN(t)){
11143                 t = (p == "relative") ? 0 : this.dom.offsetTop;
11144             }
11145
11146             return {left: (x - o[0] + l), top: (y - o[1] + t)};
11147         },
11148
11149         /**
11150          * Returns the current scroll position of the element.
11151          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
11152          */
11153         getScroll : function(){
11154             var d = this.dom, doc = document;
11155             if(d == doc || d == doc.body){
11156                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
11157                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
11158                 return {left: l, top: t};
11159             }else{
11160                 return {left: d.scrollLeft, top: d.scrollTop};
11161             }
11162         },
11163
11164         /**
11165          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
11166          * are convert to standard 6 digit hex color.
11167          * @param {String} attr The css attribute
11168          * @param {String} defaultValue The default value to use when a valid color isn't found
11169          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
11170          * YUI color anims.
11171          */
11172         getColor : function(attr, defaultValue, prefix){
11173             var v = this.getStyle(attr);
11174             if(!v || v == "transparent" || v == "inherit") {
11175                 return defaultValue;
11176             }
11177             var color = typeof prefix == "undefined" ? "#" : prefix;
11178             if(v.substr(0, 4) == "rgb("){
11179                 var rvs = v.slice(4, v.length -1).split(",");
11180                 for(var i = 0; i < 3; i++){
11181                     var h = parseInt(rvs[i]).toString(16);
11182                     if(h < 16){
11183                         h = "0" + h;
11184                     }
11185                     color += h;
11186                 }
11187             } else {
11188                 if(v.substr(0, 1) == "#"){
11189                     if(v.length == 4) {
11190                         for(var i = 1; i < 4; i++){
11191                             var c = v.charAt(i);
11192                             color +=  c + c;
11193                         }
11194                     }else if(v.length == 7){
11195                         color += v.substr(1);
11196                     }
11197                 }
11198             }
11199             return(color.length > 5 ? color.toLowerCase() : defaultValue);
11200         },
11201
11202         /**
11203          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11204          * gradient background, rounded corners and a 4-way shadow.
11205          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11206          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11207          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11208          * @return {Roo.Element} this
11209          */
11210         boxWrap : function(cls){
11211             cls = cls || 'x-box';
11212             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11213             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11214             return el;
11215         },
11216
11217         /**
11218          * Returns the value of a namespaced attribute from the element's underlying DOM node.
11219          * @param {String} namespace The namespace in which to look for the attribute
11220          * @param {String} name The attribute name
11221          * @return {String} The attribute value
11222          */
11223         getAttributeNS : Roo.isIE ? function(ns, name){
11224             var d = this.dom;
11225             var type = typeof d[ns+":"+name];
11226             if(type != 'undefined' && type != 'unknown'){
11227                 return d[ns+":"+name];
11228             }
11229             return d[name];
11230         } : function(ns, name){
11231             var d = this.dom;
11232             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11233         },
11234         
11235         
11236         /**
11237          * Sets or Returns the value the dom attribute value
11238          * @param {String|Object} name The attribute name (or object to set multiple attributes)
11239          * @param {String} value (optional) The value to set the attribute to
11240          * @return {String} The attribute value
11241          */
11242         attr : function(name){
11243             if (arguments.length > 1) {
11244                 this.dom.setAttribute(name, arguments[1]);
11245                 return arguments[1];
11246             }
11247             if (typeof(name) == 'object') {
11248                 for(var i in name) {
11249                     this.attr(i, name[i]);
11250                 }
11251                 return name;
11252             }
11253             
11254             
11255             if (!this.dom.hasAttribute(name)) {
11256                 return undefined;
11257             }
11258             return this.dom.getAttribute(name);
11259         }
11260         
11261         
11262         
11263     };
11264
11265     var ep = El.prototype;
11266
11267     /**
11268      * Appends an event handler (Shorthand for addListener)
11269      * @param {String}   eventName     The type of event to append
11270      * @param {Function} fn        The method the event invokes
11271      * @param {Object} scope       (optional) The scope (this object) of the fn
11272      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
11273      * @method
11274      */
11275     ep.on = ep.addListener;
11276         // backwards compat
11277     ep.mon = ep.addListener;
11278
11279     /**
11280      * Removes an event handler from this element (shorthand for removeListener)
11281      * @param {String} eventName the type of event to remove
11282      * @param {Function} fn the method the event invokes
11283      * @return {Roo.Element} this
11284      * @method
11285      */
11286     ep.un = ep.removeListener;
11287
11288     /**
11289      * true to automatically adjust width and height settings for box-model issues (default to true)
11290      */
11291     ep.autoBoxAdjust = true;
11292
11293     // private
11294     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11295
11296     // private
11297     El.addUnits = function(v, defaultUnit){
11298         if(v === "" || v == "auto"){
11299             return v;
11300         }
11301         if(v === undefined){
11302             return '';
11303         }
11304         if(typeof v == "number" || !El.unitPattern.test(v)){
11305             return v + (defaultUnit || 'px');
11306         }
11307         return v;
11308     };
11309
11310     // special markup used throughout Roo when box wrapping elements
11311     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>';
11312     /**
11313      * Visibility mode constant - Use visibility to hide element
11314      * @static
11315      * @type Number
11316      */
11317     El.VISIBILITY = 1;
11318     /**
11319      * Visibility mode constant - Use display to hide element
11320      * @static
11321      * @type Number
11322      */
11323     El.DISPLAY = 2;
11324
11325     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11326     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11327     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11328
11329
11330
11331     /**
11332      * @private
11333      */
11334     El.cache = {};
11335
11336     var docEl;
11337
11338     /**
11339      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11340      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11341      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11342      * @return {Element} The Element object
11343      * @static
11344      */
11345     El.get = function(el){
11346         var ex, elm, id;
11347         if(!el){ return null; }
11348         if(typeof el == "string"){ // element id
11349             if(!(elm = document.getElementById(el))){
11350                 return null;
11351             }
11352             if(ex = El.cache[el]){
11353                 ex.dom = elm;
11354             }else{
11355                 ex = El.cache[el] = new El(elm);
11356             }
11357             return ex;
11358         }else if(el.tagName){ // dom element
11359             if(!(id = el.id)){
11360                 id = Roo.id(el);
11361             }
11362             if(ex = El.cache[id]){
11363                 ex.dom = el;
11364             }else{
11365                 ex = El.cache[id] = new El(el);
11366             }
11367             return ex;
11368         }else if(el instanceof El){
11369             if(el != docEl){
11370                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11371                                                               // catch case where it hasn't been appended
11372                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11373             }
11374             return el;
11375         }else if(el.isComposite){
11376             return el;
11377         }else if(el instanceof Array){
11378             return El.select(el);
11379         }else if(el == document){
11380             // create a bogus element object representing the document object
11381             if(!docEl){
11382                 var f = function(){};
11383                 f.prototype = El.prototype;
11384                 docEl = new f();
11385                 docEl.dom = document;
11386             }
11387             return docEl;
11388         }
11389         return null;
11390     };
11391
11392     // private
11393     El.uncache = function(el){
11394         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11395             if(a[i]){
11396                 delete El.cache[a[i].id || a[i]];
11397             }
11398         }
11399     };
11400
11401     // private
11402     // Garbage collection - uncache elements/purge listeners on orphaned elements
11403     // so we don't hold a reference and cause the browser to retain them
11404     El.garbageCollect = function(){
11405         if(!Roo.enableGarbageCollector){
11406             clearInterval(El.collectorThread);
11407             return;
11408         }
11409         for(var eid in El.cache){
11410             var el = El.cache[eid], d = el.dom;
11411             // -------------------------------------------------------
11412             // Determining what is garbage:
11413             // -------------------------------------------------------
11414             // !d
11415             // dom node is null, definitely garbage
11416             // -------------------------------------------------------
11417             // !d.parentNode
11418             // no parentNode == direct orphan, definitely garbage
11419             // -------------------------------------------------------
11420             // !d.offsetParent && !document.getElementById(eid)
11421             // display none elements have no offsetParent so we will
11422             // also try to look it up by it's id. However, check
11423             // offsetParent first so we don't do unneeded lookups.
11424             // This enables collection of elements that are not orphans
11425             // directly, but somewhere up the line they have an orphan
11426             // parent.
11427             // -------------------------------------------------------
11428             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11429                 delete El.cache[eid];
11430                 if(d && Roo.enableListenerCollection){
11431                     E.purgeElement(d);
11432                 }
11433             }
11434         }
11435     }
11436     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11437
11438
11439     // dom is optional
11440     El.Flyweight = function(dom){
11441         this.dom = dom;
11442     };
11443     El.Flyweight.prototype = El.prototype;
11444
11445     El._flyweights = {};
11446     /**
11447      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11448      * the dom node can be overwritten by other code.
11449      * @param {String/HTMLElement} el The dom node or id
11450      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11451      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11452      * @static
11453      * @return {Element} The shared Element object
11454      */
11455     El.fly = function(el, named){
11456         named = named || '_global';
11457         el = Roo.getDom(el);
11458         if(!el){
11459             return null;
11460         }
11461         if(!El._flyweights[named]){
11462             El._flyweights[named] = new El.Flyweight();
11463         }
11464         El._flyweights[named].dom = el;
11465         return El._flyweights[named];
11466     };
11467
11468     /**
11469      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11470      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11471      * Shorthand of {@link Roo.Element#get}
11472      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11473      * @return {Element} The Element object
11474      * @member Roo
11475      * @method get
11476      */
11477     Roo.get = El.get;
11478     /**
11479      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11480      * the dom node can be overwritten by other code.
11481      * Shorthand of {@link Roo.Element#fly}
11482      * @param {String/HTMLElement} el The dom node or id
11483      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11484      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11485      * @static
11486      * @return {Element} The shared Element object
11487      * @member Roo
11488      * @method fly
11489      */
11490     Roo.fly = El.fly;
11491
11492     // speedy lookup for elements never to box adjust
11493     var noBoxAdjust = Roo.isStrict ? {
11494         select:1
11495     } : {
11496         input:1, select:1, textarea:1
11497     };
11498     if(Roo.isIE || Roo.isGecko){
11499         noBoxAdjust['button'] = 1;
11500     }
11501
11502
11503     Roo.EventManager.on(window, 'unload', function(){
11504         delete El.cache;
11505         delete El._flyweights;
11506     });
11507 })();
11508
11509
11510
11511
11512 if(Roo.DomQuery){
11513     Roo.Element.selectorFunction = Roo.DomQuery.select;
11514 }
11515
11516 Roo.Element.select = function(selector, unique, root){
11517     var els;
11518     if(typeof selector == "string"){
11519         els = Roo.Element.selectorFunction(selector, root);
11520     }else if(selector.length !== undefined){
11521         els = selector;
11522     }else{
11523         throw "Invalid selector";
11524     }
11525     if(unique === true){
11526         return new Roo.CompositeElement(els);
11527     }else{
11528         return new Roo.CompositeElementLite(els);
11529     }
11530 };
11531 /**
11532  * Selects elements based on the passed CSS selector to enable working on them as 1.
11533  * @param {String/Array} selector The CSS selector or an array of elements
11534  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11535  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11536  * @return {CompositeElementLite/CompositeElement}
11537  * @member Roo
11538  * @method select
11539  */
11540 Roo.select = Roo.Element.select;
11541
11542
11543
11544
11545
11546
11547
11548
11549
11550
11551
11552
11553
11554
11555 /*
11556  * Based on:
11557  * Ext JS Library 1.1.1
11558  * Copyright(c) 2006-2007, Ext JS, LLC.
11559  *
11560  * Originally Released Under LGPL - original licence link has changed is not relivant.
11561  *
11562  * Fork - LGPL
11563  * <script type="text/javascript">
11564  */
11565
11566
11567
11568 //Notifies Element that fx methods are available
11569 Roo.enableFx = true;
11570
11571 /**
11572  * @class Roo.Fx
11573  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11574  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11575  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11576  * Element effects to work.</p><br/>
11577  *
11578  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11579  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11580  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11581  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11582  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11583  * expected results and should be done with care.</p><br/>
11584  *
11585  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11586  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11587 <pre>
11588 Value  Description
11589 -----  -----------------------------
11590 tl     The top left corner
11591 t      The center of the top edge
11592 tr     The top right corner
11593 l      The center of the left edge
11594 r      The center of the right edge
11595 bl     The bottom left corner
11596 b      The center of the bottom edge
11597 br     The bottom right corner
11598 </pre>
11599  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11600  * below are common options that can be passed to any Fx method.</b>
11601  * @cfg {Function} callback A function called when the effect is finished
11602  * @cfg {Object} scope The scope of the effect function
11603  * @cfg {String} easing A valid Easing value for the effect
11604  * @cfg {String} afterCls A css class to apply after the effect
11605  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11606  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11607  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11608  * effects that end with the element being visually hidden, ignored otherwise)
11609  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11610  * a function which returns such a specification that will be applied to the Element after the effect finishes
11611  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11612  * @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
11613  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11614  */
11615 Roo.Fx = {
11616         /**
11617          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11618          * origin for the slide effect.  This function automatically handles wrapping the element with
11619          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11620          * Usage:
11621          *<pre><code>
11622 // default: slide the element in from the top
11623 el.slideIn();
11624
11625 // custom: slide the element in from the right with a 2-second duration
11626 el.slideIn('r', { duration: 2 });
11627
11628 // common config options shown with default values
11629 el.slideIn('t', {
11630     easing: 'easeOut',
11631     duration: .5
11632 });
11633 </code></pre>
11634          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11635          * @param {Object} options (optional) Object literal with any of the Fx config options
11636          * @return {Roo.Element} The Element
11637          */
11638     slideIn : function(anchor, o){
11639         var el = this.getFxEl();
11640         o = o || {};
11641
11642         el.queueFx(o, function(){
11643
11644             anchor = anchor || "t";
11645
11646             // fix display to visibility
11647             this.fixDisplay();
11648
11649             // restore values after effect
11650             var r = this.getFxRestore();
11651             var b = this.getBox();
11652             // fixed size for slide
11653             this.setSize(b);
11654
11655             // wrap if needed
11656             var wrap = this.fxWrap(r.pos, o, "hidden");
11657
11658             var st = this.dom.style;
11659             st.visibility = "visible";
11660             st.position = "absolute";
11661
11662             // clear out temp styles after slide and unwrap
11663             var after = function(){
11664                 el.fxUnwrap(wrap, r.pos, o);
11665                 st.width = r.width;
11666                 st.height = r.height;
11667                 el.afterFx(o);
11668             };
11669             // time to calc the positions
11670             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11671
11672             switch(anchor.toLowerCase()){
11673                 case "t":
11674                     wrap.setSize(b.width, 0);
11675                     st.left = st.bottom = "0";
11676                     a = {height: bh};
11677                 break;
11678                 case "l":
11679                     wrap.setSize(0, b.height);
11680                     st.right = st.top = "0";
11681                     a = {width: bw};
11682                 break;
11683                 case "r":
11684                     wrap.setSize(0, b.height);
11685                     wrap.setX(b.right);
11686                     st.left = st.top = "0";
11687                     a = {width: bw, points: pt};
11688                 break;
11689                 case "b":
11690                     wrap.setSize(b.width, 0);
11691                     wrap.setY(b.bottom);
11692                     st.left = st.top = "0";
11693                     a = {height: bh, points: pt};
11694                 break;
11695                 case "tl":
11696                     wrap.setSize(0, 0);
11697                     st.right = st.bottom = "0";
11698                     a = {width: bw, height: bh};
11699                 break;
11700                 case "bl":
11701                     wrap.setSize(0, 0);
11702                     wrap.setY(b.y+b.height);
11703                     st.right = st.top = "0";
11704                     a = {width: bw, height: bh, points: pt};
11705                 break;
11706                 case "br":
11707                     wrap.setSize(0, 0);
11708                     wrap.setXY([b.right, b.bottom]);
11709                     st.left = st.top = "0";
11710                     a = {width: bw, height: bh, points: pt};
11711                 break;
11712                 case "tr":
11713                     wrap.setSize(0, 0);
11714                     wrap.setX(b.x+b.width);
11715                     st.left = st.bottom = "0";
11716                     a = {width: bw, height: bh, points: pt};
11717                 break;
11718             }
11719             this.dom.style.visibility = "visible";
11720             wrap.show();
11721
11722             arguments.callee.anim = wrap.fxanim(a,
11723                 o,
11724                 'motion',
11725                 .5,
11726                 'easeOut', after);
11727         });
11728         return this;
11729     },
11730     
11731         /**
11732          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11733          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11734          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11735          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11736          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11737          * Usage:
11738          *<pre><code>
11739 // default: slide the element out to the top
11740 el.slideOut();
11741
11742 // custom: slide the element out to the right with a 2-second duration
11743 el.slideOut('r', { duration: 2 });
11744
11745 // common config options shown with default values
11746 el.slideOut('t', {
11747     easing: 'easeOut',
11748     duration: .5,
11749     remove: false,
11750     useDisplay: false
11751 });
11752 </code></pre>
11753          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11754          * @param {Object} options (optional) Object literal with any of the Fx config options
11755          * @return {Roo.Element} The Element
11756          */
11757     slideOut : function(anchor, o){
11758         var el = this.getFxEl();
11759         o = o || {};
11760
11761         el.queueFx(o, function(){
11762
11763             anchor = anchor || "t";
11764
11765             // restore values after effect
11766             var r = this.getFxRestore();
11767             
11768             var b = this.getBox();
11769             // fixed size for slide
11770             this.setSize(b);
11771
11772             // wrap if needed
11773             var wrap = this.fxWrap(r.pos, o, "visible");
11774
11775             var st = this.dom.style;
11776             st.visibility = "visible";
11777             st.position = "absolute";
11778
11779             wrap.setSize(b);
11780
11781             var after = function(){
11782                 if(o.useDisplay){
11783                     el.setDisplayed(false);
11784                 }else{
11785                     el.hide();
11786                 }
11787
11788                 el.fxUnwrap(wrap, r.pos, o);
11789
11790                 st.width = r.width;
11791                 st.height = r.height;
11792
11793                 el.afterFx(o);
11794             };
11795
11796             var a, zero = {to: 0};
11797             switch(anchor.toLowerCase()){
11798                 case "t":
11799                     st.left = st.bottom = "0";
11800                     a = {height: zero};
11801                 break;
11802                 case "l":
11803                     st.right = st.top = "0";
11804                     a = {width: zero};
11805                 break;
11806                 case "r":
11807                     st.left = st.top = "0";
11808                     a = {width: zero, points: {to:[b.right, b.y]}};
11809                 break;
11810                 case "b":
11811                     st.left = st.top = "0";
11812                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11813                 break;
11814                 case "tl":
11815                     st.right = st.bottom = "0";
11816                     a = {width: zero, height: zero};
11817                 break;
11818                 case "bl":
11819                     st.right = st.top = "0";
11820                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11821                 break;
11822                 case "br":
11823                     st.left = st.top = "0";
11824                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11825                 break;
11826                 case "tr":
11827                     st.left = st.bottom = "0";
11828                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11829                 break;
11830             }
11831
11832             arguments.callee.anim = wrap.fxanim(a,
11833                 o,
11834                 'motion',
11835                 .5,
11836                 "easeOut", after);
11837         });
11838         return this;
11839     },
11840
11841         /**
11842          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11843          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11844          * The element must be removed from the DOM using the 'remove' config option if desired.
11845          * Usage:
11846          *<pre><code>
11847 // default
11848 el.puff();
11849
11850 // common config options shown with default values
11851 el.puff({
11852     easing: 'easeOut',
11853     duration: .5,
11854     remove: false,
11855     useDisplay: false
11856 });
11857 </code></pre>
11858          * @param {Object} options (optional) Object literal with any of the Fx config options
11859          * @return {Roo.Element} The Element
11860          */
11861     puff : function(o){
11862         var el = this.getFxEl();
11863         o = o || {};
11864
11865         el.queueFx(o, function(){
11866             this.clearOpacity();
11867             this.show();
11868
11869             // restore values after effect
11870             var r = this.getFxRestore();
11871             var st = this.dom.style;
11872
11873             var after = function(){
11874                 if(o.useDisplay){
11875                     el.setDisplayed(false);
11876                 }else{
11877                     el.hide();
11878                 }
11879
11880                 el.clearOpacity();
11881
11882                 el.setPositioning(r.pos);
11883                 st.width = r.width;
11884                 st.height = r.height;
11885                 st.fontSize = '';
11886                 el.afterFx(o);
11887             };
11888
11889             var width = this.getWidth();
11890             var height = this.getHeight();
11891
11892             arguments.callee.anim = this.fxanim({
11893                     width : {to: this.adjustWidth(width * 2)},
11894                     height : {to: this.adjustHeight(height * 2)},
11895                     points : {by: [-(width * .5), -(height * .5)]},
11896                     opacity : {to: 0},
11897                     fontSize: {to:200, unit: "%"}
11898                 },
11899                 o,
11900                 'motion',
11901                 .5,
11902                 "easeOut", after);
11903         });
11904         return this;
11905     },
11906
11907         /**
11908          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11909          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11910          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11911          * Usage:
11912          *<pre><code>
11913 // default
11914 el.switchOff();
11915
11916 // all config options shown with default values
11917 el.switchOff({
11918     easing: 'easeIn',
11919     duration: .3,
11920     remove: false,
11921     useDisplay: false
11922 });
11923 </code></pre>
11924          * @param {Object} options (optional) Object literal with any of the Fx config options
11925          * @return {Roo.Element} The Element
11926          */
11927     switchOff : function(o){
11928         var el = this.getFxEl();
11929         o = o || {};
11930
11931         el.queueFx(o, function(){
11932             this.clearOpacity();
11933             this.clip();
11934
11935             // restore values after effect
11936             var r = this.getFxRestore();
11937             var st = this.dom.style;
11938
11939             var after = function(){
11940                 if(o.useDisplay){
11941                     el.setDisplayed(false);
11942                 }else{
11943                     el.hide();
11944                 }
11945
11946                 el.clearOpacity();
11947                 el.setPositioning(r.pos);
11948                 st.width = r.width;
11949                 st.height = r.height;
11950
11951                 el.afterFx(o);
11952             };
11953
11954             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11955                 this.clearOpacity();
11956                 (function(){
11957                     this.fxanim({
11958                         height:{to:1},
11959                         points:{by:[0, this.getHeight() * .5]}
11960                     }, o, 'motion', 0.3, 'easeIn', after);
11961                 }).defer(100, this);
11962             });
11963         });
11964         return this;
11965     },
11966
11967     /**
11968      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11969      * changed using the "attr" config option) and then fading back to the original color. If no original
11970      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11971      * Usage:
11972 <pre><code>
11973 // default: highlight background to yellow
11974 el.highlight();
11975
11976 // custom: highlight foreground text to blue for 2 seconds
11977 el.highlight("0000ff", { attr: 'color', duration: 2 });
11978
11979 // common config options shown with default values
11980 el.highlight("ffff9c", {
11981     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11982     endColor: (current color) or "ffffff",
11983     easing: 'easeIn',
11984     duration: 1
11985 });
11986 </code></pre>
11987      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11988      * @param {Object} options (optional) Object literal with any of the Fx config options
11989      * @return {Roo.Element} The Element
11990      */ 
11991     highlight : function(color, o){
11992         var el = this.getFxEl();
11993         o = o || {};
11994
11995         el.queueFx(o, function(){
11996             color = color || "ffff9c";
11997             attr = o.attr || "backgroundColor";
11998
11999             this.clearOpacity();
12000             this.show();
12001
12002             var origColor = this.getColor(attr);
12003             var restoreColor = this.dom.style[attr];
12004             endColor = (o.endColor || origColor) || "ffffff";
12005
12006             var after = function(){
12007                 el.dom.style[attr] = restoreColor;
12008                 el.afterFx(o);
12009             };
12010
12011             var a = {};
12012             a[attr] = {from: color, to: endColor};
12013             arguments.callee.anim = this.fxanim(a,
12014                 o,
12015                 'color',
12016                 1,
12017                 'easeIn', after);
12018         });
12019         return this;
12020     },
12021
12022    /**
12023     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
12024     * Usage:
12025 <pre><code>
12026 // default: a single light blue ripple
12027 el.frame();
12028
12029 // custom: 3 red ripples lasting 3 seconds total
12030 el.frame("ff0000", 3, { duration: 3 });
12031
12032 // common config options shown with default values
12033 el.frame("C3DAF9", 1, {
12034     duration: 1 //duration of entire animation (not each individual ripple)
12035     // Note: Easing is not configurable and will be ignored if included
12036 });
12037 </code></pre>
12038     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
12039     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
12040     * @param {Object} options (optional) Object literal with any of the Fx config options
12041     * @return {Roo.Element} The Element
12042     */
12043     frame : function(color, count, o){
12044         var el = this.getFxEl();
12045         o = o || {};
12046
12047         el.queueFx(o, function(){
12048             color = color || "#C3DAF9";
12049             if(color.length == 6){
12050                 color = "#" + color;
12051             }
12052             count = count || 1;
12053             duration = o.duration || 1;
12054             this.show();
12055
12056             var b = this.getBox();
12057             var animFn = function(){
12058                 var proxy = this.createProxy({
12059
12060                      style:{
12061                         visbility:"hidden",
12062                         position:"absolute",
12063                         "z-index":"35000", // yee haw
12064                         border:"0px solid " + color
12065                      }
12066                   });
12067                 var scale = Roo.isBorderBox ? 2 : 1;
12068                 proxy.animate({
12069                     top:{from:b.y, to:b.y - 20},
12070                     left:{from:b.x, to:b.x - 20},
12071                     borderWidth:{from:0, to:10},
12072                     opacity:{from:1, to:0},
12073                     height:{from:b.height, to:(b.height + (20*scale))},
12074                     width:{from:b.width, to:(b.width + (20*scale))}
12075                 }, duration, function(){
12076                     proxy.remove();
12077                 });
12078                 if(--count > 0){
12079                      animFn.defer((duration/2)*1000, this);
12080                 }else{
12081                     el.afterFx(o);
12082                 }
12083             };
12084             animFn.call(this);
12085         });
12086         return this;
12087     },
12088
12089    /**
12090     * Creates a pause before any subsequent queued effects begin.  If there are
12091     * no effects queued after the pause it will have no effect.
12092     * Usage:
12093 <pre><code>
12094 el.pause(1);
12095 </code></pre>
12096     * @param {Number} seconds The length of time to pause (in seconds)
12097     * @return {Roo.Element} The Element
12098     */
12099     pause : function(seconds){
12100         var el = this.getFxEl();
12101         var o = {};
12102
12103         el.queueFx(o, function(){
12104             setTimeout(function(){
12105                 el.afterFx(o);
12106             }, seconds * 1000);
12107         });
12108         return this;
12109     },
12110
12111    /**
12112     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
12113     * using the "endOpacity" config option.
12114     * Usage:
12115 <pre><code>
12116 // default: fade in from opacity 0 to 100%
12117 el.fadeIn();
12118
12119 // custom: fade in from opacity 0 to 75% over 2 seconds
12120 el.fadeIn({ endOpacity: .75, duration: 2});
12121
12122 // common config options shown with default values
12123 el.fadeIn({
12124     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
12125     easing: 'easeOut',
12126     duration: .5
12127 });
12128 </code></pre>
12129     * @param {Object} options (optional) Object literal with any of the Fx config options
12130     * @return {Roo.Element} The Element
12131     */
12132     fadeIn : function(o){
12133         var el = this.getFxEl();
12134         o = o || {};
12135         el.queueFx(o, function(){
12136             this.setOpacity(0);
12137             this.fixDisplay();
12138             this.dom.style.visibility = 'visible';
12139             var to = o.endOpacity || 1;
12140             arguments.callee.anim = this.fxanim({opacity:{to:to}},
12141                 o, null, .5, "easeOut", function(){
12142                 if(to == 1){
12143                     this.clearOpacity();
12144                 }
12145                 el.afterFx(o);
12146             });
12147         });
12148         return this;
12149     },
12150
12151    /**
12152     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
12153     * using the "endOpacity" config option.
12154     * Usage:
12155 <pre><code>
12156 // default: fade out from the element's current opacity to 0
12157 el.fadeOut();
12158
12159 // custom: fade out from the element's current opacity to 25% over 2 seconds
12160 el.fadeOut({ endOpacity: .25, duration: 2});
12161
12162 // common config options shown with default values
12163 el.fadeOut({
12164     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
12165     easing: 'easeOut',
12166     duration: .5
12167     remove: false,
12168     useDisplay: false
12169 });
12170 </code></pre>
12171     * @param {Object} options (optional) Object literal with any of the Fx config options
12172     * @return {Roo.Element} The Element
12173     */
12174     fadeOut : function(o){
12175         var el = this.getFxEl();
12176         o = o || {};
12177         el.queueFx(o, function(){
12178             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
12179                 o, null, .5, "easeOut", function(){
12180                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
12181                      this.dom.style.display = "none";
12182                 }else{
12183                      this.dom.style.visibility = "hidden";
12184                 }
12185                 this.clearOpacity();
12186                 el.afterFx(o);
12187             });
12188         });
12189         return this;
12190     },
12191
12192    /**
12193     * Animates the transition of an element's dimensions from a starting height/width
12194     * to an ending height/width.
12195     * Usage:
12196 <pre><code>
12197 // change height and width to 100x100 pixels
12198 el.scale(100, 100);
12199
12200 // common config options shown with default values.  The height and width will default to
12201 // the element's existing values if passed as null.
12202 el.scale(
12203     [element's width],
12204     [element's height], {
12205     easing: 'easeOut',
12206     duration: .35
12207 });
12208 </code></pre>
12209     * @param {Number} width  The new width (pass undefined to keep the original width)
12210     * @param {Number} height  The new height (pass undefined to keep the original height)
12211     * @param {Object} options (optional) Object literal with any of the Fx config options
12212     * @return {Roo.Element} The Element
12213     */
12214     scale : function(w, h, o){
12215         this.shift(Roo.apply({}, o, {
12216             width: w,
12217             height: h
12218         }));
12219         return this;
12220     },
12221
12222    /**
12223     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12224     * Any of these properties not specified in the config object will not be changed.  This effect 
12225     * requires that at least one new dimension, position or opacity setting must be passed in on
12226     * the config object in order for the function to have any effect.
12227     * Usage:
12228 <pre><code>
12229 // slide the element horizontally to x position 200 while changing the height and opacity
12230 el.shift({ x: 200, height: 50, opacity: .8 });
12231
12232 // common config options shown with default values.
12233 el.shift({
12234     width: [element's width],
12235     height: [element's height],
12236     x: [element's x position],
12237     y: [element's y position],
12238     opacity: [element's opacity],
12239     easing: 'easeOut',
12240     duration: .35
12241 });
12242 </code></pre>
12243     * @param {Object} options  Object literal with any of the Fx config options
12244     * @return {Roo.Element} The Element
12245     */
12246     shift : function(o){
12247         var el = this.getFxEl();
12248         o = o || {};
12249         el.queueFx(o, function(){
12250             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
12251             if(w !== undefined){
12252                 a.width = {to: this.adjustWidth(w)};
12253             }
12254             if(h !== undefined){
12255                 a.height = {to: this.adjustHeight(h)};
12256             }
12257             if(x !== undefined || y !== undefined){
12258                 a.points = {to: [
12259                     x !== undefined ? x : this.getX(),
12260                     y !== undefined ? y : this.getY()
12261                 ]};
12262             }
12263             if(op !== undefined){
12264                 a.opacity = {to: op};
12265             }
12266             if(o.xy !== undefined){
12267                 a.points = {to: o.xy};
12268             }
12269             arguments.callee.anim = this.fxanim(a,
12270                 o, 'motion', .35, "easeOut", function(){
12271                 el.afterFx(o);
12272             });
12273         });
12274         return this;
12275     },
12276
12277         /**
12278          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
12279          * ending point of the effect.
12280          * Usage:
12281          *<pre><code>
12282 // default: slide the element downward while fading out
12283 el.ghost();
12284
12285 // custom: slide the element out to the right with a 2-second duration
12286 el.ghost('r', { duration: 2 });
12287
12288 // common config options shown with default values
12289 el.ghost('b', {
12290     easing: 'easeOut',
12291     duration: .5
12292     remove: false,
12293     useDisplay: false
12294 });
12295 </code></pre>
12296          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12297          * @param {Object} options (optional) Object literal with any of the Fx config options
12298          * @return {Roo.Element} The Element
12299          */
12300     ghost : function(anchor, o){
12301         var el = this.getFxEl();
12302         o = o || {};
12303
12304         el.queueFx(o, function(){
12305             anchor = anchor || "b";
12306
12307             // restore values after effect
12308             var r = this.getFxRestore();
12309             var w = this.getWidth(),
12310                 h = this.getHeight();
12311
12312             var st = this.dom.style;
12313
12314             var after = function(){
12315                 if(o.useDisplay){
12316                     el.setDisplayed(false);
12317                 }else{
12318                     el.hide();
12319                 }
12320
12321                 el.clearOpacity();
12322                 el.setPositioning(r.pos);
12323                 st.width = r.width;
12324                 st.height = r.height;
12325
12326                 el.afterFx(o);
12327             };
12328
12329             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12330             switch(anchor.toLowerCase()){
12331                 case "t":
12332                     pt.by = [0, -h];
12333                 break;
12334                 case "l":
12335                     pt.by = [-w, 0];
12336                 break;
12337                 case "r":
12338                     pt.by = [w, 0];
12339                 break;
12340                 case "b":
12341                     pt.by = [0, h];
12342                 break;
12343                 case "tl":
12344                     pt.by = [-w, -h];
12345                 break;
12346                 case "bl":
12347                     pt.by = [-w, h];
12348                 break;
12349                 case "br":
12350                     pt.by = [w, h];
12351                 break;
12352                 case "tr":
12353                     pt.by = [w, -h];
12354                 break;
12355             }
12356
12357             arguments.callee.anim = this.fxanim(a,
12358                 o,
12359                 'motion',
12360                 .5,
12361                 "easeOut", after);
12362         });
12363         return this;
12364     },
12365
12366         /**
12367          * Ensures that all effects queued after syncFx is called on the element are
12368          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12369          * @return {Roo.Element} The Element
12370          */
12371     syncFx : function(){
12372         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12373             block : false,
12374             concurrent : true,
12375             stopFx : false
12376         });
12377         return this;
12378     },
12379
12380         /**
12381          * Ensures that all effects queued after sequenceFx is called on the element are
12382          * run in sequence.  This is the opposite of {@link #syncFx}.
12383          * @return {Roo.Element} The Element
12384          */
12385     sequenceFx : function(){
12386         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12387             block : false,
12388             concurrent : false,
12389             stopFx : false
12390         });
12391         return this;
12392     },
12393
12394         /* @private */
12395     nextFx : function(){
12396         var ef = this.fxQueue[0];
12397         if(ef){
12398             ef.call(this);
12399         }
12400     },
12401
12402         /**
12403          * Returns true if the element has any effects actively running or queued, else returns false.
12404          * @return {Boolean} True if element has active effects, else false
12405          */
12406     hasActiveFx : function(){
12407         return this.fxQueue && this.fxQueue[0];
12408     },
12409
12410         /**
12411          * Stops any running effects and clears the element's internal effects queue if it contains
12412          * any additional effects that haven't started yet.
12413          * @return {Roo.Element} The Element
12414          */
12415     stopFx : function(){
12416         if(this.hasActiveFx()){
12417             var cur = this.fxQueue[0];
12418             if(cur && cur.anim && cur.anim.isAnimated()){
12419                 this.fxQueue = [cur]; // clear out others
12420                 cur.anim.stop(true);
12421             }
12422         }
12423         return this;
12424     },
12425
12426         /* @private */
12427     beforeFx : function(o){
12428         if(this.hasActiveFx() && !o.concurrent){
12429            if(o.stopFx){
12430                this.stopFx();
12431                return true;
12432            }
12433            return false;
12434         }
12435         return true;
12436     },
12437
12438         /**
12439          * Returns true if the element is currently blocking so that no other effect can be queued
12440          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12441          * used to ensure that an effect initiated by a user action runs to completion prior to the
12442          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12443          * @return {Boolean} True if blocking, else false
12444          */
12445     hasFxBlock : function(){
12446         var q = this.fxQueue;
12447         return q && q[0] && q[0].block;
12448     },
12449
12450         /* @private */
12451     queueFx : function(o, fn){
12452         if(!this.fxQueue){
12453             this.fxQueue = [];
12454         }
12455         if(!this.hasFxBlock()){
12456             Roo.applyIf(o, this.fxDefaults);
12457             if(!o.concurrent){
12458                 var run = this.beforeFx(o);
12459                 fn.block = o.block;
12460                 this.fxQueue.push(fn);
12461                 if(run){
12462                     this.nextFx();
12463                 }
12464             }else{
12465                 fn.call(this);
12466             }
12467         }
12468         return this;
12469     },
12470
12471         /* @private */
12472     fxWrap : function(pos, o, vis){
12473         var wrap;
12474         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12475             var wrapXY;
12476             if(o.fixPosition){
12477                 wrapXY = this.getXY();
12478             }
12479             var div = document.createElement("div");
12480             div.style.visibility = vis;
12481             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12482             wrap.setPositioning(pos);
12483             if(wrap.getStyle("position") == "static"){
12484                 wrap.position("relative");
12485             }
12486             this.clearPositioning('auto');
12487             wrap.clip();
12488             wrap.dom.appendChild(this.dom);
12489             if(wrapXY){
12490                 wrap.setXY(wrapXY);
12491             }
12492         }
12493         return wrap;
12494     },
12495
12496         /* @private */
12497     fxUnwrap : function(wrap, pos, o){
12498         this.clearPositioning();
12499         this.setPositioning(pos);
12500         if(!o.wrap){
12501             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12502             wrap.remove();
12503         }
12504     },
12505
12506         /* @private */
12507     getFxRestore : function(){
12508         var st = this.dom.style;
12509         return {pos: this.getPositioning(), width: st.width, height : st.height};
12510     },
12511
12512         /* @private */
12513     afterFx : function(o){
12514         if(o.afterStyle){
12515             this.applyStyles(o.afterStyle);
12516         }
12517         if(o.afterCls){
12518             this.addClass(o.afterCls);
12519         }
12520         if(o.remove === true){
12521             this.remove();
12522         }
12523         Roo.callback(o.callback, o.scope, [this]);
12524         if(!o.concurrent){
12525             this.fxQueue.shift();
12526             this.nextFx();
12527         }
12528     },
12529
12530         /* @private */
12531     getFxEl : function(){ // support for composite element fx
12532         return Roo.get(this.dom);
12533     },
12534
12535         /* @private */
12536     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12537         animType = animType || 'run';
12538         opt = opt || {};
12539         var anim = Roo.lib.Anim[animType](
12540             this.dom, args,
12541             (opt.duration || defaultDur) || .35,
12542             (opt.easing || defaultEase) || 'easeOut',
12543             function(){
12544                 Roo.callback(cb, this);
12545             },
12546             this
12547         );
12548         opt.anim = anim;
12549         return anim;
12550     }
12551 };
12552
12553 // backwords compat
12554 Roo.Fx.resize = Roo.Fx.scale;
12555
12556 //When included, Roo.Fx is automatically applied to Element so that all basic
12557 //effects are available directly via the Element API
12558 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12559  * Based on:
12560  * Ext JS Library 1.1.1
12561  * Copyright(c) 2006-2007, Ext JS, LLC.
12562  *
12563  * Originally Released Under LGPL - original licence link has changed is not relivant.
12564  *
12565  * Fork - LGPL
12566  * <script type="text/javascript">
12567  */
12568
12569
12570 /**
12571  * @class Roo.CompositeElement
12572  * Standard composite class. Creates a Roo.Element for every element in the collection.
12573  * <br><br>
12574  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12575  * actions will be performed on all the elements in this collection.</b>
12576  * <br><br>
12577  * All methods return <i>this</i> and can be chained.
12578  <pre><code>
12579  var els = Roo.select("#some-el div.some-class", true);
12580  // or select directly from an existing element
12581  var el = Roo.get('some-el');
12582  el.select('div.some-class', true);
12583
12584  els.setWidth(100); // all elements become 100 width
12585  els.hide(true); // all elements fade out and hide
12586  // or
12587  els.setWidth(100).hide(true);
12588  </code></pre>
12589  */
12590 Roo.CompositeElement = function(els){
12591     this.elements = [];
12592     this.addElements(els);
12593 };
12594 Roo.CompositeElement.prototype = {
12595     isComposite: true,
12596     addElements : function(els){
12597         if(!els) {
12598             return this;
12599         }
12600         if(typeof els == "string"){
12601             els = Roo.Element.selectorFunction(els);
12602         }
12603         var yels = this.elements;
12604         var index = yels.length-1;
12605         for(var i = 0, len = els.length; i < len; i++) {
12606                 yels[++index] = Roo.get(els[i]);
12607         }
12608         return this;
12609     },
12610
12611     /**
12612     * Clears this composite and adds the elements returned by the passed selector.
12613     * @param {String/Array} els A string CSS selector, an array of elements or an element
12614     * @return {CompositeElement} this
12615     */
12616     fill : function(els){
12617         this.elements = [];
12618         this.add(els);
12619         return this;
12620     },
12621
12622     /**
12623     * Filters this composite to only elements that match the passed selector.
12624     * @param {String} selector A string CSS selector
12625     * @param {Boolean} inverse return inverse filter (not matches)
12626     * @return {CompositeElement} this
12627     */
12628     filter : function(selector, inverse){
12629         var els = [];
12630         inverse = inverse || false;
12631         this.each(function(el){
12632             var match = inverse ? !el.is(selector) : el.is(selector);
12633             if(match){
12634                 els[els.length] = el.dom;
12635             }
12636         });
12637         this.fill(els);
12638         return this;
12639     },
12640
12641     invoke : function(fn, args){
12642         var els = this.elements;
12643         for(var i = 0, len = els.length; i < len; i++) {
12644                 Roo.Element.prototype[fn].apply(els[i], args);
12645         }
12646         return this;
12647     },
12648     /**
12649     * Adds elements to this composite.
12650     * @param {String/Array} els A string CSS selector, an array of elements or an element
12651     * @return {CompositeElement} this
12652     */
12653     add : function(els){
12654         if(typeof els == "string"){
12655             this.addElements(Roo.Element.selectorFunction(els));
12656         }else if(els.length !== undefined){
12657             this.addElements(els);
12658         }else{
12659             this.addElements([els]);
12660         }
12661         return this;
12662     },
12663     /**
12664     * Calls the passed function passing (el, this, index) for each element in this composite.
12665     * @param {Function} fn The function to call
12666     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12667     * @return {CompositeElement} this
12668     */
12669     each : function(fn, scope){
12670         var els = this.elements;
12671         for(var i = 0, len = els.length; i < len; i++){
12672             if(fn.call(scope || els[i], els[i], this, i) === false) {
12673                 break;
12674             }
12675         }
12676         return this;
12677     },
12678
12679     /**
12680      * Returns the Element object at the specified index
12681      * @param {Number} index
12682      * @return {Roo.Element}
12683      */
12684     item : function(index){
12685         return this.elements[index] || null;
12686     },
12687
12688     /**
12689      * Returns the first Element
12690      * @return {Roo.Element}
12691      */
12692     first : function(){
12693         return this.item(0);
12694     },
12695
12696     /**
12697      * Returns the last Element
12698      * @return {Roo.Element}
12699      */
12700     last : function(){
12701         return this.item(this.elements.length-1);
12702     },
12703
12704     /**
12705      * Returns the number of elements in this composite
12706      * @return Number
12707      */
12708     getCount : function(){
12709         return this.elements.length;
12710     },
12711
12712     /**
12713      * Returns true if this composite contains the passed element
12714      * @return Boolean
12715      */
12716     contains : function(el){
12717         return this.indexOf(el) !== -1;
12718     },
12719
12720     /**
12721      * Returns true if this composite contains the passed element
12722      * @return Boolean
12723      */
12724     indexOf : function(el){
12725         return this.elements.indexOf(Roo.get(el));
12726     },
12727
12728
12729     /**
12730     * Removes the specified element(s).
12731     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12732     * or an array of any of those.
12733     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12734     * @return {CompositeElement} this
12735     */
12736     removeElement : function(el, removeDom){
12737         if(el instanceof Array){
12738             for(var i = 0, len = el.length; i < len; i++){
12739                 this.removeElement(el[i]);
12740             }
12741             return this;
12742         }
12743         var index = typeof el == 'number' ? el : this.indexOf(el);
12744         if(index !== -1){
12745             if(removeDom){
12746                 var d = this.elements[index];
12747                 if(d.dom){
12748                     d.remove();
12749                 }else{
12750                     d.parentNode.removeChild(d);
12751                 }
12752             }
12753             this.elements.splice(index, 1);
12754         }
12755         return this;
12756     },
12757
12758     /**
12759     * Replaces the specified element with the passed element.
12760     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12761     * to replace.
12762     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12763     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12764     * @return {CompositeElement} this
12765     */
12766     replaceElement : function(el, replacement, domReplace){
12767         var index = typeof el == 'number' ? el : this.indexOf(el);
12768         if(index !== -1){
12769             if(domReplace){
12770                 this.elements[index].replaceWith(replacement);
12771             }else{
12772                 this.elements.splice(index, 1, Roo.get(replacement))
12773             }
12774         }
12775         return this;
12776     },
12777
12778     /**
12779      * Removes all elements.
12780      */
12781     clear : function(){
12782         this.elements = [];
12783     }
12784 };
12785 (function(){
12786     Roo.CompositeElement.createCall = function(proto, fnName){
12787         if(!proto[fnName]){
12788             proto[fnName] = function(){
12789                 return this.invoke(fnName, arguments);
12790             };
12791         }
12792     };
12793     for(var fnName in Roo.Element.prototype){
12794         if(typeof Roo.Element.prototype[fnName] == "function"){
12795             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12796         }
12797     };
12798 })();
12799 /*
12800  * Based on:
12801  * Ext JS Library 1.1.1
12802  * Copyright(c) 2006-2007, Ext JS, LLC.
12803  *
12804  * Originally Released Under LGPL - original licence link has changed is not relivant.
12805  *
12806  * Fork - LGPL
12807  * <script type="text/javascript">
12808  */
12809
12810 /**
12811  * @class Roo.CompositeElementLite
12812  * @extends Roo.CompositeElement
12813  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12814  <pre><code>
12815  var els = Roo.select("#some-el div.some-class");
12816  // or select directly from an existing element
12817  var el = Roo.get('some-el');
12818  el.select('div.some-class');
12819
12820  els.setWidth(100); // all elements become 100 width
12821  els.hide(true); // all elements fade out and hide
12822  // or
12823  els.setWidth(100).hide(true);
12824  </code></pre><br><br>
12825  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12826  * actions will be performed on all the elements in this collection.</b>
12827  */
12828 Roo.CompositeElementLite = function(els){
12829     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12830     this.el = new Roo.Element.Flyweight();
12831 };
12832 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12833     addElements : function(els){
12834         if(els){
12835             if(els instanceof Array){
12836                 this.elements = this.elements.concat(els);
12837             }else{
12838                 var yels = this.elements;
12839                 var index = yels.length-1;
12840                 for(var i = 0, len = els.length; i < len; i++) {
12841                     yels[++index] = els[i];
12842                 }
12843             }
12844         }
12845         return this;
12846     },
12847     invoke : function(fn, args){
12848         var els = this.elements;
12849         var el = this.el;
12850         for(var i = 0, len = els.length; i < len; i++) {
12851             el.dom = els[i];
12852                 Roo.Element.prototype[fn].apply(el, args);
12853         }
12854         return this;
12855     },
12856     /**
12857      * Returns a flyweight Element of the dom element object at the specified index
12858      * @param {Number} index
12859      * @return {Roo.Element}
12860      */
12861     item : function(index){
12862         if(!this.elements[index]){
12863             return null;
12864         }
12865         this.el.dom = this.elements[index];
12866         return this.el;
12867     },
12868
12869     // fixes scope with flyweight
12870     addListener : function(eventName, handler, scope, opt){
12871         var els = this.elements;
12872         for(var i = 0, len = els.length; i < len; i++) {
12873             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12874         }
12875         return this;
12876     },
12877
12878     /**
12879     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12880     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12881     * a reference to the dom node, use el.dom.</b>
12882     * @param {Function} fn The function to call
12883     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12884     * @return {CompositeElement} this
12885     */
12886     each : function(fn, scope){
12887         var els = this.elements;
12888         var el = this.el;
12889         for(var i = 0, len = els.length; i < len; i++){
12890             el.dom = els[i];
12891                 if(fn.call(scope || el, el, this, i) === false){
12892                 break;
12893             }
12894         }
12895         return this;
12896     },
12897
12898     indexOf : function(el){
12899         return this.elements.indexOf(Roo.getDom(el));
12900     },
12901
12902     replaceElement : function(el, replacement, domReplace){
12903         var index = typeof el == 'number' ? el : this.indexOf(el);
12904         if(index !== -1){
12905             replacement = Roo.getDom(replacement);
12906             if(domReplace){
12907                 var d = this.elements[index];
12908                 d.parentNode.insertBefore(replacement, d);
12909                 d.parentNode.removeChild(d);
12910             }
12911             this.elements.splice(index, 1, replacement);
12912         }
12913         return this;
12914     }
12915 });
12916 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12917
12918 /*
12919  * Based on:
12920  * Ext JS Library 1.1.1
12921  * Copyright(c) 2006-2007, Ext JS, LLC.
12922  *
12923  * Originally Released Under LGPL - original licence link has changed is not relivant.
12924  *
12925  * Fork - LGPL
12926  * <script type="text/javascript">
12927  */
12928
12929  
12930
12931 /**
12932  * @class Roo.data.Connection
12933  * @extends Roo.util.Observable
12934  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12935  * either to a configured URL, or to a URL specified at request time. 
12936  * 
12937  * Requests made by this class are asynchronous, and will return immediately. No data from
12938  * the server will be available to the statement immediately following the {@link #request} call.
12939  * To process returned data, use a callback in the request options object, or an event listener.
12940  * 
12941  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12942  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12943  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12944  * property and, if present, the IFRAME's XML document as the responseXML property.
12945  * 
12946  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12947  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12948  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12949  * standard DOM methods.
12950  * @constructor
12951  * @param {Object} config a configuration object.
12952  */
12953 Roo.data.Connection = function(config){
12954     Roo.apply(this, config);
12955     this.addEvents({
12956         /**
12957          * @event beforerequest
12958          * Fires before a network request is made to retrieve a data object.
12959          * @param {Connection} conn This Connection object.
12960          * @param {Object} options The options config object passed to the {@link #request} method.
12961          */
12962         "beforerequest" : true,
12963         /**
12964          * @event requestcomplete
12965          * Fires if the request was successfully completed.
12966          * @param {Connection} conn This Connection object.
12967          * @param {Object} response The XHR object containing the response data.
12968          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12969          * @param {Object} options The options config object passed to the {@link #request} method.
12970          */
12971         "requestcomplete" : true,
12972         /**
12973          * @event requestexception
12974          * Fires if an error HTTP status was returned from the server.
12975          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12976          * @param {Connection} conn This Connection object.
12977          * @param {Object} response The XHR object containing the response data.
12978          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12979          * @param {Object} options The options config object passed to the {@link #request} method.
12980          */
12981         "requestexception" : true
12982     });
12983     Roo.data.Connection.superclass.constructor.call(this);
12984 };
12985
12986 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12987     /**
12988      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12989      */
12990     /**
12991      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12992      * extra parameters to each request made by this object. (defaults to undefined)
12993      */
12994     /**
12995      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12996      *  to each request made by this object. (defaults to undefined)
12997      */
12998     /**
12999      * @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)
13000      */
13001     /**
13002      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13003      */
13004     timeout : 30000,
13005     /**
13006      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
13007      * @type Boolean
13008      */
13009     autoAbort:false,
13010
13011     /**
13012      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
13013      * @type Boolean
13014      */
13015     disableCaching: true,
13016
13017     /**
13018      * Sends an HTTP request to a remote server.
13019      * @param {Object} options An object which may contain the following properties:<ul>
13020      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
13021      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
13022      * request, a url encoded string or a function to call to get either.</li>
13023      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
13024      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
13025      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
13026      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
13027      * <li>options {Object} The parameter to the request call.</li>
13028      * <li>success {Boolean} True if the request succeeded.</li>
13029      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13030      * </ul></li>
13031      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
13032      * The callback is passed the following parameters:<ul>
13033      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13034      * <li>options {Object} The parameter to the request call.</li>
13035      * </ul></li>
13036      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
13037      * The callback is passed the following parameters:<ul>
13038      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
13039      * <li>options {Object} The parameter to the request call.</li>
13040      * </ul></li>
13041      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
13042      * for the callback function. Defaults to the browser window.</li>
13043      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
13044      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
13045      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
13046      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
13047      * params for the post data. Any params will be appended to the URL.</li>
13048      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
13049      * </ul>
13050      * @return {Number} transactionId
13051      */
13052     request : function(o){
13053         if(this.fireEvent("beforerequest", this, o) !== false){
13054             var p = o.params;
13055
13056             if(typeof p == "function"){
13057                 p = p.call(o.scope||window, o);
13058             }
13059             if(typeof p == "object"){
13060                 p = Roo.urlEncode(o.params);
13061             }
13062             if(this.extraParams){
13063                 var extras = Roo.urlEncode(this.extraParams);
13064                 p = p ? (p + '&' + extras) : extras;
13065             }
13066
13067             var url = o.url || this.url;
13068             if(typeof url == 'function'){
13069                 url = url.call(o.scope||window, o);
13070             }
13071
13072             if(o.form){
13073                 var form = Roo.getDom(o.form);
13074                 url = url || form.action;
13075
13076                 var enctype = form.getAttribute("enctype");
13077                 
13078                 if (o.formData) {
13079                     return this.doFormDataUpload(o, url);
13080                 }
13081                 
13082                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
13083                     return this.doFormUpload(o, p, url);
13084                 }
13085                 var f = Roo.lib.Ajax.serializeForm(form);
13086                 p = p ? (p + '&' + f) : f;
13087             }
13088             
13089             if (!o.form && o.formData) {
13090                 o.formData = o.formData === true ? new FormData() : o.formData;
13091                 for (var k in o.params) {
13092                     o.formData.append(k,o.params[k]);
13093                 }
13094                     
13095                 return this.doFormDataUpload(o, url);
13096             }
13097             
13098
13099             var hs = o.headers;
13100             if(this.defaultHeaders){
13101                 hs = Roo.apply(hs || {}, this.defaultHeaders);
13102                 if(!o.headers){
13103                     o.headers = hs;
13104                 }
13105             }
13106
13107             var cb = {
13108                 success: this.handleResponse,
13109                 failure: this.handleFailure,
13110                 scope: this,
13111                 argument: {options: o},
13112                 timeout : o.timeout || this.timeout
13113             };
13114
13115             var method = o.method||this.method||(p ? "POST" : "GET");
13116
13117             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
13118                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
13119             }
13120
13121             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13122                 if(o.autoAbort){
13123                     this.abort();
13124                 }
13125             }else if(this.autoAbort !== false){
13126                 this.abort();
13127             }
13128
13129             if((method == 'GET' && p) || o.xmlData){
13130                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
13131                 p = '';
13132             }
13133             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
13134             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
13135             Roo.lib.Ajax.useDefaultHeader == true;
13136             return this.transId;
13137         }else{
13138             Roo.callback(o.callback, o.scope, [o, null, null]);
13139             return null;
13140         }
13141     },
13142
13143     /**
13144      * Determine whether this object has a request outstanding.
13145      * @param {Number} transactionId (Optional) defaults to the last transaction
13146      * @return {Boolean} True if there is an outstanding request.
13147      */
13148     isLoading : function(transId){
13149         if(transId){
13150             return Roo.lib.Ajax.isCallInProgress(transId);
13151         }else{
13152             return this.transId ? true : false;
13153         }
13154     },
13155
13156     /**
13157      * Aborts any outstanding request.
13158      * @param {Number} transactionId (Optional) defaults to the last transaction
13159      */
13160     abort : function(transId){
13161         if(transId || this.isLoading()){
13162             Roo.lib.Ajax.abort(transId || this.transId);
13163         }
13164     },
13165
13166     // private
13167     handleResponse : function(response){
13168         this.transId = false;
13169         var options = response.argument.options;
13170         response.argument = options ? options.argument : null;
13171         this.fireEvent("requestcomplete", this, response, options);
13172         Roo.callback(options.success, options.scope, [response, options]);
13173         Roo.callback(options.callback, options.scope, [options, true, response]);
13174     },
13175
13176     // private
13177     handleFailure : function(response, e){
13178         this.transId = false;
13179         var options = response.argument.options;
13180         response.argument = options ? options.argument : null;
13181         this.fireEvent("requestexception", this, response, options, e);
13182         Roo.callback(options.failure, options.scope, [response, options]);
13183         Roo.callback(options.callback, options.scope, [options, false, response]);
13184     },
13185
13186     // private
13187     doFormUpload : function(o, ps, url){
13188         var id = Roo.id();
13189         var frame = document.createElement('iframe');
13190         frame.id = id;
13191         frame.name = id;
13192         frame.className = 'x-hidden';
13193         if(Roo.isIE){
13194             frame.src = Roo.SSL_SECURE_URL;
13195         }
13196         document.body.appendChild(frame);
13197
13198         if(Roo.isIE){
13199            document.frames[id].name = id;
13200         }
13201
13202         var form = Roo.getDom(o.form);
13203         form.target = id;
13204         form.method = 'POST';
13205         form.enctype = form.encoding = 'multipart/form-data';
13206         if(url){
13207             form.action = url;
13208         }
13209
13210         var hiddens, hd;
13211         if(ps){ // add dynamic params
13212             hiddens = [];
13213             ps = Roo.urlDecode(ps, false);
13214             for(var k in ps){
13215                 if(ps.hasOwnProperty(k)){
13216                     hd = document.createElement('input');
13217                     hd.type = 'hidden';
13218                     hd.name = k;
13219                     hd.value = ps[k];
13220                     form.appendChild(hd);
13221                     hiddens.push(hd);
13222                 }
13223             }
13224         }
13225
13226         function cb(){
13227             var r = {  // bogus response object
13228                 responseText : '',
13229                 responseXML : null
13230             };
13231
13232             r.argument = o ? o.argument : null;
13233
13234             try { //
13235                 var doc;
13236                 if(Roo.isIE){
13237                     doc = frame.contentWindow.document;
13238                 }else {
13239                     doc = (frame.contentDocument || window.frames[id].document);
13240                 }
13241                 if(doc && doc.body){
13242                     r.responseText = doc.body.innerHTML;
13243                 }
13244                 if(doc && doc.XMLDocument){
13245                     r.responseXML = doc.XMLDocument;
13246                 }else {
13247                     r.responseXML = doc;
13248                 }
13249             }
13250             catch(e) {
13251                 // ignore
13252             }
13253
13254             Roo.EventManager.removeListener(frame, 'load', cb, this);
13255
13256             this.fireEvent("requestcomplete", this, r, o);
13257             Roo.callback(o.success, o.scope, [r, o]);
13258             Roo.callback(o.callback, o.scope, [o, true, r]);
13259
13260             setTimeout(function(){document.body.removeChild(frame);}, 100);
13261         }
13262
13263         Roo.EventManager.on(frame, 'load', cb, this);
13264         form.submit();
13265
13266         if(hiddens){ // remove dynamic params
13267             for(var i = 0, len = hiddens.length; i < len; i++){
13268                 form.removeChild(hiddens[i]);
13269             }
13270         }
13271     },
13272     // this is a 'formdata version???'
13273     
13274     
13275     doFormDataUpload : function(o,  url)
13276     {
13277         var formData;
13278         if (o.form) {
13279             var form =  Roo.getDom(o.form);
13280             form.enctype = form.encoding = 'multipart/form-data';
13281             formData = o.formData === true ? new FormData(form) : o.formData;
13282         } else {
13283             formData = o.formData === true ? new FormData() : o.formData;
13284         }
13285         
13286       
13287         var cb = {
13288             success: this.handleResponse,
13289             failure: this.handleFailure,
13290             scope: this,
13291             argument: {options: o},
13292             timeout : o.timeout || this.timeout
13293         };
13294  
13295         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13296             if(o.autoAbort){
13297                 this.abort();
13298             }
13299         }else if(this.autoAbort !== false){
13300             this.abort();
13301         }
13302
13303         //Roo.lib.Ajax.defaultPostHeader = null;
13304         Roo.lib.Ajax.useDefaultHeader = false;
13305         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
13306         Roo.lib.Ajax.useDefaultHeader = true;
13307  
13308          
13309     }
13310     
13311 });
13312 /*
13313  * Based on:
13314  * Ext JS Library 1.1.1
13315  * Copyright(c) 2006-2007, Ext JS, LLC.
13316  *
13317  * Originally Released Under LGPL - original licence link has changed is not relivant.
13318  *
13319  * Fork - LGPL
13320  * <script type="text/javascript">
13321  */
13322  
13323 /**
13324  * Global Ajax request class.
13325  * 
13326  * @class Roo.Ajax
13327  * @extends Roo.data.Connection
13328  * @static
13329  * 
13330  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13331  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13332  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13333  * @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)
13334  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13335  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13336  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13337  */
13338 Roo.Ajax = new Roo.data.Connection({
13339     // fix up the docs
13340     /**
13341      * @scope Roo.Ajax
13342      * @type {Boolear} 
13343      */
13344     autoAbort : false,
13345
13346     /**
13347      * Serialize the passed form into a url encoded string
13348      * @scope Roo.Ajax
13349      * @param {String/HTMLElement} form
13350      * @return {String}
13351      */
13352     serializeForm : function(form){
13353         return Roo.lib.Ajax.serializeForm(form);
13354     }
13355 });/*
13356  * Based on:
13357  * Ext JS Library 1.1.1
13358  * Copyright(c) 2006-2007, Ext JS, LLC.
13359  *
13360  * Originally Released Under LGPL - original licence link has changed is not relivant.
13361  *
13362  * Fork - LGPL
13363  * <script type="text/javascript">
13364  */
13365
13366  
13367 /**
13368  * @class Roo.UpdateManager
13369  * @extends Roo.util.Observable
13370  * Provides AJAX-style update for Element object.<br><br>
13371  * Usage:<br>
13372  * <pre><code>
13373  * // Get it from a Roo.Element object
13374  * var el = Roo.get("foo");
13375  * var mgr = el.getUpdateManager();
13376  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13377  * ...
13378  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13379  * <br>
13380  * // or directly (returns the same UpdateManager instance)
13381  * var mgr = new Roo.UpdateManager("myElementId");
13382  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13383  * mgr.on("update", myFcnNeedsToKnow);
13384  * <br>
13385    // short handed call directly from the element object
13386    Roo.get("foo").load({
13387         url: "bar.php",
13388         scripts:true,
13389         params: "for=bar",
13390         text: "Loading Foo..."
13391    });
13392  * </code></pre>
13393  * @constructor
13394  * Create new UpdateManager directly.
13395  * @param {String/HTMLElement/Roo.Element} el The element to update
13396  * @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).
13397  */
13398 Roo.UpdateManager = function(el, forceNew){
13399     el = Roo.get(el);
13400     if(!forceNew && el.updateManager){
13401         return el.updateManager;
13402     }
13403     /**
13404      * The Element object
13405      * @type Roo.Element
13406      */
13407     this.el = el;
13408     /**
13409      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13410      * @type String
13411      */
13412     this.defaultUrl = null;
13413
13414     this.addEvents({
13415         /**
13416          * @event beforeupdate
13417          * Fired before an update is made, return false from your handler and the update is cancelled.
13418          * @param {Roo.Element} el
13419          * @param {String/Object/Function} url
13420          * @param {String/Object} params
13421          */
13422         "beforeupdate": true,
13423         /**
13424          * @event update
13425          * Fired after successful update is made.
13426          * @param {Roo.Element} el
13427          * @param {Object} oResponseObject The response Object
13428          */
13429         "update": true,
13430         /**
13431          * @event failure
13432          * Fired on update failure.
13433          * @param {Roo.Element} el
13434          * @param {Object} oResponseObject The response Object
13435          */
13436         "failure": true
13437     });
13438     var d = Roo.UpdateManager.defaults;
13439     /**
13440      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13441      * @type String
13442      */
13443     this.sslBlankUrl = d.sslBlankUrl;
13444     /**
13445      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13446      * @type Boolean
13447      */
13448     this.disableCaching = d.disableCaching;
13449     /**
13450      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13451      * @type String
13452      */
13453     this.indicatorText = d.indicatorText;
13454     /**
13455      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13456      * @type String
13457      */
13458     this.showLoadIndicator = d.showLoadIndicator;
13459     /**
13460      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13461      * @type Number
13462      */
13463     this.timeout = d.timeout;
13464
13465     /**
13466      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13467      * @type Boolean
13468      */
13469     this.loadScripts = d.loadScripts;
13470
13471     /**
13472      * Transaction object of current executing transaction
13473      */
13474     this.transaction = null;
13475
13476     /**
13477      * @private
13478      */
13479     this.autoRefreshProcId = null;
13480     /**
13481      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13482      * @type Function
13483      */
13484     this.refreshDelegate = this.refresh.createDelegate(this);
13485     /**
13486      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13487      * @type Function
13488      */
13489     this.updateDelegate = this.update.createDelegate(this);
13490     /**
13491      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13492      * @type Function
13493      */
13494     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13495     /**
13496      * @private
13497      */
13498     this.successDelegate = this.processSuccess.createDelegate(this);
13499     /**
13500      * @private
13501      */
13502     this.failureDelegate = this.processFailure.createDelegate(this);
13503
13504     if(!this.renderer){
13505      /**
13506       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13507       */
13508     this.renderer = new Roo.UpdateManager.BasicRenderer();
13509     }
13510     
13511     Roo.UpdateManager.superclass.constructor.call(this);
13512 };
13513
13514 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13515     /**
13516      * Get the Element this UpdateManager is bound to
13517      * @return {Roo.Element} The element
13518      */
13519     getEl : function(){
13520         return this.el;
13521     },
13522     /**
13523      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13524      * @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:
13525 <pre><code>
13526 um.update({<br/>
13527     url: "your-url.php",<br/>
13528     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13529     callback: yourFunction,<br/>
13530     scope: yourObject, //(optional scope)  <br/>
13531     discardUrl: false, <br/>
13532     nocache: false,<br/>
13533     text: "Loading...",<br/>
13534     timeout: 30,<br/>
13535     scripts: false<br/>
13536 });
13537 </code></pre>
13538      * The only required property is url. The optional properties nocache, text and scripts
13539      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13540      * @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}
13541      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13542      * @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.
13543      */
13544     update : function(url, params, callback, discardUrl){
13545         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13546             var method = this.method,
13547                 cfg;
13548             if(typeof url == "object"){ // must be config object
13549                 cfg = url;
13550                 url = cfg.url;
13551                 params = params || cfg.params;
13552                 callback = callback || cfg.callback;
13553                 discardUrl = discardUrl || cfg.discardUrl;
13554                 if(callback && cfg.scope){
13555                     callback = callback.createDelegate(cfg.scope);
13556                 }
13557                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13558                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13559                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13560                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13561                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13562             }
13563             this.showLoading();
13564             if(!discardUrl){
13565                 this.defaultUrl = url;
13566             }
13567             if(typeof url == "function"){
13568                 url = url.call(this);
13569             }
13570
13571             method = method || (params ? "POST" : "GET");
13572             if(method == "GET"){
13573                 url = this.prepareUrl(url);
13574             }
13575
13576             var o = Roo.apply(cfg ||{}, {
13577                 url : url,
13578                 params: params,
13579                 success: this.successDelegate,
13580                 failure: this.failureDelegate,
13581                 callback: undefined,
13582                 timeout: (this.timeout*1000),
13583                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13584             });
13585             Roo.log("updated manager called with timeout of " + o.timeout);
13586             this.transaction = Roo.Ajax.request(o);
13587         }
13588     },
13589
13590     /**
13591      * 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.
13592      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13593      * @param {String/HTMLElement} form The form Id or form element
13594      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13595      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13596      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13597      */
13598     formUpdate : function(form, url, reset, callback){
13599         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13600             if(typeof url == "function"){
13601                 url = url.call(this);
13602             }
13603             form = Roo.getDom(form);
13604             this.transaction = Roo.Ajax.request({
13605                 form: form,
13606                 url:url,
13607                 success: this.successDelegate,
13608                 failure: this.failureDelegate,
13609                 timeout: (this.timeout*1000),
13610                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13611             });
13612             this.showLoading.defer(1, this);
13613         }
13614     },
13615
13616     /**
13617      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13618      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13619      */
13620     refresh : function(callback){
13621         if(this.defaultUrl == null){
13622             return;
13623         }
13624         this.update(this.defaultUrl, null, callback, true);
13625     },
13626
13627     /**
13628      * Set this element to auto refresh.
13629      * @param {Number} interval How often to update (in seconds).
13630      * @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)
13631      * @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}
13632      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13633      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13634      */
13635     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13636         if(refreshNow){
13637             this.update(url || this.defaultUrl, params, callback, true);
13638         }
13639         if(this.autoRefreshProcId){
13640             clearInterval(this.autoRefreshProcId);
13641         }
13642         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13643     },
13644
13645     /**
13646      * Stop auto refresh on this element.
13647      */
13648      stopAutoRefresh : function(){
13649         if(this.autoRefreshProcId){
13650             clearInterval(this.autoRefreshProcId);
13651             delete this.autoRefreshProcId;
13652         }
13653     },
13654
13655     isAutoRefreshing : function(){
13656        return this.autoRefreshProcId ? true : false;
13657     },
13658     /**
13659      * Called to update the element to "Loading" state. Override to perform custom action.
13660      */
13661     showLoading : function(){
13662         if(this.showLoadIndicator){
13663             this.el.update(this.indicatorText);
13664         }
13665     },
13666
13667     /**
13668      * Adds unique parameter to query string if disableCaching = true
13669      * @private
13670      */
13671     prepareUrl : function(url){
13672         if(this.disableCaching){
13673             var append = "_dc=" + (new Date().getTime());
13674             if(url.indexOf("?") !== -1){
13675                 url += "&" + append;
13676             }else{
13677                 url += "?" + append;
13678             }
13679         }
13680         return url;
13681     },
13682
13683     /**
13684      * @private
13685      */
13686     processSuccess : function(response){
13687         this.transaction = null;
13688         if(response.argument.form && response.argument.reset){
13689             try{ // put in try/catch since some older FF releases had problems with this
13690                 response.argument.form.reset();
13691             }catch(e){}
13692         }
13693         if(this.loadScripts){
13694             this.renderer.render(this.el, response, this,
13695                 this.updateComplete.createDelegate(this, [response]));
13696         }else{
13697             this.renderer.render(this.el, response, this);
13698             this.updateComplete(response);
13699         }
13700     },
13701
13702     updateComplete : function(response){
13703         this.fireEvent("update", this.el, response);
13704         if(typeof response.argument.callback == "function"){
13705             response.argument.callback(this.el, true, response);
13706         }
13707     },
13708
13709     /**
13710      * @private
13711      */
13712     processFailure : function(response){
13713         this.transaction = null;
13714         this.fireEvent("failure", this.el, response);
13715         if(typeof response.argument.callback == "function"){
13716             response.argument.callback(this.el, false, response);
13717         }
13718     },
13719
13720     /**
13721      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13722      * @param {Object} renderer The object implementing the render() method
13723      */
13724     setRenderer : function(renderer){
13725         this.renderer = renderer;
13726     },
13727
13728     getRenderer : function(){
13729        return this.renderer;
13730     },
13731
13732     /**
13733      * Set the defaultUrl used for updates
13734      * @param {String/Function} defaultUrl The url or a function to call to get the url
13735      */
13736     setDefaultUrl : function(defaultUrl){
13737         this.defaultUrl = defaultUrl;
13738     },
13739
13740     /**
13741      * Aborts the executing transaction
13742      */
13743     abort : function(){
13744         if(this.transaction){
13745             Roo.Ajax.abort(this.transaction);
13746         }
13747     },
13748
13749     /**
13750      * Returns true if an update is in progress
13751      * @return {Boolean}
13752      */
13753     isUpdating : function(){
13754         if(this.transaction){
13755             return Roo.Ajax.isLoading(this.transaction);
13756         }
13757         return false;
13758     }
13759 });
13760
13761 /**
13762  * @class Roo.UpdateManager.defaults
13763  * @static (not really - but it helps the doc tool)
13764  * The defaults collection enables customizing the default properties of UpdateManager
13765  */
13766    Roo.UpdateManager.defaults = {
13767        /**
13768          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13769          * @type Number
13770          */
13771          timeout : 30,
13772
13773          /**
13774          * True to process scripts by default (Defaults to false).
13775          * @type Boolean
13776          */
13777         loadScripts : false,
13778
13779         /**
13780         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13781         * @type String
13782         */
13783         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13784         /**
13785          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13786          * @type Boolean
13787          */
13788         disableCaching : false,
13789         /**
13790          * Whether to show indicatorText when loading (Defaults to true).
13791          * @type Boolean
13792          */
13793         showLoadIndicator : true,
13794         /**
13795          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13796          * @type String
13797          */
13798         indicatorText : '<div class="loading-indicator">Loading...</div>'
13799    };
13800
13801 /**
13802  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13803  *Usage:
13804  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13805  * @param {String/HTMLElement/Roo.Element} el The element to update
13806  * @param {String} url The url
13807  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13808  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13809  * @static
13810  * @deprecated
13811  * @member Roo.UpdateManager
13812  */
13813 Roo.UpdateManager.updateElement = function(el, url, params, options){
13814     var um = Roo.get(el, true).getUpdateManager();
13815     Roo.apply(um, options);
13816     um.update(url, params, options ? options.callback : null);
13817 };
13818 // alias for backwards compat
13819 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13820 /**
13821  * @class Roo.UpdateManager.BasicRenderer
13822  * Default Content renderer. Updates the elements innerHTML with the responseText.
13823  */
13824 Roo.UpdateManager.BasicRenderer = function(){};
13825
13826 Roo.UpdateManager.BasicRenderer.prototype = {
13827     /**
13828      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13829      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13830      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13831      * @param {Roo.Element} el The element being rendered
13832      * @param {Object} response The YUI Connect response object
13833      * @param {UpdateManager} updateManager The calling update manager
13834      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13835      */
13836      render : function(el, response, updateManager, callback){
13837         el.update(response.responseText, updateManager.loadScripts, callback);
13838     }
13839 };
13840 /*
13841  * Based on:
13842  * Roo JS
13843  * (c)) Alan Knowles
13844  * Licence : LGPL
13845  */
13846
13847
13848 /**
13849  * @class Roo.DomTemplate
13850  * @extends Roo.Template
13851  * An effort at a dom based template engine..
13852  *
13853  * Similar to XTemplate, except it uses dom parsing to create the template..
13854  *
13855  * Supported features:
13856  *
13857  *  Tags:
13858
13859 <pre><code>
13860       {a_variable} - output encoded.
13861       {a_variable.format:("Y-m-d")} - call a method on the variable
13862       {a_variable:raw} - unencoded output
13863       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13864       {a_variable:this.method_on_template(...)} - call a method on the template object.
13865  
13866 </code></pre>
13867  *  The tpl tag:
13868 <pre><code>
13869         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13870         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13871         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13872         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13873   
13874 </code></pre>
13875  *      
13876  */
13877 Roo.DomTemplate = function()
13878 {
13879      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13880      if (this.html) {
13881         this.compile();
13882      }
13883 };
13884
13885
13886 Roo.extend(Roo.DomTemplate, Roo.Template, {
13887     /**
13888      * id counter for sub templates.
13889      */
13890     id : 0,
13891     /**
13892      * flag to indicate if dom parser is inside a pre,
13893      * it will strip whitespace if not.
13894      */
13895     inPre : false,
13896     
13897     /**
13898      * The various sub templates
13899      */
13900     tpls : false,
13901     
13902     
13903     
13904     /**
13905      *
13906      * basic tag replacing syntax
13907      * WORD:WORD()
13908      *
13909      * // you can fake an object call by doing this
13910      *  x.t:(test,tesT) 
13911      * 
13912      */
13913     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13914     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13915     
13916     iterChild : function (node, method) {
13917         
13918         var oldPre = this.inPre;
13919         if (node.tagName == 'PRE') {
13920             this.inPre = true;
13921         }
13922         for( var i = 0; i < node.childNodes.length; i++) {
13923             method.call(this, node.childNodes[i]);
13924         }
13925         this.inPre = oldPre;
13926     },
13927     
13928     
13929     
13930     /**
13931      * compile the template
13932      *
13933      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13934      *
13935      */
13936     compile: function()
13937     {
13938         var s = this.html;
13939         
13940         // covert the html into DOM...
13941         var doc = false;
13942         var div =false;
13943         try {
13944             doc = document.implementation.createHTMLDocument("");
13945             doc.documentElement.innerHTML =   this.html  ;
13946             div = doc.documentElement;
13947         } catch (e) {
13948             // old IE... - nasty -- it causes all sorts of issues.. with
13949             // images getting pulled from server..
13950             div = document.createElement('div');
13951             div.innerHTML = this.html;
13952         }
13953         //doc.documentElement.innerHTML = htmlBody
13954          
13955         
13956         
13957         this.tpls = [];
13958         var _t = this;
13959         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13960         
13961         var tpls = this.tpls;
13962         
13963         // create a top level template from the snippet..
13964         
13965         //Roo.log(div.innerHTML);
13966         
13967         var tpl = {
13968             uid : 'master',
13969             id : this.id++,
13970             attr : false,
13971             value : false,
13972             body : div.innerHTML,
13973             
13974             forCall : false,
13975             execCall : false,
13976             dom : div,
13977             isTop : true
13978             
13979         };
13980         tpls.unshift(tpl);
13981         
13982         
13983         // compile them...
13984         this.tpls = [];
13985         Roo.each(tpls, function(tp){
13986             this.compileTpl(tp);
13987             this.tpls[tp.id] = tp;
13988         }, this);
13989         
13990         this.master = tpls[0];
13991         return this;
13992         
13993         
13994     },
13995     
13996     compileNode : function(node, istop) {
13997         // test for
13998         //Roo.log(node);
13999         
14000         
14001         // skip anything not a tag..
14002         if (node.nodeType != 1) {
14003             if (node.nodeType == 3 && !this.inPre) {
14004                 // reduce white space..
14005                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
14006                 
14007             }
14008             return;
14009         }
14010         
14011         var tpl = {
14012             uid : false,
14013             id : false,
14014             attr : false,
14015             value : false,
14016             body : '',
14017             
14018             forCall : false,
14019             execCall : false,
14020             dom : false,
14021             isTop : istop
14022             
14023             
14024         };
14025         
14026         
14027         switch(true) {
14028             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
14029             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
14030             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
14031             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
14032             // no default..
14033         }
14034         
14035         
14036         if (!tpl.attr) {
14037             // just itterate children..
14038             this.iterChild(node,this.compileNode);
14039             return;
14040         }
14041         tpl.uid = this.id++;
14042         tpl.value = node.getAttribute('roo-' +  tpl.attr);
14043         node.removeAttribute('roo-'+ tpl.attr);
14044         if (tpl.attr != 'name') {
14045             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
14046             node.parentNode.replaceChild(placeholder,  node);
14047         } else {
14048             
14049             var placeholder =  document.createElement('span');
14050             placeholder.className = 'roo-tpl-' + tpl.value;
14051             node.parentNode.replaceChild(placeholder,  node);
14052         }
14053         
14054         // parent now sees '{domtplXXXX}
14055         this.iterChild(node,this.compileNode);
14056         
14057         // we should now have node body...
14058         var div = document.createElement('div');
14059         div.appendChild(node);
14060         tpl.dom = node;
14061         // this has the unfortunate side effect of converting tagged attributes
14062         // eg. href="{...}" into %7C...%7D
14063         // this has been fixed by searching for those combo's although it's a bit hacky..
14064         
14065         
14066         tpl.body = div.innerHTML;
14067         
14068         
14069          
14070         tpl.id = tpl.uid;
14071         switch(tpl.attr) {
14072             case 'for' :
14073                 switch (tpl.value) {
14074                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
14075                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
14076                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
14077                 }
14078                 break;
14079             
14080             case 'exec':
14081                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14082                 break;
14083             
14084             case 'if':     
14085                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
14086                 break;
14087             
14088             case 'name':
14089                 tpl.id  = tpl.value; // replace non characters???
14090                 break;
14091             
14092         }
14093         
14094         
14095         this.tpls.push(tpl);
14096         
14097         
14098         
14099     },
14100     
14101     
14102     
14103     
14104     /**
14105      * Compile a segment of the template into a 'sub-template'
14106      *
14107      * 
14108      * 
14109      *
14110      */
14111     compileTpl : function(tpl)
14112     {
14113         var fm = Roo.util.Format;
14114         var useF = this.disableFormats !== true;
14115         
14116         var sep = Roo.isGecko ? "+\n" : ",\n";
14117         
14118         var undef = function(str) {
14119             Roo.debug && Roo.log("Property not found :"  + str);
14120             return '';
14121         };
14122           
14123         //Roo.log(tpl.body);
14124         
14125         
14126         
14127         var fn = function(m, lbrace, name, format, args)
14128         {
14129             //Roo.log("ARGS");
14130             //Roo.log(arguments);
14131             args = args ? args.replace(/\\'/g,"'") : args;
14132             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
14133             if (typeof(format) == 'undefined') {
14134                 format =  'htmlEncode'; 
14135             }
14136             if (format == 'raw' ) {
14137                 format = false;
14138             }
14139             
14140             if(name.substr(0, 6) == 'domtpl'){
14141                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
14142             }
14143             
14144             // build an array of options to determine if value is undefined..
14145             
14146             // basically get 'xxxx.yyyy' then do
14147             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
14148             //    (function () { Roo.log("Property not found"); return ''; })() :
14149             //    ......
14150             
14151             var udef_ar = [];
14152             var lookfor = '';
14153             Roo.each(name.split('.'), function(st) {
14154                 lookfor += (lookfor.length ? '.': '') + st;
14155                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
14156             });
14157             
14158             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
14159             
14160             
14161             if(format && useF){
14162                 
14163                 args = args ? ',' + args : "";
14164                  
14165                 if(format.substr(0, 5) != "this."){
14166                     format = "fm." + format + '(';
14167                 }else{
14168                     format = 'this.call("'+ format.substr(5) + '", ';
14169                     args = ", values";
14170                 }
14171                 
14172                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
14173             }
14174              
14175             if (args && args.length) {
14176                 // called with xxyx.yuu:(test,test)
14177                 // change to ()
14178                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
14179             }
14180             // raw.. - :raw modifier..
14181             return "'"+ sep + udef_st  + name + ")"+sep+"'";
14182             
14183         };
14184         var body;
14185         // branched to use + in gecko and [].join() in others
14186         if(Roo.isGecko){
14187             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
14188                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
14189                     "';};};";
14190         }else{
14191             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
14192             body.push(tpl.body.replace(/(\r\n|\n)/g,
14193                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
14194             body.push("'].join('');};};");
14195             body = body.join('');
14196         }
14197         
14198         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
14199        
14200         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
14201         eval(body);
14202         
14203         return this;
14204     },
14205      
14206     /**
14207      * same as applyTemplate, except it's done to one of the subTemplates
14208      * when using named templates, you can do:
14209      *
14210      * var str = pl.applySubTemplate('your-name', values);
14211      *
14212      * 
14213      * @param {Number} id of the template
14214      * @param {Object} values to apply to template
14215      * @param {Object} parent (normaly the instance of this object)
14216      */
14217     applySubTemplate : function(id, values, parent)
14218     {
14219         
14220         
14221         var t = this.tpls[id];
14222         
14223         
14224         try { 
14225             if(t.ifCall && !t.ifCall.call(this, values, parent)){
14226                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14227                 return '';
14228             }
14229         } catch(e) {
14230             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14231             Roo.log(values);
14232           
14233             return '';
14234         }
14235         try { 
14236             
14237             if(t.execCall && t.execCall.call(this, values, parent)){
14238                 return '';
14239             }
14240         } catch(e) {
14241             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14242             Roo.log(values);
14243             return '';
14244         }
14245         
14246         try {
14247             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14248             parent = t.target ? values : parent;
14249             if(t.forCall && vs instanceof Array){
14250                 var buf = [];
14251                 for(var i = 0, len = vs.length; i < len; i++){
14252                     try {
14253                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
14254                     } catch (e) {
14255                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14256                         Roo.log(e.body);
14257                         //Roo.log(t.compiled);
14258                         Roo.log(vs[i]);
14259                     }   
14260                 }
14261                 return buf.join('');
14262             }
14263         } catch (e) {
14264             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14265             Roo.log(values);
14266             return '';
14267         }
14268         try {
14269             return t.compiled.call(this, vs, parent);
14270         } catch (e) {
14271             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14272             Roo.log(e.body);
14273             //Roo.log(t.compiled);
14274             Roo.log(values);
14275             return '';
14276         }
14277     },
14278
14279    
14280
14281     applyTemplate : function(values){
14282         return this.master.compiled.call(this, values, {});
14283         //var s = this.subs;
14284     },
14285
14286     apply : function(){
14287         return this.applyTemplate.apply(this, arguments);
14288     }
14289
14290  });
14291
14292 Roo.DomTemplate.from = function(el){
14293     el = Roo.getDom(el);
14294     return new Roo.Domtemplate(el.value || el.innerHTML);
14295 };/*
14296  * Based on:
14297  * Ext JS Library 1.1.1
14298  * Copyright(c) 2006-2007, Ext JS, LLC.
14299  *
14300  * Originally Released Under LGPL - original licence link has changed is not relivant.
14301  *
14302  * Fork - LGPL
14303  * <script type="text/javascript">
14304  */
14305
14306 /**
14307  * @class Roo.util.DelayedTask
14308  * Provides a convenient method of performing setTimeout where a new
14309  * timeout cancels the old timeout. An example would be performing validation on a keypress.
14310  * You can use this class to buffer
14311  * the keypress events for a certain number of milliseconds, and perform only if they stop
14312  * for that amount of time.
14313  * @constructor The parameters to this constructor serve as defaults and are not required.
14314  * @param {Function} fn (optional) The default function to timeout
14315  * @param {Object} scope (optional) The default scope of that timeout
14316  * @param {Array} args (optional) The default Array of arguments
14317  */
14318 Roo.util.DelayedTask = function(fn, scope, args){
14319     var id = null, d, t;
14320
14321     var call = function(){
14322         var now = new Date().getTime();
14323         if(now - t >= d){
14324             clearInterval(id);
14325             id = null;
14326             fn.apply(scope, args || []);
14327         }
14328     };
14329     /**
14330      * Cancels any pending timeout and queues a new one
14331      * @param {Number} delay The milliseconds to delay
14332      * @param {Function} newFn (optional) Overrides function passed to constructor
14333      * @param {Object} newScope (optional) Overrides scope passed to constructor
14334      * @param {Array} newArgs (optional) Overrides args passed to constructor
14335      */
14336     this.delay = function(delay, newFn, newScope, newArgs){
14337         if(id && delay != d){
14338             this.cancel();
14339         }
14340         d = delay;
14341         t = new Date().getTime();
14342         fn = newFn || fn;
14343         scope = newScope || scope;
14344         args = newArgs || args;
14345         if(!id){
14346             id = setInterval(call, d);
14347         }
14348     };
14349
14350     /**
14351      * Cancel the last queued timeout
14352      */
14353     this.cancel = function(){
14354         if(id){
14355             clearInterval(id);
14356             id = null;
14357         }
14358     };
14359 };/*
14360  * Based on:
14361  * Ext JS Library 1.1.1
14362  * Copyright(c) 2006-2007, Ext JS, LLC.
14363  *
14364  * Originally Released Under LGPL - original licence link has changed is not relivant.
14365  *
14366  * Fork - LGPL
14367  * <script type="text/javascript">
14368  */
14369 /**
14370  * @class Roo.util.TaskRunner
14371  * Manage background tasks - not sure why this is better that setInterval?
14372  * @static
14373  *
14374  */
14375  
14376 Roo.util.TaskRunner = function(interval){
14377     interval = interval || 10;
14378     var tasks = [], removeQueue = [];
14379     var id = 0;
14380     var running = false;
14381
14382     var stopThread = function(){
14383         running = false;
14384         clearInterval(id);
14385         id = 0;
14386     };
14387
14388     var startThread = function(){
14389         if(!running){
14390             running = true;
14391             id = setInterval(runTasks, interval);
14392         }
14393     };
14394
14395     var removeTask = function(task){
14396         removeQueue.push(task);
14397         if(task.onStop){
14398             task.onStop();
14399         }
14400     };
14401
14402     var runTasks = function(){
14403         if(removeQueue.length > 0){
14404             for(var i = 0, len = removeQueue.length; i < len; i++){
14405                 tasks.remove(removeQueue[i]);
14406             }
14407             removeQueue = [];
14408             if(tasks.length < 1){
14409                 stopThread();
14410                 return;
14411             }
14412         }
14413         var now = new Date().getTime();
14414         for(var i = 0, len = tasks.length; i < len; ++i){
14415             var t = tasks[i];
14416             var itime = now - t.taskRunTime;
14417             if(t.interval <= itime){
14418                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14419                 t.taskRunTime = now;
14420                 if(rt === false || t.taskRunCount === t.repeat){
14421                     removeTask(t);
14422                     return;
14423                 }
14424             }
14425             if(t.duration && t.duration <= (now - t.taskStartTime)){
14426                 removeTask(t);
14427             }
14428         }
14429     };
14430
14431     /**
14432      * Queues a new task.
14433      * @param {Object} task
14434      *
14435      * Task property : interval = how frequent to run.
14436      * Task object should implement
14437      * function run()
14438      * Task object may implement
14439      * function onStop()
14440      */
14441     this.start = function(task){
14442         tasks.push(task);
14443         task.taskStartTime = new Date().getTime();
14444         task.taskRunTime = 0;
14445         task.taskRunCount = 0;
14446         startThread();
14447         return task;
14448     };
14449     /**
14450      * Stop  new task.
14451      * @param {Object} task
14452      */
14453     this.stop = function(task){
14454         removeTask(task);
14455         return task;
14456     };
14457     /**
14458      * Stop all Tasks
14459      */
14460     this.stopAll = function(){
14461         stopThread();
14462         for(var i = 0, len = tasks.length; i < len; i++){
14463             if(tasks[i].onStop){
14464                 tasks[i].onStop();
14465             }
14466         }
14467         tasks = [];
14468         removeQueue = [];
14469     };
14470 };
14471
14472 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14473  * Based on:
14474  * Ext JS Library 1.1.1
14475  * Copyright(c) 2006-2007, Ext JS, LLC.
14476  *
14477  * Originally Released Under LGPL - original licence link has changed is not relivant.
14478  *
14479  * Fork - LGPL
14480  * <script type="text/javascript">
14481  */
14482
14483  
14484 /**
14485  * @class Roo.util.MixedCollection
14486  * @extends Roo.util.Observable
14487  * A Collection class that maintains both numeric indexes and keys and exposes events.
14488  * @constructor
14489  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14490  * collection (defaults to false)
14491  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14492  * and return the key value for that item.  This is used when available to look up the key on items that
14493  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14494  * equivalent to providing an implementation for the {@link #getKey} method.
14495  */
14496 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14497     this.items = [];
14498     this.map = {};
14499     this.keys = [];
14500     this.length = 0;
14501     this.addEvents({
14502         /**
14503          * @event clear
14504          * Fires when the collection is cleared.
14505          */
14506         "clear" : true,
14507         /**
14508          * @event add
14509          * Fires when an item is added to the collection.
14510          * @param {Number} index The index at which the item was added.
14511          * @param {Object} o The item added.
14512          * @param {String} key The key associated with the added item.
14513          */
14514         "add" : true,
14515         /**
14516          * @event replace
14517          * Fires when an item is replaced in the collection.
14518          * @param {String} key he key associated with the new added.
14519          * @param {Object} old The item being replaced.
14520          * @param {Object} new The new item.
14521          */
14522         "replace" : true,
14523         /**
14524          * @event remove
14525          * Fires when an item is removed from the collection.
14526          * @param {Object} o The item being removed.
14527          * @param {String} key (optional) The key associated with the removed item.
14528          */
14529         "remove" : true,
14530         "sort" : true
14531     });
14532     this.allowFunctions = allowFunctions === true;
14533     if(keyFn){
14534         this.getKey = keyFn;
14535     }
14536     Roo.util.MixedCollection.superclass.constructor.call(this);
14537 };
14538
14539 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14540     allowFunctions : false,
14541     
14542 /**
14543  * Adds an item to the collection.
14544  * @param {String} key The key to associate with the item
14545  * @param {Object} o The item to add.
14546  * @return {Object} The item added.
14547  */
14548     add : function(key, o){
14549         if(arguments.length == 1){
14550             o = arguments[0];
14551             key = this.getKey(o);
14552         }
14553         if(typeof key == "undefined" || key === null){
14554             this.length++;
14555             this.items.push(o);
14556             this.keys.push(null);
14557         }else{
14558             var old = this.map[key];
14559             if(old){
14560                 return this.replace(key, o);
14561             }
14562             this.length++;
14563             this.items.push(o);
14564             this.map[key] = o;
14565             this.keys.push(key);
14566         }
14567         this.fireEvent("add", this.length-1, o, key);
14568         return o;
14569     },
14570        
14571 /**
14572   * MixedCollection has a generic way to fetch keys if you implement getKey.
14573 <pre><code>
14574 // normal way
14575 var mc = new Roo.util.MixedCollection();
14576 mc.add(someEl.dom.id, someEl);
14577 mc.add(otherEl.dom.id, otherEl);
14578 //and so on
14579
14580 // using getKey
14581 var mc = new Roo.util.MixedCollection();
14582 mc.getKey = function(el){
14583    return el.dom.id;
14584 };
14585 mc.add(someEl);
14586 mc.add(otherEl);
14587
14588 // or via the constructor
14589 var mc = new Roo.util.MixedCollection(false, function(el){
14590    return el.dom.id;
14591 });
14592 mc.add(someEl);
14593 mc.add(otherEl);
14594 </code></pre>
14595  * @param o {Object} The item for which to find the key.
14596  * @return {Object} The key for the passed item.
14597  */
14598     getKey : function(o){
14599          return o.id; 
14600     },
14601    
14602 /**
14603  * Replaces an item in the collection.
14604  * @param {String} key The key associated with the item to replace, or the item to replace.
14605  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14606  * @return {Object}  The new item.
14607  */
14608     replace : function(key, o){
14609         if(arguments.length == 1){
14610             o = arguments[0];
14611             key = this.getKey(o);
14612         }
14613         var old = this.item(key);
14614         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14615              return this.add(key, o);
14616         }
14617         var index = this.indexOfKey(key);
14618         this.items[index] = o;
14619         this.map[key] = o;
14620         this.fireEvent("replace", key, old, o);
14621         return o;
14622     },
14623    
14624 /**
14625  * Adds all elements of an Array or an Object to the collection.
14626  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14627  * an Array of values, each of which are added to the collection.
14628  */
14629     addAll : function(objs){
14630         if(arguments.length > 1 || objs instanceof Array){
14631             var args = arguments.length > 1 ? arguments : objs;
14632             for(var i = 0, len = args.length; i < len; i++){
14633                 this.add(args[i]);
14634             }
14635         }else{
14636             for(var key in objs){
14637                 if(this.allowFunctions || typeof objs[key] != "function"){
14638                     this.add(key, objs[key]);
14639                 }
14640             }
14641         }
14642     },
14643    
14644 /**
14645  * Executes the specified function once for every item in the collection, passing each
14646  * item as the first and only parameter. returning false from the function will stop the iteration.
14647  * @param {Function} fn The function to execute for each item.
14648  * @param {Object} scope (optional) The scope in which to execute the function.
14649  */
14650     each : function(fn, scope){
14651         var items = [].concat(this.items); // each safe for removal
14652         for(var i = 0, len = items.length; i < len; i++){
14653             if(fn.call(scope || items[i], items[i], i, len) === false){
14654                 break;
14655             }
14656         }
14657     },
14658    
14659 /**
14660  * Executes the specified function once for every key in the collection, passing each
14661  * key, and its associated item as the first two parameters.
14662  * @param {Function} fn The function to execute for each item.
14663  * @param {Object} scope (optional) The scope in which to execute the function.
14664  */
14665     eachKey : function(fn, scope){
14666         for(var i = 0, len = this.keys.length; i < len; i++){
14667             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14668         }
14669     },
14670    
14671 /**
14672  * Returns the first item in the collection which elicits a true return value from the
14673  * passed selection function.
14674  * @param {Function} fn The selection function to execute for each item.
14675  * @param {Object} scope (optional) The scope in which to execute the function.
14676  * @return {Object} The first item in the collection which returned true from the selection function.
14677  */
14678     find : function(fn, scope){
14679         for(var i = 0, len = this.items.length; i < len; i++){
14680             if(fn.call(scope || window, this.items[i], this.keys[i])){
14681                 return this.items[i];
14682             }
14683         }
14684         return null;
14685     },
14686    
14687 /**
14688  * Inserts an item at the specified index in the collection.
14689  * @param {Number} index The index to insert the item at.
14690  * @param {String} key The key to associate with the new item, or the item itself.
14691  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14692  * @return {Object} The item inserted.
14693  */
14694     insert : function(index, key, o){
14695         if(arguments.length == 2){
14696             o = arguments[1];
14697             key = this.getKey(o);
14698         }
14699         if(index >= this.length){
14700             return this.add(key, o);
14701         }
14702         this.length++;
14703         this.items.splice(index, 0, o);
14704         if(typeof key != "undefined" && key != null){
14705             this.map[key] = o;
14706         }
14707         this.keys.splice(index, 0, key);
14708         this.fireEvent("add", index, o, key);
14709         return o;
14710     },
14711    
14712 /**
14713  * Removed an item from the collection.
14714  * @param {Object} o The item to remove.
14715  * @return {Object} The item removed.
14716  */
14717     remove : function(o){
14718         return this.removeAt(this.indexOf(o));
14719     },
14720    
14721 /**
14722  * Remove an item from a specified index in the collection.
14723  * @param {Number} index The index within the collection of the item to remove.
14724  */
14725     removeAt : function(index){
14726         if(index < this.length && index >= 0){
14727             this.length--;
14728             var o = this.items[index];
14729             this.items.splice(index, 1);
14730             var key = this.keys[index];
14731             if(typeof key != "undefined"){
14732                 delete this.map[key];
14733             }
14734             this.keys.splice(index, 1);
14735             this.fireEvent("remove", o, key);
14736         }
14737     },
14738    
14739 /**
14740  * Removed an item associated with the passed key fom the collection.
14741  * @param {String} key The key of the item to remove.
14742  */
14743     removeKey : function(key){
14744         return this.removeAt(this.indexOfKey(key));
14745     },
14746    
14747 /**
14748  * Returns the number of items in the collection.
14749  * @return {Number} the number of items in the collection.
14750  */
14751     getCount : function(){
14752         return this.length; 
14753     },
14754    
14755 /**
14756  * Returns index within the collection of the passed Object.
14757  * @param {Object} o The item to find the index of.
14758  * @return {Number} index of the item.
14759  */
14760     indexOf : function(o){
14761         if(!this.items.indexOf){
14762             for(var i = 0, len = this.items.length; i < len; i++){
14763                 if(this.items[i] == o) {
14764                     return i;
14765                 }
14766             }
14767             return -1;
14768         }else{
14769             return this.items.indexOf(o);
14770         }
14771     },
14772    
14773 /**
14774  * Returns index within the collection of the passed key.
14775  * @param {String} key The key to find the index of.
14776  * @return {Number} index of the key.
14777  */
14778     indexOfKey : function(key){
14779         if(!this.keys.indexOf){
14780             for(var i = 0, len = this.keys.length; i < len; i++){
14781                 if(this.keys[i] == key) {
14782                     return i;
14783                 }
14784             }
14785             return -1;
14786         }else{
14787             return this.keys.indexOf(key);
14788         }
14789     },
14790    
14791 /**
14792  * Returns the item associated with the passed key OR index. Key has priority over index.
14793  * @param {String/Number} key The key or index of the item.
14794  * @return {Object} The item associated with the passed key.
14795  */
14796     item : function(key){
14797         if (key === 'length') {
14798             return null;
14799         }
14800         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14801         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14802     },
14803     
14804 /**
14805  * Returns the item at the specified index.
14806  * @param {Number} index The index of the item.
14807  * @return {Object}
14808  */
14809     itemAt : function(index){
14810         return this.items[index];
14811     },
14812     
14813 /**
14814  * Returns the item associated with the passed key.
14815  * @param {String/Number} key The key of the item.
14816  * @return {Object} The item associated with the passed key.
14817  */
14818     key : function(key){
14819         return this.map[key];
14820     },
14821    
14822 /**
14823  * Returns true if the collection contains the passed Object as an item.
14824  * @param {Object} o  The Object to look for in the collection.
14825  * @return {Boolean} True if the collection contains the Object as an item.
14826  */
14827     contains : function(o){
14828         return this.indexOf(o) != -1;
14829     },
14830    
14831 /**
14832  * Returns true if the collection contains the passed Object as a key.
14833  * @param {String} key The key to look for in the collection.
14834  * @return {Boolean} True if the collection contains the Object as a key.
14835  */
14836     containsKey : function(key){
14837         return typeof this.map[key] != "undefined";
14838     },
14839    
14840 /**
14841  * Removes all items from the collection.
14842  */
14843     clear : function(){
14844         this.length = 0;
14845         this.items = [];
14846         this.keys = [];
14847         this.map = {};
14848         this.fireEvent("clear");
14849     },
14850    
14851 /**
14852  * Returns the first item in the collection.
14853  * @return {Object} the first item in the collection..
14854  */
14855     first : function(){
14856         return this.items[0]; 
14857     },
14858    
14859 /**
14860  * Returns the last item in the collection.
14861  * @return {Object} the last item in the collection..
14862  */
14863     last : function(){
14864         return this.items[this.length-1];   
14865     },
14866     
14867     _sort : function(property, dir, fn){
14868         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14869         fn = fn || function(a, b){
14870             return a-b;
14871         };
14872         var c = [], k = this.keys, items = this.items;
14873         for(var i = 0, len = items.length; i < len; i++){
14874             c[c.length] = {key: k[i], value: items[i], index: i};
14875         }
14876         c.sort(function(a, b){
14877             var v = fn(a[property], b[property]) * dsc;
14878             if(v == 0){
14879                 v = (a.index < b.index ? -1 : 1);
14880             }
14881             return v;
14882         });
14883         for(var i = 0, len = c.length; i < len; i++){
14884             items[i] = c[i].value;
14885             k[i] = c[i].key;
14886         }
14887         this.fireEvent("sort", this);
14888     },
14889     
14890     /**
14891      * Sorts this collection with the passed comparison function
14892      * @param {String} direction (optional) "ASC" or "DESC"
14893      * @param {Function} fn (optional) comparison function
14894      */
14895     sort : function(dir, fn){
14896         this._sort("value", dir, fn);
14897     },
14898     
14899     /**
14900      * Sorts this collection by keys
14901      * @param {String} direction (optional) "ASC" or "DESC"
14902      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14903      */
14904     keySort : function(dir, fn){
14905         this._sort("key", dir, fn || function(a, b){
14906             return String(a).toUpperCase()-String(b).toUpperCase();
14907         });
14908     },
14909     
14910     /**
14911      * Returns a range of items in this collection
14912      * @param {Number} startIndex (optional) defaults to 0
14913      * @param {Number} endIndex (optional) default to the last item
14914      * @return {Array} An array of items
14915      */
14916     getRange : function(start, end){
14917         var items = this.items;
14918         if(items.length < 1){
14919             return [];
14920         }
14921         start = start || 0;
14922         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14923         var r = [];
14924         if(start <= end){
14925             for(var i = start; i <= end; i++) {
14926                     r[r.length] = items[i];
14927             }
14928         }else{
14929             for(var i = start; i >= end; i--) {
14930                     r[r.length] = items[i];
14931             }
14932         }
14933         return r;
14934     },
14935         
14936     /**
14937      * Filter the <i>objects</i> in this collection by a specific property. 
14938      * Returns a new collection that has been filtered.
14939      * @param {String} property A property on your objects
14940      * @param {String/RegExp} value Either string that the property values 
14941      * should start with or a RegExp to test against the property
14942      * @return {MixedCollection} The new filtered collection
14943      */
14944     filter : function(property, value){
14945         if(!value.exec){ // not a regex
14946             value = String(value);
14947             if(value.length == 0){
14948                 return this.clone();
14949             }
14950             value = new RegExp("^" + Roo.escapeRe(value), "i");
14951         }
14952         return this.filterBy(function(o){
14953             return o && value.test(o[property]);
14954         });
14955         },
14956     
14957     /**
14958      * Filter by a function. * Returns a new collection that has been filtered.
14959      * The passed function will be called with each 
14960      * object in the collection. If the function returns true, the value is included 
14961      * otherwise it is filtered.
14962      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14963      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14964      * @return {MixedCollection} The new filtered collection
14965      */
14966     filterBy : function(fn, scope){
14967         var r = new Roo.util.MixedCollection();
14968         r.getKey = this.getKey;
14969         var k = this.keys, it = this.items;
14970         for(var i = 0, len = it.length; i < len; i++){
14971             if(fn.call(scope||this, it[i], k[i])){
14972                                 r.add(k[i], it[i]);
14973                         }
14974         }
14975         return r;
14976     },
14977     
14978     /**
14979      * Creates a duplicate of this collection
14980      * @return {MixedCollection}
14981      */
14982     clone : function(){
14983         var r = new Roo.util.MixedCollection();
14984         var k = this.keys, it = this.items;
14985         for(var i = 0, len = it.length; i < len; i++){
14986             r.add(k[i], it[i]);
14987         }
14988         r.getKey = this.getKey;
14989         return r;
14990     }
14991 });
14992 /**
14993  * Returns the item associated with the passed key or index.
14994  * @method
14995  * @param {String/Number} key The key or index of the item.
14996  * @return {Object} The item associated with the passed key.
14997  */
14998 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14999  * Based on:
15000  * Ext JS Library 1.1.1
15001  * Copyright(c) 2006-2007, Ext JS, LLC.
15002  *
15003  * Originally Released Under LGPL - original licence link has changed is not relivant.
15004  *
15005  * Fork - LGPL
15006  * <script type="text/javascript">
15007  */
15008 /**
15009  * @class Roo.util.JSON
15010  * Modified version of Douglas Crockford"s json.js that doesn"t
15011  * mess with the Object prototype 
15012  * http://www.json.org/js.html
15013  * @static
15014  */
15015 Roo.util.JSON = new (function(){
15016     var useHasOwn = {}.hasOwnProperty ? true : false;
15017     
15018     // crashes Safari in some instances
15019     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
15020     
15021     var pad = function(n) {
15022         return n < 10 ? "0" + n : n;
15023     };
15024     
15025     var m = {
15026         "\b": '\\b',
15027         "\t": '\\t',
15028         "\n": '\\n',
15029         "\f": '\\f',
15030         "\r": '\\r',
15031         '"' : '\\"',
15032         "\\": '\\\\'
15033     };
15034
15035     var encodeString = function(s){
15036         if (/["\\\x00-\x1f]/.test(s)) {
15037             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
15038                 var c = m[b];
15039                 if(c){
15040                     return c;
15041                 }
15042                 c = b.charCodeAt();
15043                 return "\\u00" +
15044                     Math.floor(c / 16).toString(16) +
15045                     (c % 16).toString(16);
15046             }) + '"';
15047         }
15048         return '"' + s + '"';
15049     };
15050     
15051     var encodeArray = function(o){
15052         var a = ["["], b, i, l = o.length, v;
15053             for (i = 0; i < l; i += 1) {
15054                 v = o[i];
15055                 switch (typeof v) {
15056                     case "undefined":
15057                     case "function":
15058                     case "unknown":
15059                         break;
15060                     default:
15061                         if (b) {
15062                             a.push(',');
15063                         }
15064                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
15065                         b = true;
15066                 }
15067             }
15068             a.push("]");
15069             return a.join("");
15070     };
15071     
15072     var encodeDate = function(o){
15073         return '"' + o.getFullYear() + "-" +
15074                 pad(o.getMonth() + 1) + "-" +
15075                 pad(o.getDate()) + "T" +
15076                 pad(o.getHours()) + ":" +
15077                 pad(o.getMinutes()) + ":" +
15078                 pad(o.getSeconds()) + '"';
15079     };
15080     
15081     /**
15082      * Encodes an Object, Array or other value
15083      * @param {Mixed} o The variable to encode
15084      * @return {String} The JSON string
15085      */
15086     this.encode = function(o)
15087     {
15088         // should this be extended to fully wrap stringify..
15089         
15090         if(typeof o == "undefined" || o === null){
15091             return "null";
15092         }else if(o instanceof Array){
15093             return encodeArray(o);
15094         }else if(o instanceof Date){
15095             return encodeDate(o);
15096         }else if(typeof o == "string"){
15097             return encodeString(o);
15098         }else if(typeof o == "number"){
15099             return isFinite(o) ? String(o) : "null";
15100         }else if(typeof o == "boolean"){
15101             return String(o);
15102         }else {
15103             var a = ["{"], b, i, v;
15104             for (i in o) {
15105                 if(!useHasOwn || o.hasOwnProperty(i)) {
15106                     v = o[i];
15107                     switch (typeof v) {
15108                     case "undefined":
15109                     case "function":
15110                     case "unknown":
15111                         break;
15112                     default:
15113                         if(b){
15114                             a.push(',');
15115                         }
15116                         a.push(this.encode(i), ":",
15117                                 v === null ? "null" : this.encode(v));
15118                         b = true;
15119                     }
15120                 }
15121             }
15122             a.push("}");
15123             return a.join("");
15124         }
15125     };
15126     
15127     /**
15128      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
15129      * @param {String} json The JSON string
15130      * @return {Object} The resulting object
15131      */
15132     this.decode = function(json){
15133         
15134         return  /** eval:var:json */ eval("(" + json + ')');
15135     };
15136 })();
15137 /** 
15138  * Shorthand for {@link Roo.util.JSON#encode}
15139  * @member Roo encode 
15140  * @method */
15141 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
15142 /** 
15143  * Shorthand for {@link Roo.util.JSON#decode}
15144  * @member Roo decode 
15145  * @method */
15146 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
15147 /*
15148  * Based on:
15149  * Ext JS Library 1.1.1
15150  * Copyright(c) 2006-2007, Ext JS, LLC.
15151  *
15152  * Originally Released Under LGPL - original licence link has changed is not relivant.
15153  *
15154  * Fork - LGPL
15155  * <script type="text/javascript">
15156  */
15157  
15158 /**
15159  * @class Roo.util.Format
15160  * Reusable data formatting functions
15161  * @static
15162  */
15163 Roo.util.Format = function(){
15164     var trimRe = /^\s+|\s+$/g;
15165     return {
15166         /**
15167          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
15168          * @param {String} value The string to truncate
15169          * @param {Number} length The maximum length to allow before truncating
15170          * @return {String} The converted text
15171          */
15172         ellipsis : function(value, len){
15173             if(value && value.length > len){
15174                 return value.substr(0, len-3)+"...";
15175             }
15176             return value;
15177         },
15178
15179         /**
15180          * Checks a reference and converts it to empty string if it is undefined
15181          * @param {Mixed} value Reference to check
15182          * @return {Mixed} Empty string if converted, otherwise the original value
15183          */
15184         undef : function(value){
15185             return typeof value != "undefined" ? value : "";
15186         },
15187
15188         /**
15189          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
15190          * @param {String} value The string to encode
15191          * @return {String} The encoded text
15192          */
15193         htmlEncode : function(value){
15194             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
15195         },
15196
15197         /**
15198          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
15199          * @param {String} value The string to decode
15200          * @return {String} The decoded text
15201          */
15202         htmlDecode : function(value){
15203             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
15204         },
15205
15206         /**
15207          * Trims any whitespace from either side of a string
15208          * @param {String} value The text to trim
15209          * @return {String} The trimmed text
15210          */
15211         trim : function(value){
15212             return String(value).replace(trimRe, "");
15213         },
15214
15215         /**
15216          * Returns a substring from within an original string
15217          * @param {String} value The original text
15218          * @param {Number} start The start index of the substring
15219          * @param {Number} length The length of the substring
15220          * @return {String} The substring
15221          */
15222         substr : function(value, start, length){
15223             return String(value).substr(start, length);
15224         },
15225
15226         /**
15227          * Converts a string to all lower case letters
15228          * @param {String} value The text to convert
15229          * @return {String} The converted text
15230          */
15231         lowercase : function(value){
15232             return String(value).toLowerCase();
15233         },
15234
15235         /**
15236          * Converts a string to all upper case letters
15237          * @param {String} value The text to convert
15238          * @return {String} The converted text
15239          */
15240         uppercase : function(value){
15241             return String(value).toUpperCase();
15242         },
15243
15244         /**
15245          * Converts the first character only of a string to upper case
15246          * @param {String} value The text to convert
15247          * @return {String} The converted text
15248          */
15249         capitalize : function(value){
15250             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15251         },
15252
15253         // private
15254         call : function(value, fn){
15255             if(arguments.length > 2){
15256                 var args = Array.prototype.slice.call(arguments, 2);
15257                 args.unshift(value);
15258                  
15259                 return /** eval:var:value */  eval(fn).apply(window, args);
15260             }else{
15261                 /** eval:var:value */
15262                 return /** eval:var:value */ eval(fn).call(window, value);
15263             }
15264         },
15265
15266        
15267         /**
15268          * safer version of Math.toFixed..??/
15269          * @param {Number/String} value The numeric value to format
15270          * @param {Number/String} value Decimal places 
15271          * @return {String} The formatted currency string
15272          */
15273         toFixed : function(v, n)
15274         {
15275             // why not use to fixed - precision is buggered???
15276             if (!n) {
15277                 return Math.round(v-0);
15278             }
15279             var fact = Math.pow(10,n+1);
15280             v = (Math.round((v-0)*fact))/fact;
15281             var z = (''+fact).substring(2);
15282             if (v == Math.floor(v)) {
15283                 return Math.floor(v) + '.' + z;
15284             }
15285             
15286             // now just padd decimals..
15287             var ps = String(v).split('.');
15288             var fd = (ps[1] + z);
15289             var r = fd.substring(0,n); 
15290             var rm = fd.substring(n); 
15291             if (rm < 5) {
15292                 return ps[0] + '.' + r;
15293             }
15294             r*=1; // turn it into a number;
15295             r++;
15296             if (String(r).length != n) {
15297                 ps[0]*=1;
15298                 ps[0]++;
15299                 r = String(r).substring(1); // chop the end off.
15300             }
15301             
15302             return ps[0] + '.' + r;
15303              
15304         },
15305         
15306         /**
15307          * Format a number as US currency
15308          * @param {Number/String} value The numeric value to format
15309          * @return {String} The formatted currency string
15310          */
15311         usMoney : function(v){
15312             return '$' + Roo.util.Format.number(v);
15313         },
15314         
15315         /**
15316          * Format a number
15317          * eventually this should probably emulate php's number_format
15318          * @param {Number/String} value The numeric value to format
15319          * @param {Number} decimals number of decimal places
15320          * @param {String} delimiter for thousands (default comma)
15321          * @return {String} The formatted currency string
15322          */
15323         number : function(v, decimals, thousandsDelimiter)
15324         {
15325             // multiply and round.
15326             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15327             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15328             
15329             var mul = Math.pow(10, decimals);
15330             var zero = String(mul).substring(1);
15331             v = (Math.round((v-0)*mul))/mul;
15332             
15333             // if it's '0' number.. then
15334             
15335             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15336             v = String(v);
15337             var ps = v.split('.');
15338             var whole = ps[0];
15339             
15340             var r = /(\d+)(\d{3})/;
15341             // add comma's
15342             
15343             if(thousandsDelimiter.length != 0) {
15344                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15345             } 
15346             
15347             var sub = ps[1] ?
15348                     // has decimals..
15349                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15350                     // does not have decimals
15351                     (decimals ? ('.' + zero) : '');
15352             
15353             
15354             return whole + sub ;
15355         },
15356         
15357         /**
15358          * Parse a value into a formatted date using the specified format pattern.
15359          * @param {Mixed} value The value to format
15360          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15361          * @return {String} The formatted date string
15362          */
15363         date : function(v, format){
15364             if(!v){
15365                 return "";
15366             }
15367             if(!(v instanceof Date)){
15368                 v = new Date(Date.parse(v));
15369             }
15370             return v.dateFormat(format || Roo.util.Format.defaults.date);
15371         },
15372
15373         /**
15374          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15375          * @param {String} format Any valid date format string
15376          * @return {Function} The date formatting function
15377          */
15378         dateRenderer : function(format){
15379             return function(v){
15380                 return Roo.util.Format.date(v, format);  
15381             };
15382         },
15383
15384         // private
15385         stripTagsRE : /<\/?[^>]+>/gi,
15386         
15387         /**
15388          * Strips all HTML tags
15389          * @param {Mixed} value The text from which to strip tags
15390          * @return {String} The stripped text
15391          */
15392         stripTags : function(v){
15393             return !v ? v : String(v).replace(this.stripTagsRE, "");
15394         },
15395         
15396         /**
15397          * Size in Mb,Gb etc.
15398          * @param {Number} value The number to be formated
15399          * @param {number} decimals how many decimal places
15400          * @return {String} the formated string
15401          */
15402         size : function(value, decimals)
15403         {
15404             var sizes = ['b', 'k', 'M', 'G', 'T'];
15405             if (value == 0) {
15406                 return 0;
15407             }
15408             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15409             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15410         }
15411         
15412         
15413         
15414     };
15415 }();
15416 Roo.util.Format.defaults = {
15417     date : 'd/M/Y'
15418 };/*
15419  * Based on:
15420  * Ext JS Library 1.1.1
15421  * Copyright(c) 2006-2007, Ext JS, LLC.
15422  *
15423  * Originally Released Under LGPL - original licence link has changed is not relivant.
15424  *
15425  * Fork - LGPL
15426  * <script type="text/javascript">
15427  */
15428
15429
15430  
15431
15432 /**
15433  * @class Roo.MasterTemplate
15434  * @extends Roo.Template
15435  * Provides a template that can have child templates. The syntax is:
15436 <pre><code>
15437 var t = new Roo.MasterTemplate(
15438         '&lt;select name="{name}"&gt;',
15439                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15440         '&lt;/select&gt;'
15441 );
15442 t.add('options', {value: 'foo', text: 'bar'});
15443 // or you can add multiple child elements in one shot
15444 t.addAll('options', [
15445     {value: 'foo', text: 'bar'},
15446     {value: 'foo2', text: 'bar2'},
15447     {value: 'foo3', text: 'bar3'}
15448 ]);
15449 // then append, applying the master template values
15450 t.append('my-form', {name: 'my-select'});
15451 </code></pre>
15452 * A name attribute for the child template is not required if you have only one child
15453 * template or you want to refer to them by index.
15454  */
15455 Roo.MasterTemplate = function(){
15456     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15457     this.originalHtml = this.html;
15458     var st = {};
15459     var m, re = this.subTemplateRe;
15460     re.lastIndex = 0;
15461     var subIndex = 0;
15462     while(m = re.exec(this.html)){
15463         var name = m[1], content = m[2];
15464         st[subIndex] = {
15465             name: name,
15466             index: subIndex,
15467             buffer: [],
15468             tpl : new Roo.Template(content)
15469         };
15470         if(name){
15471             st[name] = st[subIndex];
15472         }
15473         st[subIndex].tpl.compile();
15474         st[subIndex].tpl.call = this.call.createDelegate(this);
15475         subIndex++;
15476     }
15477     this.subCount = subIndex;
15478     this.subs = st;
15479 };
15480 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15481     /**
15482     * The regular expression used to match sub templates
15483     * @type RegExp
15484     * @property
15485     */
15486     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15487
15488     /**
15489      * Applies the passed values to a child template.
15490      * @param {String/Number} name (optional) The name or index of the child template
15491      * @param {Array/Object} values The values to be applied to the template
15492      * @return {MasterTemplate} this
15493      */
15494      add : function(name, values){
15495         if(arguments.length == 1){
15496             values = arguments[0];
15497             name = 0;
15498         }
15499         var s = this.subs[name];
15500         s.buffer[s.buffer.length] = s.tpl.apply(values);
15501         return this;
15502     },
15503
15504     /**
15505      * Applies all the passed values to a child template.
15506      * @param {String/Number} name (optional) The name or index of the child template
15507      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15508      * @param {Boolean} reset (optional) True to reset the template first
15509      * @return {MasterTemplate} this
15510      */
15511     fill : function(name, values, reset){
15512         var a = arguments;
15513         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15514             values = a[0];
15515             name = 0;
15516             reset = a[1];
15517         }
15518         if(reset){
15519             this.reset();
15520         }
15521         for(var i = 0, len = values.length; i < len; i++){
15522             this.add(name, values[i]);
15523         }
15524         return this;
15525     },
15526
15527     /**
15528      * Resets the template for reuse
15529      * @return {MasterTemplate} this
15530      */
15531      reset : function(){
15532         var s = this.subs;
15533         for(var i = 0; i < this.subCount; i++){
15534             s[i].buffer = [];
15535         }
15536         return this;
15537     },
15538
15539     applyTemplate : function(values){
15540         var s = this.subs;
15541         var replaceIndex = -1;
15542         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15543             return s[++replaceIndex].buffer.join("");
15544         });
15545         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15546     },
15547
15548     apply : function(){
15549         return this.applyTemplate.apply(this, arguments);
15550     },
15551
15552     compile : function(){return this;}
15553 });
15554
15555 /**
15556  * Alias for fill().
15557  * @method
15558  */
15559 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15560  /**
15561  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15562  * var tpl = Roo.MasterTemplate.from('element-id');
15563  * @param {String/HTMLElement} el
15564  * @param {Object} config
15565  * @static
15566  */
15567 Roo.MasterTemplate.from = function(el, config){
15568     el = Roo.getDom(el);
15569     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15570 };/*
15571  * Based on:
15572  * Ext JS Library 1.1.1
15573  * Copyright(c) 2006-2007, Ext JS, LLC.
15574  *
15575  * Originally Released Under LGPL - original licence link has changed is not relivant.
15576  *
15577  * Fork - LGPL
15578  * <script type="text/javascript">
15579  */
15580
15581  
15582 /**
15583  * @class Roo.util.CSS
15584  * Utility class for manipulating CSS rules
15585  * @static
15586
15587  */
15588 Roo.util.CSS = function(){
15589         var rules = null;
15590         var doc = document;
15591
15592     var camelRe = /(-[a-z])/gi;
15593     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15594
15595    return {
15596    /**
15597     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15598     * tag and appended to the HEAD of the document.
15599     * @param {String|Object} cssText The text containing the css rules
15600     * @param {String} id An id to add to the stylesheet for later removal
15601     * @return {StyleSheet}
15602     */
15603     createStyleSheet : function(cssText, id){
15604         var ss;
15605         var head = doc.getElementsByTagName("head")[0];
15606         var nrules = doc.createElement("style");
15607         nrules.setAttribute("type", "text/css");
15608         if(id){
15609             nrules.setAttribute("id", id);
15610         }
15611         if (typeof(cssText) != 'string') {
15612             // support object maps..
15613             // not sure if this a good idea.. 
15614             // perhaps it should be merged with the general css handling
15615             // and handle js style props.
15616             var cssTextNew = [];
15617             for(var n in cssText) {
15618                 var citems = [];
15619                 for(var k in cssText[n]) {
15620                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15621                 }
15622                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15623                 
15624             }
15625             cssText = cssTextNew.join("\n");
15626             
15627         }
15628        
15629        
15630        if(Roo.isIE){
15631            head.appendChild(nrules);
15632            ss = nrules.styleSheet;
15633            ss.cssText = cssText;
15634        }else{
15635            try{
15636                 nrules.appendChild(doc.createTextNode(cssText));
15637            }catch(e){
15638                nrules.cssText = cssText; 
15639            }
15640            head.appendChild(nrules);
15641            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15642        }
15643        this.cacheStyleSheet(ss);
15644        return ss;
15645    },
15646
15647    /**
15648     * Removes a style or link tag by id
15649     * @param {String} id The id of the tag
15650     */
15651    removeStyleSheet : function(id){
15652        var existing = doc.getElementById(id);
15653        if(existing){
15654            existing.parentNode.removeChild(existing);
15655        }
15656    },
15657
15658    /**
15659     * Dynamically swaps an existing stylesheet reference for a new one
15660     * @param {String} id The id of an existing link tag to remove
15661     * @param {String} url The href of the new stylesheet to include
15662     */
15663    swapStyleSheet : function(id, url){
15664        this.removeStyleSheet(id);
15665        var ss = doc.createElement("link");
15666        ss.setAttribute("rel", "stylesheet");
15667        ss.setAttribute("type", "text/css");
15668        ss.setAttribute("id", id);
15669        ss.setAttribute("href", url);
15670        doc.getElementsByTagName("head")[0].appendChild(ss);
15671    },
15672    
15673    /**
15674     * Refresh the rule cache if you have dynamically added stylesheets
15675     * @return {Object} An object (hash) of rules indexed by selector
15676     */
15677    refreshCache : function(){
15678        return this.getRules(true);
15679    },
15680
15681    // private
15682    cacheStyleSheet : function(stylesheet){
15683        if(!rules){
15684            rules = {};
15685        }
15686        try{// try catch for cross domain access issue
15687            var ssRules = stylesheet.cssRules || stylesheet.rules;
15688            for(var j = ssRules.length-1; j >= 0; --j){
15689                rules[ssRules[j].selectorText] = ssRules[j];
15690            }
15691        }catch(e){}
15692    },
15693    
15694    /**
15695     * Gets all css rules for the document
15696     * @param {Boolean} refreshCache true to refresh the internal cache
15697     * @return {Object} An object (hash) of rules indexed by selector
15698     */
15699    getRules : function(refreshCache){
15700                 if(rules == null || refreshCache){
15701                         rules = {};
15702                         var ds = doc.styleSheets;
15703                         for(var i =0, len = ds.length; i < len; i++){
15704                             try{
15705                         this.cacheStyleSheet(ds[i]);
15706                     }catch(e){} 
15707                 }
15708                 }
15709                 return rules;
15710         },
15711         
15712         /**
15713     * Gets an an individual CSS rule by selector(s)
15714     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15715     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15716     * @return {CSSRule} The CSS rule or null if one is not found
15717     */
15718    getRule : function(selector, refreshCache){
15719                 var rs = this.getRules(refreshCache);
15720                 if(!(selector instanceof Array)){
15721                     return rs[selector];
15722                 }
15723                 for(var i = 0; i < selector.length; i++){
15724                         if(rs[selector[i]]){
15725                                 return rs[selector[i]];
15726                         }
15727                 }
15728                 return null;
15729         },
15730         
15731         
15732         /**
15733     * Updates a rule property
15734     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15735     * @param {String} property The css property
15736     * @param {String} value The new value for the property
15737     * @return {Boolean} true If a rule was found and updated
15738     */
15739    updateRule : function(selector, property, value){
15740                 if(!(selector instanceof Array)){
15741                         var rule = this.getRule(selector);
15742                         if(rule){
15743                                 rule.style[property.replace(camelRe, camelFn)] = value;
15744                                 return true;
15745                         }
15746                 }else{
15747                         for(var i = 0; i < selector.length; i++){
15748                                 if(this.updateRule(selector[i], property, value)){
15749                                         return true;
15750                                 }
15751                         }
15752                 }
15753                 return false;
15754         }
15755    };   
15756 }();/*
15757  * Based on:
15758  * Ext JS Library 1.1.1
15759  * Copyright(c) 2006-2007, Ext JS, LLC.
15760  *
15761  * Originally Released Under LGPL - original licence link has changed is not relivant.
15762  *
15763  * Fork - LGPL
15764  * <script type="text/javascript">
15765  */
15766
15767  
15768
15769 /**
15770  * @class Roo.util.ClickRepeater
15771  * @extends Roo.util.Observable
15772  * 
15773  * A wrapper class which can be applied to any element. Fires a "click" event while the
15774  * mouse is pressed. The interval between firings may be specified in the config but
15775  * defaults to 10 milliseconds.
15776  * 
15777  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15778  * 
15779  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15780  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15781  * Similar to an autorepeat key delay.
15782  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15783  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15784  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15785  *           "interval" and "delay" are ignored. "immediate" is honored.
15786  * @cfg {Boolean} preventDefault True to prevent the default click event
15787  * @cfg {Boolean} stopDefault True to stop the default click event
15788  * 
15789  * @history
15790  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15791  *     2007-02-02 jvs Renamed to ClickRepeater
15792  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15793  *
15794  *  @constructor
15795  * @param {String/HTMLElement/Element} el The element to listen on
15796  * @param {Object} config
15797  **/
15798 Roo.util.ClickRepeater = function(el, config)
15799 {
15800     this.el = Roo.get(el);
15801     this.el.unselectable();
15802
15803     Roo.apply(this, config);
15804
15805     this.addEvents({
15806     /**
15807      * @event mousedown
15808      * Fires when the mouse button is depressed.
15809      * @param {Roo.util.ClickRepeater} this
15810      */
15811         "mousedown" : true,
15812     /**
15813      * @event click
15814      * Fires on a specified interval during the time the element is pressed.
15815      * @param {Roo.util.ClickRepeater} this
15816      */
15817         "click" : true,
15818     /**
15819      * @event mouseup
15820      * Fires when the mouse key is released.
15821      * @param {Roo.util.ClickRepeater} this
15822      */
15823         "mouseup" : true
15824     });
15825
15826     this.el.on("mousedown", this.handleMouseDown, this);
15827     if(this.preventDefault || this.stopDefault){
15828         this.el.on("click", function(e){
15829             if(this.preventDefault){
15830                 e.preventDefault();
15831             }
15832             if(this.stopDefault){
15833                 e.stopEvent();
15834             }
15835         }, this);
15836     }
15837
15838     // allow inline handler
15839     if(this.handler){
15840         this.on("click", this.handler,  this.scope || this);
15841     }
15842
15843     Roo.util.ClickRepeater.superclass.constructor.call(this);
15844 };
15845
15846 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15847     interval : 20,
15848     delay: 250,
15849     preventDefault : true,
15850     stopDefault : false,
15851     timer : 0,
15852
15853     // private
15854     handleMouseDown : function(){
15855         clearTimeout(this.timer);
15856         this.el.blur();
15857         if(this.pressClass){
15858             this.el.addClass(this.pressClass);
15859         }
15860         this.mousedownTime = new Date();
15861
15862         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15863         this.el.on("mouseout", this.handleMouseOut, this);
15864
15865         this.fireEvent("mousedown", this);
15866         this.fireEvent("click", this);
15867         
15868         this.timer = this.click.defer(this.delay || this.interval, this);
15869     },
15870
15871     // private
15872     click : function(){
15873         this.fireEvent("click", this);
15874         this.timer = this.click.defer(this.getInterval(), this);
15875     },
15876
15877     // private
15878     getInterval: function(){
15879         if(!this.accelerate){
15880             return this.interval;
15881         }
15882         var pressTime = this.mousedownTime.getElapsed();
15883         if(pressTime < 500){
15884             return 400;
15885         }else if(pressTime < 1700){
15886             return 320;
15887         }else if(pressTime < 2600){
15888             return 250;
15889         }else if(pressTime < 3500){
15890             return 180;
15891         }else if(pressTime < 4400){
15892             return 140;
15893         }else if(pressTime < 5300){
15894             return 80;
15895         }else if(pressTime < 6200){
15896             return 50;
15897         }else{
15898             return 10;
15899         }
15900     },
15901
15902     // private
15903     handleMouseOut : function(){
15904         clearTimeout(this.timer);
15905         if(this.pressClass){
15906             this.el.removeClass(this.pressClass);
15907         }
15908         this.el.on("mouseover", this.handleMouseReturn, this);
15909     },
15910
15911     // private
15912     handleMouseReturn : function(){
15913         this.el.un("mouseover", this.handleMouseReturn);
15914         if(this.pressClass){
15915             this.el.addClass(this.pressClass);
15916         }
15917         this.click();
15918     },
15919
15920     // private
15921     handleMouseUp : function(){
15922         clearTimeout(this.timer);
15923         this.el.un("mouseover", this.handleMouseReturn);
15924         this.el.un("mouseout", this.handleMouseOut);
15925         Roo.get(document).un("mouseup", this.handleMouseUp);
15926         this.el.removeClass(this.pressClass);
15927         this.fireEvent("mouseup", this);
15928     }
15929 });/**
15930  * @class Roo.util.Clipboard
15931  * @static
15932  * 
15933  * Clipboard UTILS
15934  * 
15935  **/
15936 Roo.util.Clipboard = {
15937     /**
15938      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15939      * @param {String} text to copy to clipboard
15940      */
15941     write : function(text) {
15942         // navigator clipboard api needs a secure context (https)
15943         if (navigator.clipboard && window.isSecureContext) {
15944             // navigator clipboard api method'
15945             navigator.clipboard.writeText(text);
15946             return ;
15947         } 
15948         // text area method
15949         var ta = document.createElement("textarea");
15950         ta.value = text;
15951         // make the textarea out of viewport
15952         ta.style.position = "fixed";
15953         ta.style.left = "-999999px";
15954         ta.style.top = "-999999px";
15955         document.body.appendChild(ta);
15956         ta.focus();
15957         ta.select();
15958         document.execCommand('copy');
15959         (function() {
15960             ta.remove();
15961         }).defer(100);
15962         
15963     }
15964         
15965 }
15966     /*
15967  * Based on:
15968  * Ext JS Library 1.1.1
15969  * Copyright(c) 2006-2007, Ext JS, LLC.
15970  *
15971  * Originally Released Under LGPL - original licence link has changed is not relivant.
15972  *
15973  * Fork - LGPL
15974  * <script type="text/javascript">
15975  */
15976
15977  
15978 /**
15979  * @class Roo.KeyNav
15980  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15981  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15982  * way to implement custom navigation schemes for any UI component.</p>
15983  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15984  * pageUp, pageDown, del, home, end.  Usage:</p>
15985  <pre><code>
15986 var nav = new Roo.KeyNav("my-element", {
15987     "left" : function(e){
15988         this.moveLeft(e.ctrlKey);
15989     },
15990     "right" : function(e){
15991         this.moveRight(e.ctrlKey);
15992     },
15993     "enter" : function(e){
15994         this.save();
15995     },
15996     scope : this
15997 });
15998 </code></pre>
15999  * @constructor
16000  * @param {String/HTMLElement/Roo.Element} el The element to bind to
16001  * @param {Object} config The config
16002  */
16003 Roo.KeyNav = function(el, config){
16004     this.el = Roo.get(el);
16005     Roo.apply(this, config);
16006     if(!this.disabled){
16007         this.disabled = true;
16008         this.enable();
16009     }
16010 };
16011
16012 Roo.KeyNav.prototype = {
16013     /**
16014      * @cfg {Boolean} disabled
16015      * True to disable this KeyNav instance (defaults to false)
16016      */
16017     disabled : false,
16018     /**
16019      * @cfg {String} defaultEventAction
16020      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
16021      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
16022      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
16023      */
16024     defaultEventAction: "stopEvent",
16025     /**
16026      * @cfg {Boolean} forceKeyDown
16027      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
16028      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
16029      * handle keydown instead of keypress.
16030      */
16031     forceKeyDown : false,
16032
16033     // private
16034     prepareEvent : function(e){
16035         var k = e.getKey();
16036         var h = this.keyToHandler[k];
16037         //if(h && this[h]){
16038         //    e.stopPropagation();
16039         //}
16040         if(Roo.isSafari && h && k >= 37 && k <= 40){
16041             e.stopEvent();
16042         }
16043     },
16044
16045     // private
16046     relay : function(e){
16047         var k = e.getKey();
16048         var h = this.keyToHandler[k];
16049         if(h && this[h]){
16050             if(this.doRelay(e, this[h], h) !== true){
16051                 e[this.defaultEventAction]();
16052             }
16053         }
16054     },
16055
16056     // private
16057     doRelay : function(e, h, hname){
16058         return h.call(this.scope || this, e);
16059     },
16060
16061     // possible handlers
16062     enter : false,
16063     left : false,
16064     right : false,
16065     up : false,
16066     down : false,
16067     tab : false,
16068     esc : false,
16069     pageUp : false,
16070     pageDown : false,
16071     del : false,
16072     home : false,
16073     end : false,
16074
16075     // quick lookup hash
16076     keyToHandler : {
16077         37 : "left",
16078         39 : "right",
16079         38 : "up",
16080         40 : "down",
16081         33 : "pageUp",
16082         34 : "pageDown",
16083         46 : "del",
16084         36 : "home",
16085         35 : "end",
16086         13 : "enter",
16087         27 : "esc",
16088         9  : "tab"
16089     },
16090
16091         /**
16092          * Enable this KeyNav
16093          */
16094         enable: function(){
16095                 if(this.disabled){
16096             // ie won't do special keys on keypress, no one else will repeat keys with keydown
16097             // the EventObject will normalize Safari automatically
16098             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16099                 this.el.on("keydown", this.relay,  this);
16100             }else{
16101                 this.el.on("keydown", this.prepareEvent,  this);
16102                 this.el.on("keypress", this.relay,  this);
16103             }
16104                     this.disabled = false;
16105                 }
16106         },
16107
16108         /**
16109          * Disable this KeyNav
16110          */
16111         disable: function(){
16112                 if(!this.disabled){
16113                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
16114                 this.el.un("keydown", this.relay);
16115             }else{
16116                 this.el.un("keydown", this.prepareEvent);
16117                 this.el.un("keypress", this.relay);
16118             }
16119                     this.disabled = true;
16120                 }
16121         }
16122 };/*
16123  * Based on:
16124  * Ext JS Library 1.1.1
16125  * Copyright(c) 2006-2007, Ext JS, LLC.
16126  *
16127  * Originally Released Under LGPL - original licence link has changed is not relivant.
16128  *
16129  * Fork - LGPL
16130  * <script type="text/javascript">
16131  */
16132
16133  
16134 /**
16135  * @class Roo.KeyMap
16136  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
16137  * The constructor accepts the same config object as defined by {@link #addBinding}.
16138  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
16139  * combination it will call the function with this signature (if the match is a multi-key
16140  * combination the callback will still be called only once): (String key, Roo.EventObject e)
16141  * A KeyMap can also handle a string representation of keys.<br />
16142  * Usage:
16143  <pre><code>
16144 // map one key by key code
16145 var map = new Roo.KeyMap("my-element", {
16146     key: 13, // or Roo.EventObject.ENTER
16147     fn: myHandler,
16148     scope: myObject
16149 });
16150
16151 // map multiple keys to one action by string
16152 var map = new Roo.KeyMap("my-element", {
16153     key: "a\r\n\t",
16154     fn: myHandler,
16155     scope: myObject
16156 });
16157
16158 // map multiple keys to multiple actions by strings and array of codes
16159 var map = new Roo.KeyMap("my-element", [
16160     {
16161         key: [10,13],
16162         fn: function(){ alert("Return was pressed"); }
16163     }, {
16164         key: "abc",
16165         fn: function(){ alert('a, b or c was pressed'); }
16166     }, {
16167         key: "\t",
16168         ctrl:true,
16169         shift:true,
16170         fn: function(){ alert('Control + shift + tab was pressed.'); }
16171     }
16172 ]);
16173 </code></pre>
16174  * <b>Note: A KeyMap starts enabled</b>
16175  * @constructor
16176  * @param {String/HTMLElement/Roo.Element} el The element to bind to
16177  * @param {Object} config The config (see {@link #addBinding})
16178  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
16179  */
16180 Roo.KeyMap = function(el, config, eventName){
16181     this.el  = Roo.get(el);
16182     this.eventName = eventName || "keydown";
16183     this.bindings = [];
16184     if(config){
16185         this.addBinding(config);
16186     }
16187     this.enable();
16188 };
16189
16190 Roo.KeyMap.prototype = {
16191     /**
16192      * True to stop the event from bubbling and prevent the default browser action if the
16193      * key was handled by the KeyMap (defaults to false)
16194      * @type Boolean
16195      */
16196     stopEvent : false,
16197
16198     /**
16199      * Add a new binding to this KeyMap. The following config object properties are supported:
16200      * <pre>
16201 Property    Type             Description
16202 ----------  ---------------  ----------------------------------------------------------------------
16203 key         String/Array     A single keycode or an array of keycodes to handle
16204 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
16205 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
16206 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
16207 fn          Function         The function to call when KeyMap finds the expected key combination
16208 scope       Object           The scope of the callback function
16209 </pre>
16210      *
16211      * Usage:
16212      * <pre><code>
16213 // Create a KeyMap
16214 var map = new Roo.KeyMap(document, {
16215     key: Roo.EventObject.ENTER,
16216     fn: handleKey,
16217     scope: this
16218 });
16219
16220 //Add a new binding to the existing KeyMap later
16221 map.addBinding({
16222     key: 'abc',
16223     shift: true,
16224     fn: handleKey,
16225     scope: this
16226 });
16227 </code></pre>
16228      * @param {Object/Array} config A single KeyMap config or an array of configs
16229      */
16230         addBinding : function(config){
16231         if(config instanceof Array){
16232             for(var i = 0, len = config.length; i < len; i++){
16233                 this.addBinding(config[i]);
16234             }
16235             return;
16236         }
16237         var keyCode = config.key,
16238             shift = config.shift, 
16239             ctrl = config.ctrl, 
16240             alt = config.alt,
16241             fn = config.fn,
16242             scope = config.scope;
16243         if(typeof keyCode == "string"){
16244             var ks = [];
16245             var keyString = keyCode.toUpperCase();
16246             for(var j = 0, len = keyString.length; j < len; j++){
16247                 ks.push(keyString.charCodeAt(j));
16248             }
16249             keyCode = ks;
16250         }
16251         var keyArray = keyCode instanceof Array;
16252         var handler = function(e){
16253             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
16254                 var k = e.getKey();
16255                 if(keyArray){
16256                     for(var i = 0, len = keyCode.length; i < len; i++){
16257                         if(keyCode[i] == k){
16258                           if(this.stopEvent){
16259                               e.stopEvent();
16260                           }
16261                           fn.call(scope || window, k, e);
16262                           return;
16263                         }
16264                     }
16265                 }else{
16266                     if(k == keyCode){
16267                         if(this.stopEvent){
16268                            e.stopEvent();
16269                         }
16270                         fn.call(scope || window, k, e);
16271                     }
16272                 }
16273             }
16274         };
16275         this.bindings.push(handler);  
16276         },
16277
16278     /**
16279      * Shorthand for adding a single key listener
16280      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16281      * following options:
16282      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16283      * @param {Function} fn The function to call
16284      * @param {Object} scope (optional) The scope of the function
16285      */
16286     on : function(key, fn, scope){
16287         var keyCode, shift, ctrl, alt;
16288         if(typeof key == "object" && !(key instanceof Array)){
16289             keyCode = key.key;
16290             shift = key.shift;
16291             ctrl = key.ctrl;
16292             alt = key.alt;
16293         }else{
16294             keyCode = key;
16295         }
16296         this.addBinding({
16297             key: keyCode,
16298             shift: shift,
16299             ctrl: ctrl,
16300             alt: alt,
16301             fn: fn,
16302             scope: scope
16303         })
16304     },
16305
16306     // private
16307     handleKeyDown : function(e){
16308             if(this.enabled){ //just in case
16309             var b = this.bindings;
16310             for(var i = 0, len = b.length; i < len; i++){
16311                 b[i].call(this, e);
16312             }
16313             }
16314         },
16315         
16316         /**
16317          * Returns true if this KeyMap is enabled
16318          * @return {Boolean} 
16319          */
16320         isEnabled : function(){
16321             return this.enabled;  
16322         },
16323         
16324         /**
16325          * Enables this KeyMap
16326          */
16327         enable: function(){
16328                 if(!this.enabled){
16329                     this.el.on(this.eventName, this.handleKeyDown, this);
16330                     this.enabled = true;
16331                 }
16332         },
16333
16334         /**
16335          * Disable this KeyMap
16336          */
16337         disable: function(){
16338                 if(this.enabled){
16339                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16340                     this.enabled = false;
16341                 }
16342         }
16343 };/*
16344  * Based on:
16345  * Ext JS Library 1.1.1
16346  * Copyright(c) 2006-2007, Ext JS, LLC.
16347  *
16348  * Originally Released Under LGPL - original licence link has changed is not relivant.
16349  *
16350  * Fork - LGPL
16351  * <script type="text/javascript">
16352  */
16353
16354  
16355 /**
16356  * @class Roo.util.TextMetrics
16357  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16358  * wide, in pixels, a given block of text will be.
16359  * @static
16360  */
16361 Roo.util.TextMetrics = function(){
16362     var shared;
16363     return {
16364         /**
16365          * Measures the size of the specified text
16366          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16367          * that can affect the size of the rendered text
16368          * @param {String} text The text to measure
16369          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16370          * in order to accurately measure the text height
16371          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16372          */
16373         measure : function(el, text, fixedWidth){
16374             if(!shared){
16375                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16376             }
16377             shared.bind(el);
16378             shared.setFixedWidth(fixedWidth || 'auto');
16379             return shared.getSize(text);
16380         },
16381
16382         /**
16383          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16384          * the overhead of multiple calls to initialize the style properties on each measurement.
16385          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16386          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16387          * in order to accurately measure the text height
16388          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16389          */
16390         createInstance : function(el, fixedWidth){
16391             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16392         }
16393     };
16394 }();
16395
16396 /**
16397  * @class Roo.util.TextMetrics.Instance
16398  * Instance of  TextMetrics Calcuation
16399  * @constructor
16400  * Create a new TextMetrics Instance
16401  * @param {Object} bindto
16402  * @param {Boolean} fixedWidth
16403  */
16404
16405 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16406 {
16407     var ml = new Roo.Element(document.createElement('div'));
16408     document.body.appendChild(ml.dom);
16409     ml.position('absolute');
16410     ml.setLeftTop(-1000, -1000);
16411     ml.hide();
16412
16413     if(fixedWidth){
16414         ml.setWidth(fixedWidth);
16415     }
16416      
16417     var instance = {
16418         /**
16419          * Returns the size of the specified text based on the internal element's style and width properties
16420          * @param {String} text The text to measure
16421          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16422          */
16423         getSize : function(text){
16424             ml.update(text);
16425             var s = ml.getSize();
16426             ml.update('');
16427             return s;
16428         },
16429
16430         /**
16431          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16432          * that can affect the size of the rendered text
16433          * @param {String/HTMLElement} el The element, dom node or id
16434          */
16435         bind : function(el){
16436             ml.setStyle(
16437                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16438             );
16439         },
16440
16441         /**
16442          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16443          * to set a fixed width in order to accurately measure the text height.
16444          * @param {Number} width The width to set on the element
16445          */
16446         setFixedWidth : function(width){
16447             ml.setWidth(width);
16448         },
16449
16450         /**
16451          * Returns the measured width of the specified text
16452          * @param {String} text The text to measure
16453          * @return {Number} width The width in pixels
16454          */
16455         getWidth : function(text){
16456             ml.dom.style.width = 'auto';
16457             return this.getSize(text).width;
16458         },
16459
16460         /**
16461          * Returns the measured height of the specified text.  For multiline text, be sure to call
16462          * {@link #setFixedWidth} if necessary.
16463          * @param {String} text The text to measure
16464          * @return {Number} height The height in pixels
16465          */
16466         getHeight : function(text){
16467             return this.getSize(text).height;
16468         }
16469     };
16470
16471     instance.bind(bindTo);
16472
16473     return instance;
16474 };
16475
16476 // backwards compat
16477 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16478  * Based on:
16479  * Ext JS Library 1.1.1
16480  * Copyright(c) 2006-2007, Ext JS, LLC.
16481  *
16482  * Originally Released Under LGPL - original licence link has changed is not relivant.
16483  *
16484  * Fork - LGPL
16485  * <script type="text/javascript">
16486  */
16487
16488 /**
16489  * @class Roo.state.Provider
16490  * Abstract base class for state provider implementations. This class provides methods
16491  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16492  * Provider interface.
16493  */
16494 Roo.state.Provider = function(){
16495     /**
16496      * @event statechange
16497      * Fires when a state change occurs.
16498      * @param {Provider} this This state provider
16499      * @param {String} key The state key which was changed
16500      * @param {String} value The encoded value for the state
16501      */
16502     this.addEvents({
16503         "statechange": true
16504     });
16505     this.state = {};
16506     Roo.state.Provider.superclass.constructor.call(this);
16507 };
16508 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16509     /**
16510      * Returns the current value for a key
16511      * @param {String} name The key name
16512      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16513      * @return {Mixed} The state data
16514      */
16515     get : function(name, defaultValue){
16516         return typeof this.state[name] == "undefined" ?
16517             defaultValue : this.state[name];
16518     },
16519     
16520     /**
16521      * Clears a value from the state
16522      * @param {String} name The key name
16523      */
16524     clear : function(name){
16525         delete this.state[name];
16526         this.fireEvent("statechange", this, name, null);
16527     },
16528     
16529     /**
16530      * Sets the value for a key
16531      * @param {String} name The key name
16532      * @param {Mixed} value The value to set
16533      */
16534     set : function(name, value){
16535         this.state[name] = value;
16536         this.fireEvent("statechange", this, name, value);
16537     },
16538     
16539     /**
16540      * Decodes a string previously encoded with {@link #encodeValue}.
16541      * @param {String} value The value to decode
16542      * @return {Mixed} The decoded value
16543      */
16544     decodeValue : function(cookie){
16545         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16546         var matches = re.exec(unescape(cookie));
16547         if(!matches || !matches[1]) {
16548             return; // non state cookie
16549         }
16550         var type = matches[1];
16551         var v = matches[2];
16552         switch(type){
16553             case "n":
16554                 return parseFloat(v);
16555             case "d":
16556                 return new Date(Date.parse(v));
16557             case "b":
16558                 return (v == "1");
16559             case "a":
16560                 var all = [];
16561                 var values = v.split("^");
16562                 for(var i = 0, len = values.length; i < len; i++){
16563                     all.push(this.decodeValue(values[i]));
16564                 }
16565                 return all;
16566            case "o":
16567                 var all = {};
16568                 var values = v.split("^");
16569                 for(var i = 0, len = values.length; i < len; i++){
16570                     var kv = values[i].split("=");
16571                     all[kv[0]] = this.decodeValue(kv[1]);
16572                 }
16573                 return all;
16574            default:
16575                 return v;
16576         }
16577     },
16578     
16579     /**
16580      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16581      * @param {Mixed} value The value to encode
16582      * @return {String} The encoded value
16583      */
16584     encodeValue : function(v){
16585         var enc;
16586         if(typeof v == "number"){
16587             enc = "n:" + v;
16588         }else if(typeof v == "boolean"){
16589             enc = "b:" + (v ? "1" : "0");
16590         }else if(v instanceof Date){
16591             enc = "d:" + v.toGMTString();
16592         }else if(v instanceof Array){
16593             var flat = "";
16594             for(var i = 0, len = v.length; i < len; i++){
16595                 flat += this.encodeValue(v[i]);
16596                 if(i != len-1) {
16597                     flat += "^";
16598                 }
16599             }
16600             enc = "a:" + flat;
16601         }else if(typeof v == "object"){
16602             var flat = "";
16603             for(var key in v){
16604                 if(typeof v[key] != "function"){
16605                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16606                 }
16607             }
16608             enc = "o:" + flat.substring(0, flat.length-1);
16609         }else{
16610             enc = "s:" + v;
16611         }
16612         return escape(enc);        
16613     }
16614 });
16615
16616 /*
16617  * Based on:
16618  * Ext JS Library 1.1.1
16619  * Copyright(c) 2006-2007, Ext JS, LLC.
16620  *
16621  * Originally Released Under LGPL - original licence link has changed is not relivant.
16622  *
16623  * Fork - LGPL
16624  * <script type="text/javascript">
16625  */
16626 /**
16627  * @class Roo.state.Manager
16628  * This is the global state manager. By default all components that are "state aware" check this class
16629  * for state information if you don't pass them a custom state provider. In order for this class
16630  * to be useful, it must be initialized with a provider when your application initializes.
16631  <pre><code>
16632 // in your initialization function
16633 init : function(){
16634    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16635    ...
16636    // supposed you have a {@link Roo.BorderLayout}
16637    var layout = new Roo.BorderLayout(...);
16638    layout.restoreState();
16639    // or a {Roo.BasicDialog}
16640    var dialog = new Roo.BasicDialog(...);
16641    dialog.restoreState();
16642  </code></pre>
16643  * @static
16644  */
16645 Roo.state.Manager = function(){
16646     var provider = new Roo.state.Provider();
16647     
16648     return {
16649         /**
16650          * Configures the default state provider for your application
16651          * @param {Provider} stateProvider The state provider to set
16652          */
16653         setProvider : function(stateProvider){
16654             provider = stateProvider;
16655         },
16656         
16657         /**
16658          * Returns the current value for a key
16659          * @param {String} name The key name
16660          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16661          * @return {Mixed} The state data
16662          */
16663         get : function(key, defaultValue){
16664             return provider.get(key, defaultValue);
16665         },
16666         
16667         /**
16668          * Sets the value for a key
16669          * @param {String} name The key name
16670          * @param {Mixed} value The state data
16671          */
16672          set : function(key, value){
16673             provider.set(key, value);
16674         },
16675         
16676         /**
16677          * Clears a value from the state
16678          * @param {String} name The key name
16679          */
16680         clear : function(key){
16681             provider.clear(key);
16682         },
16683         
16684         /**
16685          * Gets the currently configured state provider
16686          * @return {Provider} The state provider
16687          */
16688         getProvider : function(){
16689             return provider;
16690         }
16691     };
16692 }();
16693 /*
16694  * Based on:
16695  * Ext JS Library 1.1.1
16696  * Copyright(c) 2006-2007, Ext JS, LLC.
16697  *
16698  * Originally Released Under LGPL - original licence link has changed is not relivant.
16699  *
16700  * Fork - LGPL
16701  * <script type="text/javascript">
16702  */
16703 /**
16704  * @class Roo.state.CookieProvider
16705  * @extends Roo.state.Provider
16706  * The default Provider implementation which saves state via cookies.
16707  * <br />Usage:
16708  <pre><code>
16709    var cp = new Roo.state.CookieProvider({
16710        path: "/cgi-bin/",
16711        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16712        domain: "roojs.com"
16713    })
16714    Roo.state.Manager.setProvider(cp);
16715  </code></pre>
16716  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16717  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16718  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16719  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16720  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16721  * domain the page is running on including the 'www' like 'www.roojs.com')
16722  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16723  * @constructor
16724  * Create a new CookieProvider
16725  * @param {Object} config The configuration object
16726  */
16727 Roo.state.CookieProvider = function(config){
16728     Roo.state.CookieProvider.superclass.constructor.call(this);
16729     this.path = "/";
16730     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16731     this.domain = null;
16732     this.secure = false;
16733     Roo.apply(this, config);
16734     this.state = this.readCookies();
16735 };
16736
16737 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16738     // private
16739     set : function(name, value){
16740         if(typeof value == "undefined" || value === null){
16741             this.clear(name);
16742             return;
16743         }
16744         this.setCookie(name, value);
16745         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16746     },
16747
16748     // private
16749     clear : function(name){
16750         this.clearCookie(name);
16751         Roo.state.CookieProvider.superclass.clear.call(this, name);
16752     },
16753
16754     // private
16755     readCookies : function(){
16756         var cookies = {};
16757         var c = document.cookie + ";";
16758         var re = /\s?(.*?)=(.*?);/g;
16759         var matches;
16760         while((matches = re.exec(c)) != null){
16761             var name = matches[1];
16762             var value = matches[2];
16763             if(name && name.substring(0,3) == "ys-"){
16764                 cookies[name.substr(3)] = this.decodeValue(value);
16765             }
16766         }
16767         return cookies;
16768     },
16769
16770     // private
16771     setCookie : function(name, value){
16772         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16773            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16774            ((this.path == null) ? "" : ("; path=" + this.path)) +
16775            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16776            ((this.secure == true) ? "; secure" : "");
16777     },
16778
16779     // private
16780     clearCookie : function(name){
16781         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16782            ((this.path == null) ? "" : ("; path=" + this.path)) +
16783            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16784            ((this.secure == true) ? "; secure" : "");
16785     }
16786 });/*
16787  * Based on:
16788  * Ext JS Library 1.1.1
16789  * Copyright(c) 2006-2007, Ext JS, LLC.
16790  *
16791  * Originally Released Under LGPL - original licence link has changed is not relivant.
16792  *
16793  * Fork - LGPL
16794  * <script type="text/javascript">
16795  */
16796  
16797
16798 /**
16799  * @class Roo.ComponentMgr
16800  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16801  * @static
16802  */
16803 Roo.ComponentMgr = function(){
16804     var all = new Roo.util.MixedCollection();
16805
16806     return {
16807         /**
16808          * Registers a component.
16809          * @param {Roo.Component} c The component
16810          */
16811         register : function(c){
16812             all.add(c);
16813         },
16814
16815         /**
16816          * Unregisters a component.
16817          * @param {Roo.Component} c The component
16818          */
16819         unregister : function(c){
16820             all.remove(c);
16821         },
16822
16823         /**
16824          * Returns a component by id
16825          * @param {String} id The component id
16826          */
16827         get : function(id){
16828             return all.get(id);
16829         },
16830
16831         /**
16832          * Registers a function that will be called when a specified component is added to ComponentMgr
16833          * @param {String} id The component id
16834          * @param {Funtction} fn The callback function
16835          * @param {Object} scope The scope of the callback
16836          */
16837         onAvailable : function(id, fn, scope){
16838             all.on("add", function(index, o){
16839                 if(o.id == id){
16840                     fn.call(scope || o, o);
16841                     all.un("add", fn, scope);
16842                 }
16843             });
16844         }
16845     };
16846 }();/*
16847  * Based on:
16848  * Ext JS Library 1.1.1
16849  * Copyright(c) 2006-2007, Ext JS, LLC.
16850  *
16851  * Originally Released Under LGPL - original licence link has changed is not relivant.
16852  *
16853  * Fork - LGPL
16854  * <script type="text/javascript">
16855  */
16856  
16857 /**
16858  * @class Roo.Component
16859  * @extends Roo.util.Observable
16860  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16861  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16862  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16863  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16864  * All visual components (widgets) that require rendering into a layout should subclass Component.
16865  * @constructor
16866  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16867  * 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
16868  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16869  */
16870 Roo.Component = function(config){
16871     config = config || {};
16872     if(config.tagName || config.dom || typeof config == "string"){ // element object
16873         config = {el: config, id: config.id || config};
16874     }
16875     this.initialConfig = config;
16876
16877     Roo.apply(this, config);
16878     this.addEvents({
16879         /**
16880          * @event disable
16881          * Fires after the component is disabled.
16882              * @param {Roo.Component} this
16883              */
16884         disable : true,
16885         /**
16886          * @event enable
16887          * Fires after the component is enabled.
16888              * @param {Roo.Component} this
16889              */
16890         enable : true,
16891         /**
16892          * @event beforeshow
16893          * Fires before the component is shown.  Return false to stop the show.
16894              * @param {Roo.Component} this
16895              */
16896         beforeshow : true,
16897         /**
16898          * @event show
16899          * Fires after the component is shown.
16900              * @param {Roo.Component} this
16901              */
16902         show : true,
16903         /**
16904          * @event beforehide
16905          * Fires before the component is hidden. Return false to stop the hide.
16906              * @param {Roo.Component} this
16907              */
16908         beforehide : true,
16909         /**
16910          * @event hide
16911          * Fires after the component is hidden.
16912              * @param {Roo.Component} this
16913              */
16914         hide : true,
16915         /**
16916          * @event beforerender
16917          * Fires before the component is rendered. Return false to stop the render.
16918              * @param {Roo.Component} this
16919              */
16920         beforerender : true,
16921         /**
16922          * @event render
16923          * Fires after the component is rendered.
16924              * @param {Roo.Component} this
16925              */
16926         render : true,
16927         /**
16928          * @event beforedestroy
16929          * Fires before the component is destroyed. Return false to stop the destroy.
16930              * @param {Roo.Component} this
16931              */
16932         beforedestroy : true,
16933         /**
16934          * @event destroy
16935          * Fires after the component is destroyed.
16936              * @param {Roo.Component} this
16937              */
16938         destroy : true
16939     });
16940     if(!this.id){
16941         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16942     }
16943     Roo.ComponentMgr.register(this);
16944     Roo.Component.superclass.constructor.call(this);
16945     this.initComponent();
16946     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16947         this.render(this.renderTo);
16948         delete this.renderTo;
16949     }
16950 };
16951
16952 /** @private */
16953 Roo.Component.AUTO_ID = 1000;
16954
16955 Roo.extend(Roo.Component, Roo.util.Observable, {
16956     /**
16957      * @scope Roo.Component.prototype
16958      * @type {Boolean}
16959      * true if this component is hidden. Read-only.
16960      */
16961     hidden : false,
16962     /**
16963      * @type {Boolean}
16964      * true if this component is disabled. Read-only.
16965      */
16966     disabled : false,
16967     /**
16968      * @type {Boolean}
16969      * true if this component has been rendered. Read-only.
16970      */
16971     rendered : false,
16972     
16973     /** @cfg {String} disableClass
16974      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16975      */
16976     disabledClass : "x-item-disabled",
16977         /** @cfg {Boolean} allowDomMove
16978          * Whether the component can move the Dom node when rendering (defaults to true).
16979          */
16980     allowDomMove : true,
16981     /** @cfg {String} hideMode (display|visibility)
16982      * How this component should hidden. Supported values are
16983      * "visibility" (css visibility), "offsets" (negative offset position) and
16984      * "display" (css display) - defaults to "display".
16985      */
16986     hideMode: 'display',
16987
16988     /** @private */
16989     ctype : "Roo.Component",
16990
16991     /**
16992      * @cfg {String} actionMode 
16993      * which property holds the element that used for  hide() / show() / disable() / enable()
16994      * default is 'el' for forms you probably want to set this to fieldEl 
16995      */
16996     actionMode : "el",
16997
16998          /**
16999      * @cfg {String} style
17000      * css styles to add to component
17001      * eg. text-align:right;
17002      */
17003     style : false,
17004         
17005     /** @private */
17006     getActionEl : function(){
17007         return this[this.actionMode];
17008     },
17009
17010     initComponent : Roo.emptyFn,
17011     /**
17012      * If this is a lazy rendering component, render it to its container element.
17013      * @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.
17014      */
17015     render : function(container, position){
17016         Roo.log('render');
17017         Roo.log(this.xtype);
17018         Roo.log(this.rendered);
17019         if(this.rendered){
17020             return this;
17021         }
17022         
17023         if(this.fireEvent("beforerender", this) === false){
17024             return false;
17025         }
17026         
17027         if(!container && this.el){
17028             this.el = Roo.get(this.el);
17029             container = this.el.dom.parentNode;
17030             this.allowDomMove = false;
17031         }
17032         this.container = Roo.get(container);
17033         this.rendered = true;
17034         if(position !== undefined){
17035             if(typeof position == 'number'){
17036                 position = this.container.dom.childNodes[position];
17037             }else{
17038                 position = Roo.getDom(position);
17039             }
17040         }
17041         this.onRender(this.container, position || null);
17042         if(this.cls){
17043             this.el.addClass(this.cls);
17044             delete this.cls;
17045         }
17046         if(this.style){
17047             this.el.applyStyles(this.style);
17048             delete this.style;
17049         }
17050         this.fireEvent("render", this);
17051         this.afterRender(this.container);
17052         if(this.hidden){
17053             this.hide();
17054         }
17055         if(this.disabled){
17056             this.disable();
17057         }
17058
17059         return this;
17060         
17061     },
17062
17063     /** @private */
17064     // default function is not really useful
17065     onRender : function(ct, position){
17066         if(this.el){
17067             this.el = Roo.get(this.el);
17068             if(this.allowDomMove !== false){
17069                 ct.dom.insertBefore(this.el.dom, position);
17070             }
17071         }
17072     },
17073
17074     /** @private */
17075     getAutoCreate : function(){
17076         var cfg = typeof this.autoCreate == "object" ?
17077                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
17078         if(this.id && !cfg.id){
17079             cfg.id = this.id;
17080         }
17081         return cfg;
17082     },
17083
17084     /** @private */
17085     afterRender : Roo.emptyFn,
17086
17087     /**
17088      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
17089      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
17090      */
17091     destroy : function(){
17092         if(this.fireEvent("beforedestroy", this) !== false){
17093             this.purgeListeners();
17094             this.beforeDestroy();
17095             if(this.rendered){
17096                 this.el.removeAllListeners();
17097                 this.el.remove();
17098                 if(this.actionMode == "container"){
17099                     this.container.remove();
17100                 }
17101             }
17102             this.onDestroy();
17103             Roo.ComponentMgr.unregister(this);
17104             this.fireEvent("destroy", this);
17105         }
17106     },
17107
17108         /** @private */
17109     beforeDestroy : function(){
17110
17111     },
17112
17113         /** @private */
17114         onDestroy : function(){
17115
17116     },
17117
17118     /**
17119      * Returns the underlying {@link Roo.Element}.
17120      * @return {Roo.Element} The element
17121      */
17122     getEl : function(){
17123         return this.el;
17124     },
17125
17126     /**
17127      * Returns the id of this component.
17128      * @return {String}
17129      */
17130     getId : function(){
17131         return this.id;
17132     },
17133
17134     /**
17135      * Try to focus this component.
17136      * @param {Boolean} selectText True to also select the text in this component (if applicable)
17137      * @return {Roo.Component} this
17138      */
17139     focus : function(selectText){
17140         if(this.rendered){
17141             this.el.focus();
17142             if(selectText === true){
17143                 this.el.dom.select();
17144             }
17145         }
17146         return this;
17147     },
17148
17149     /** @private */
17150     blur : function(){
17151         if(this.rendered){
17152             this.el.blur();
17153         }
17154         return this;
17155     },
17156
17157     /**
17158      * Disable this component.
17159      * @return {Roo.Component} this
17160      */
17161     disable : function(){
17162         if(this.rendered){
17163             this.onDisable();
17164         }
17165         this.disabled = true;
17166         this.fireEvent("disable", this);
17167         return this;
17168     },
17169
17170         // private
17171     onDisable : function(){
17172         this.getActionEl().addClass(this.disabledClass);
17173         this.el.dom.disabled = true;
17174     },
17175
17176     /**
17177      * Enable this component.
17178      * @return {Roo.Component} this
17179      */
17180     enable : function(){
17181         if(this.rendered){
17182             this.onEnable();
17183         }
17184         this.disabled = false;
17185         this.fireEvent("enable", this);
17186         return this;
17187     },
17188
17189         // private
17190     onEnable : function(){
17191         this.getActionEl().removeClass(this.disabledClass);
17192         this.el.dom.disabled = false;
17193     },
17194
17195     /**
17196      * Convenience function for setting disabled/enabled by boolean.
17197      * @param {Boolean} disabled
17198      */
17199     setDisabled : function(disabled){
17200         this[disabled ? "disable" : "enable"]();
17201     },
17202
17203     /**
17204      * Show this component.
17205      * @return {Roo.Component} this
17206      */
17207     show: function(){
17208         if(this.fireEvent("beforeshow", this) !== false){
17209             this.hidden = false;
17210             if(this.rendered){
17211                 this.onShow();
17212             }
17213             this.fireEvent("show", this);
17214         }
17215         return this;
17216     },
17217
17218     // private
17219     onShow : function(){
17220         var ae = this.getActionEl();
17221         if(this.hideMode == 'visibility'){
17222             ae.dom.style.visibility = "visible";
17223         }else if(this.hideMode == 'offsets'){
17224             ae.removeClass('x-hidden');
17225         }else{
17226             ae.dom.style.display = "";
17227         }
17228     },
17229
17230     /**
17231      * Hide this component.
17232      * @return {Roo.Component} this
17233      */
17234     hide: function(){
17235         if(this.fireEvent("beforehide", this) !== false){
17236             this.hidden = true;
17237             if(this.rendered){
17238                 this.onHide();
17239             }
17240             this.fireEvent("hide", this);
17241         }
17242         return this;
17243     },
17244
17245     // private
17246     onHide : function(){
17247         var ae = this.getActionEl();
17248         if(this.hideMode == 'visibility'){
17249             ae.dom.style.visibility = "hidden";
17250         }else if(this.hideMode == 'offsets'){
17251             ae.addClass('x-hidden');
17252         }else{
17253             ae.dom.style.display = "none";
17254         }
17255     },
17256
17257     /**
17258      * Convenience function to hide or show this component by boolean.
17259      * @param {Boolean} visible True to show, false to hide
17260      * @return {Roo.Component} this
17261      */
17262     setVisible: function(visible){
17263         if(visible) {
17264             this.show();
17265         }else{
17266             this.hide();
17267         }
17268         return this;
17269     },
17270
17271     /**
17272      * Returns true if this component is visible.
17273      */
17274     isVisible : function(){
17275         return this.getActionEl().isVisible();
17276     },
17277
17278     cloneConfig : function(overrides){
17279         overrides = overrides || {};
17280         var id = overrides.id || Roo.id();
17281         var cfg = Roo.applyIf(overrides, this.initialConfig);
17282         cfg.id = id; // prevent dup id
17283         return new this.constructor(cfg);
17284     }
17285 });/*
17286  * Based on:
17287  * Ext JS Library 1.1.1
17288  * Copyright(c) 2006-2007, Ext JS, LLC.
17289  *
17290  * Originally Released Under LGPL - original licence link has changed is not relivant.
17291  *
17292  * Fork - LGPL
17293  * <script type="text/javascript">
17294  */
17295
17296 /**
17297  * @class Roo.BoxComponent
17298  * @extends Roo.Component
17299  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17300  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17301  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17302  * layout containers.
17303  * @constructor
17304  * @param {Roo.Element/String/Object} config The configuration options.
17305  */
17306 Roo.BoxComponent = function(config){
17307     Roo.Component.call(this, config);
17308     this.addEvents({
17309         /**
17310          * @event resize
17311          * Fires after the component is resized.
17312              * @param {Roo.Component} this
17313              * @param {Number} adjWidth The box-adjusted width that was set
17314              * @param {Number} adjHeight The box-adjusted height that was set
17315              * @param {Number} rawWidth The width that was originally specified
17316              * @param {Number} rawHeight The height that was originally specified
17317              */
17318         resize : true,
17319         /**
17320          * @event move
17321          * Fires after the component is moved.
17322              * @param {Roo.Component} this
17323              * @param {Number} x The new x position
17324              * @param {Number} y The new y position
17325              */
17326         move : true
17327     });
17328 };
17329
17330 Roo.extend(Roo.BoxComponent, Roo.Component, {
17331     // private, set in afterRender to signify that the component has been rendered
17332     boxReady : false,
17333     // private, used to defer height settings to subclasses
17334     deferHeight: false,
17335     /** @cfg {Number} width
17336      * width (optional) size of component
17337      */
17338      /** @cfg {Number} height
17339      * height (optional) size of component
17340      */
17341      
17342     /**
17343      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17344      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17345      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17346      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17347      * @return {Roo.BoxComponent} this
17348      */
17349     setSize : function(w, h){
17350         // support for standard size objects
17351         if(typeof w == 'object'){
17352             h = w.height;
17353             w = w.width;
17354         }
17355         // not rendered
17356         if(!this.boxReady){
17357             this.width = w;
17358             this.height = h;
17359             return this;
17360         }
17361
17362         // prevent recalcs when not needed
17363         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17364             return this;
17365         }
17366         this.lastSize = {width: w, height: h};
17367
17368         var adj = this.adjustSize(w, h);
17369         var aw = adj.width, ah = adj.height;
17370         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17371             var rz = this.getResizeEl();
17372             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17373                 rz.setSize(aw, ah);
17374             }else if(!this.deferHeight && ah !== undefined){
17375                 rz.setHeight(ah);
17376             }else if(aw !== undefined){
17377                 rz.setWidth(aw);
17378             }
17379             this.onResize(aw, ah, w, h);
17380             this.fireEvent('resize', this, aw, ah, w, h);
17381         }
17382         return this;
17383     },
17384
17385     /**
17386      * Gets the current size of the component's underlying element.
17387      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17388      */
17389     getSize : function(){
17390         return this.el.getSize();
17391     },
17392
17393     /**
17394      * Gets the current XY position of the component's underlying element.
17395      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17396      * @return {Array} The XY position of the element (e.g., [100, 200])
17397      */
17398     getPosition : function(local){
17399         if(local === true){
17400             return [this.el.getLeft(true), this.el.getTop(true)];
17401         }
17402         return this.xy || this.el.getXY();
17403     },
17404
17405     /**
17406      * Gets the current box measurements of the component's underlying element.
17407      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17408      * @returns {Object} box An object in the format {x, y, width, height}
17409      */
17410     getBox : function(local){
17411         var s = this.el.getSize();
17412         if(local){
17413             s.x = this.el.getLeft(true);
17414             s.y = this.el.getTop(true);
17415         }else{
17416             var xy = this.xy || this.el.getXY();
17417             s.x = xy[0];
17418             s.y = xy[1];
17419         }
17420         return s;
17421     },
17422
17423     /**
17424      * Sets the current box measurements of the component's underlying element.
17425      * @param {Object} box An object in the format {x, y, width, height}
17426      * @returns {Roo.BoxComponent} this
17427      */
17428     updateBox : function(box){
17429         this.setSize(box.width, box.height);
17430         this.setPagePosition(box.x, box.y);
17431         return this;
17432     },
17433
17434     // protected
17435     getResizeEl : function(){
17436         return this.resizeEl || this.el;
17437     },
17438
17439     // protected
17440     getPositionEl : function(){
17441         return this.positionEl || this.el;
17442     },
17443
17444     /**
17445      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17446      * This method fires the move event.
17447      * @param {Number} left The new left
17448      * @param {Number} top The new top
17449      * @returns {Roo.BoxComponent} this
17450      */
17451     setPosition : function(x, y){
17452         this.x = x;
17453         this.y = y;
17454         if(!this.boxReady){
17455             return this;
17456         }
17457         var adj = this.adjustPosition(x, y);
17458         var ax = adj.x, ay = adj.y;
17459
17460         var el = this.getPositionEl();
17461         if(ax !== undefined || ay !== undefined){
17462             if(ax !== undefined && ay !== undefined){
17463                 el.setLeftTop(ax, ay);
17464             }else if(ax !== undefined){
17465                 el.setLeft(ax);
17466             }else if(ay !== undefined){
17467                 el.setTop(ay);
17468             }
17469             this.onPosition(ax, ay);
17470             this.fireEvent('move', this, ax, ay);
17471         }
17472         return this;
17473     },
17474
17475     /**
17476      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17477      * This method fires the move event.
17478      * @param {Number} x The new x position
17479      * @param {Number} y The new y position
17480      * @returns {Roo.BoxComponent} this
17481      */
17482     setPagePosition : function(x, y){
17483         this.pageX = x;
17484         this.pageY = y;
17485         if(!this.boxReady){
17486             return;
17487         }
17488         if(x === undefined || y === undefined){ // cannot translate undefined points
17489             return;
17490         }
17491         var p = this.el.translatePoints(x, y);
17492         this.setPosition(p.left, p.top);
17493         return this;
17494     },
17495
17496     // private
17497     onRender : function(ct, position){
17498         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17499         if(this.resizeEl){
17500             this.resizeEl = Roo.get(this.resizeEl);
17501         }
17502         if(this.positionEl){
17503             this.positionEl = Roo.get(this.positionEl);
17504         }
17505     },
17506
17507     // private
17508     afterRender : function(){
17509         Roo.BoxComponent.superclass.afterRender.call(this);
17510         this.boxReady = true;
17511         this.setSize(this.width, this.height);
17512         if(this.x || this.y){
17513             this.setPosition(this.x, this.y);
17514         }
17515         if(this.pageX || this.pageY){
17516             this.setPagePosition(this.pageX, this.pageY);
17517         }
17518     },
17519
17520     /**
17521      * Force the component's size to recalculate based on the underlying element's current height and width.
17522      * @returns {Roo.BoxComponent} this
17523      */
17524     syncSize : function(){
17525         delete this.lastSize;
17526         this.setSize(this.el.getWidth(), this.el.getHeight());
17527         return this;
17528     },
17529
17530     /**
17531      * Called after the component is resized, this method is empty by default but can be implemented by any
17532      * subclass that needs to perform custom logic after a resize occurs.
17533      * @param {Number} adjWidth The box-adjusted width that was set
17534      * @param {Number} adjHeight The box-adjusted height that was set
17535      * @param {Number} rawWidth The width that was originally specified
17536      * @param {Number} rawHeight The height that was originally specified
17537      */
17538     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17539
17540     },
17541
17542     /**
17543      * Called after the component is moved, this method is empty by default but can be implemented by any
17544      * subclass that needs to perform custom logic after a move occurs.
17545      * @param {Number} x The new x position
17546      * @param {Number} y The new y position
17547      */
17548     onPosition : function(x, y){
17549
17550     },
17551
17552     // private
17553     adjustSize : function(w, h){
17554         if(this.autoWidth){
17555             w = 'auto';
17556         }
17557         if(this.autoHeight){
17558             h = 'auto';
17559         }
17560         return {width : w, height: h};
17561     },
17562
17563     // private
17564     adjustPosition : function(x, y){
17565         return {x : x, y: y};
17566     }
17567 });/*
17568  * Based on:
17569  * Ext JS Library 1.1.1
17570  * Copyright(c) 2006-2007, Ext JS, LLC.
17571  *
17572  * Originally Released Under LGPL - original licence link has changed is not relivant.
17573  *
17574  * Fork - LGPL
17575  * <script type="text/javascript">
17576  */
17577  (function(){ 
17578 /**
17579  * @class Roo.Layer
17580  * @extends Roo.Element
17581  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17582  * automatic maintaining of shadow/shim positions.
17583  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17584  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17585  * you can pass a string with a CSS class name. False turns off the shadow.
17586  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17587  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17588  * @cfg {String} cls CSS class to add to the element
17589  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17590  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17591  * @constructor
17592  * @param {Object} config An object with config options.
17593  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17594  */
17595
17596 Roo.Layer = function(config, existingEl){
17597     config = config || {};
17598     var dh = Roo.DomHelper;
17599     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17600     if(existingEl){
17601         this.dom = Roo.getDom(existingEl);
17602     }
17603     if(!this.dom){
17604         var o = config.dh || {tag: "div", cls: "x-layer"};
17605         this.dom = dh.append(pel, o);
17606     }
17607     if(config.cls){
17608         this.addClass(config.cls);
17609     }
17610     this.constrain = config.constrain !== false;
17611     this.visibilityMode = Roo.Element.VISIBILITY;
17612     if(config.id){
17613         this.id = this.dom.id = config.id;
17614     }else{
17615         this.id = Roo.id(this.dom);
17616     }
17617     this.zindex = config.zindex || this.getZIndex();
17618     this.position("absolute", this.zindex);
17619     if(config.shadow){
17620         this.shadowOffset = config.shadowOffset || 4;
17621         this.shadow = new Roo.Shadow({
17622             offset : this.shadowOffset,
17623             mode : config.shadow
17624         });
17625     }else{
17626         this.shadowOffset = 0;
17627     }
17628     this.useShim = config.shim !== false && Roo.useShims;
17629     this.useDisplay = config.useDisplay;
17630     this.hide();
17631 };
17632
17633 var supr = Roo.Element.prototype;
17634
17635 // shims are shared among layer to keep from having 100 iframes
17636 var shims = [];
17637
17638 Roo.extend(Roo.Layer, Roo.Element, {
17639
17640     getZIndex : function(){
17641         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17642     },
17643
17644     getShim : function(){
17645         if(!this.useShim){
17646             return null;
17647         }
17648         if(this.shim){
17649             return this.shim;
17650         }
17651         var shim = shims.shift();
17652         if(!shim){
17653             shim = this.createShim();
17654             shim.enableDisplayMode('block');
17655             shim.dom.style.display = 'none';
17656             shim.dom.style.visibility = 'visible';
17657         }
17658         var pn = this.dom.parentNode;
17659         if(shim.dom.parentNode != pn){
17660             pn.insertBefore(shim.dom, this.dom);
17661         }
17662         shim.setStyle('z-index', this.getZIndex()-2);
17663         this.shim = shim;
17664         return shim;
17665     },
17666
17667     hideShim : function(){
17668         if(this.shim){
17669             this.shim.setDisplayed(false);
17670             shims.push(this.shim);
17671             delete this.shim;
17672         }
17673     },
17674
17675     disableShadow : function(){
17676         if(this.shadow){
17677             this.shadowDisabled = true;
17678             this.shadow.hide();
17679             this.lastShadowOffset = this.shadowOffset;
17680             this.shadowOffset = 0;
17681         }
17682     },
17683
17684     enableShadow : function(show){
17685         if(this.shadow){
17686             this.shadowDisabled = false;
17687             this.shadowOffset = this.lastShadowOffset;
17688             delete this.lastShadowOffset;
17689             if(show){
17690                 this.sync(true);
17691             }
17692         }
17693     },
17694
17695     // private
17696     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17697     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17698     sync : function(doShow){
17699         var sw = this.shadow;
17700         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17701             var sh = this.getShim();
17702
17703             var w = this.getWidth(),
17704                 h = this.getHeight();
17705
17706             var l = this.getLeft(true),
17707                 t = this.getTop(true);
17708
17709             if(sw && !this.shadowDisabled){
17710                 if(doShow && !sw.isVisible()){
17711                     sw.show(this);
17712                 }else{
17713                     sw.realign(l, t, w, h);
17714                 }
17715                 if(sh){
17716                     if(doShow){
17717                        sh.show();
17718                     }
17719                     // fit the shim behind the shadow, so it is shimmed too
17720                     var a = sw.adjusts, s = sh.dom.style;
17721                     s.left = (Math.min(l, l+a.l))+"px";
17722                     s.top = (Math.min(t, t+a.t))+"px";
17723                     s.width = (w+a.w)+"px";
17724                     s.height = (h+a.h)+"px";
17725                 }
17726             }else if(sh){
17727                 if(doShow){
17728                    sh.show();
17729                 }
17730                 sh.setSize(w, h);
17731                 sh.setLeftTop(l, t);
17732             }
17733             
17734         }
17735     },
17736
17737     // private
17738     destroy : function(){
17739         this.hideShim();
17740         if(this.shadow){
17741             this.shadow.hide();
17742         }
17743         this.removeAllListeners();
17744         var pn = this.dom.parentNode;
17745         if(pn){
17746             pn.removeChild(this.dom);
17747         }
17748         Roo.Element.uncache(this.id);
17749     },
17750
17751     remove : function(){
17752         this.destroy();
17753     },
17754
17755     // private
17756     beginUpdate : function(){
17757         this.updating = true;
17758     },
17759
17760     // private
17761     endUpdate : function(){
17762         this.updating = false;
17763         this.sync(true);
17764     },
17765
17766     // private
17767     hideUnders : function(negOffset){
17768         if(this.shadow){
17769             this.shadow.hide();
17770         }
17771         this.hideShim();
17772     },
17773
17774     // private
17775     constrainXY : function(){
17776         if(this.constrain){
17777             var vw = Roo.lib.Dom.getViewWidth(),
17778                 vh = Roo.lib.Dom.getViewHeight();
17779             var s = Roo.get(document).getScroll();
17780
17781             var xy = this.getXY();
17782             var x = xy[0], y = xy[1];   
17783             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17784             // only move it if it needs it
17785             var moved = false;
17786             // first validate right/bottom
17787             if((x + w) > vw+s.left){
17788                 x = vw - w - this.shadowOffset;
17789                 moved = true;
17790             }
17791             if((y + h) > vh+s.top){
17792                 y = vh - h - this.shadowOffset;
17793                 moved = true;
17794             }
17795             // then make sure top/left isn't negative
17796             if(x < s.left){
17797                 x = s.left;
17798                 moved = true;
17799             }
17800             if(y < s.top){
17801                 y = s.top;
17802                 moved = true;
17803             }
17804             if(moved){
17805                 if(this.avoidY){
17806                     var ay = this.avoidY;
17807                     if(y <= ay && (y+h) >= ay){
17808                         y = ay-h-5;   
17809                     }
17810                 }
17811                 xy = [x, y];
17812                 this.storeXY(xy);
17813                 supr.setXY.call(this, xy);
17814                 this.sync();
17815             }
17816         }
17817     },
17818
17819     isVisible : function(){
17820         return this.visible;    
17821     },
17822
17823     // private
17824     showAction : function(){
17825         this.visible = true; // track visibility to prevent getStyle calls
17826         if(this.useDisplay === true){
17827             this.setDisplayed("");
17828         }else if(this.lastXY){
17829             supr.setXY.call(this, this.lastXY);
17830         }else if(this.lastLT){
17831             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17832         }
17833     },
17834
17835     // private
17836     hideAction : function(){
17837         this.visible = false;
17838         if(this.useDisplay === true){
17839             this.setDisplayed(false);
17840         }else{
17841             this.setLeftTop(-10000,-10000);
17842         }
17843     },
17844
17845     // overridden Element method
17846     setVisible : function(v, a, d, c, e){
17847         if(v){
17848             this.showAction();
17849         }
17850         if(a && v){
17851             var cb = function(){
17852                 this.sync(true);
17853                 if(c){
17854                     c();
17855                 }
17856             }.createDelegate(this);
17857             supr.setVisible.call(this, true, true, d, cb, e);
17858         }else{
17859             if(!v){
17860                 this.hideUnders(true);
17861             }
17862             var cb = c;
17863             if(a){
17864                 cb = function(){
17865                     this.hideAction();
17866                     if(c){
17867                         c();
17868                     }
17869                 }.createDelegate(this);
17870             }
17871             supr.setVisible.call(this, v, a, d, cb, e);
17872             if(v){
17873                 this.sync(true);
17874             }else if(!a){
17875                 this.hideAction();
17876             }
17877         }
17878     },
17879
17880     storeXY : function(xy){
17881         delete this.lastLT;
17882         this.lastXY = xy;
17883     },
17884
17885     storeLeftTop : function(left, top){
17886         delete this.lastXY;
17887         this.lastLT = [left, top];
17888     },
17889
17890     // private
17891     beforeFx : function(){
17892         this.beforeAction();
17893         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17894     },
17895
17896     // private
17897     afterFx : function(){
17898         Roo.Layer.superclass.afterFx.apply(this, arguments);
17899         this.sync(this.isVisible());
17900     },
17901
17902     // private
17903     beforeAction : function(){
17904         if(!this.updating && this.shadow){
17905             this.shadow.hide();
17906         }
17907     },
17908
17909     // overridden Element method
17910     setLeft : function(left){
17911         this.storeLeftTop(left, this.getTop(true));
17912         supr.setLeft.apply(this, arguments);
17913         this.sync();
17914     },
17915
17916     setTop : function(top){
17917         this.storeLeftTop(this.getLeft(true), top);
17918         supr.setTop.apply(this, arguments);
17919         this.sync();
17920     },
17921
17922     setLeftTop : function(left, top){
17923         this.storeLeftTop(left, top);
17924         supr.setLeftTop.apply(this, arguments);
17925         this.sync();
17926     },
17927
17928     setXY : function(xy, a, d, c, e){
17929         this.fixDisplay();
17930         this.beforeAction();
17931         this.storeXY(xy);
17932         var cb = this.createCB(c);
17933         supr.setXY.call(this, xy, a, d, cb, e);
17934         if(!a){
17935             cb();
17936         }
17937     },
17938
17939     // private
17940     createCB : function(c){
17941         var el = this;
17942         return function(){
17943             el.constrainXY();
17944             el.sync(true);
17945             if(c){
17946                 c();
17947             }
17948         };
17949     },
17950
17951     // overridden Element method
17952     setX : function(x, a, d, c, e){
17953         this.setXY([x, this.getY()], a, d, c, e);
17954     },
17955
17956     // overridden Element method
17957     setY : function(y, a, d, c, e){
17958         this.setXY([this.getX(), y], a, d, c, e);
17959     },
17960
17961     // overridden Element method
17962     setSize : function(w, h, a, d, c, e){
17963         this.beforeAction();
17964         var cb = this.createCB(c);
17965         supr.setSize.call(this, w, h, a, d, cb, e);
17966         if(!a){
17967             cb();
17968         }
17969     },
17970
17971     // overridden Element method
17972     setWidth : function(w, a, d, c, e){
17973         this.beforeAction();
17974         var cb = this.createCB(c);
17975         supr.setWidth.call(this, w, a, d, cb, e);
17976         if(!a){
17977             cb();
17978         }
17979     },
17980
17981     // overridden Element method
17982     setHeight : function(h, a, d, c, e){
17983         this.beforeAction();
17984         var cb = this.createCB(c);
17985         supr.setHeight.call(this, h, a, d, cb, e);
17986         if(!a){
17987             cb();
17988         }
17989     },
17990
17991     // overridden Element method
17992     setBounds : function(x, y, w, h, a, d, c, e){
17993         this.beforeAction();
17994         var cb = this.createCB(c);
17995         if(!a){
17996             this.storeXY([x, y]);
17997             supr.setXY.call(this, [x, y]);
17998             supr.setSize.call(this, w, h, a, d, cb, e);
17999             cb();
18000         }else{
18001             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
18002         }
18003         return this;
18004     },
18005     
18006     /**
18007      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
18008      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
18009      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
18010      * @param {Number} zindex The new z-index to set
18011      * @return {this} The Layer
18012      */
18013     setZIndex : function(zindex){
18014         this.zindex = zindex;
18015         this.setStyle("z-index", zindex + 2);
18016         if(this.shadow){
18017             this.shadow.setZIndex(zindex + 1);
18018         }
18019         if(this.shim){
18020             this.shim.setStyle("z-index", zindex);
18021         }
18022     }
18023 });
18024 })();/*
18025  * Original code for Roojs - LGPL
18026  * <script type="text/javascript">
18027  */
18028  
18029 /**
18030  * @class Roo.XComponent
18031  * A delayed Element creator...
18032  * Or a way to group chunks of interface together.
18033  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
18034  *  used in conjunction with XComponent.build() it will create an instance of each element,
18035  *  then call addxtype() to build the User interface.
18036  * 
18037  * Mypart.xyx = new Roo.XComponent({
18038
18039     parent : 'Mypart.xyz', // empty == document.element.!!
18040     order : '001',
18041     name : 'xxxx'
18042     region : 'xxxx'
18043     disabled : function() {} 
18044      
18045     tree : function() { // return an tree of xtype declared components
18046         var MODULE = this;
18047         return 
18048         {
18049             xtype : 'NestedLayoutPanel',
18050             // technicall
18051         }
18052      ]
18053  *})
18054  *
18055  *
18056  * It can be used to build a big heiracy, with parent etc.
18057  * or you can just use this to render a single compoent to a dom element
18058  * MYPART.render(Roo.Element | String(id) | dom_element )
18059  *
18060  *
18061  * Usage patterns.
18062  *
18063  * Classic Roo
18064  *
18065  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
18066  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
18067  *
18068  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
18069  *
18070  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
18071  * - if mulitple topModules exist, the last one is defined as the top module.
18072  *
18073  * Embeded Roo
18074  * 
18075  * When the top level or multiple modules are to embedded into a existing HTML page,
18076  * the parent element can container '#id' of the element where the module will be drawn.
18077  *
18078  * Bootstrap Roo
18079  *
18080  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
18081  * it relies more on a include mechanism, where sub modules are included into an outer page.
18082  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
18083  * 
18084  * Bootstrap Roo Included elements
18085  *
18086  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
18087  * hence confusing the component builder as it thinks there are multiple top level elements. 
18088  *
18089  * String Over-ride & Translations
18090  *
18091  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
18092  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
18093  * are needed. @see Roo.XComponent.overlayString  
18094  * 
18095  * 
18096  * 
18097  * @extends Roo.util.Observable
18098  * @constructor
18099  * @param cfg {Object} configuration of component
18100  * 
18101  */
18102 Roo.XComponent = function(cfg) {
18103     Roo.apply(this, cfg);
18104     this.addEvents({ 
18105         /**
18106              * @event built
18107              * Fires when this the componnt is built
18108              * @param {Roo.XComponent} c the component
18109              */
18110         'built' : true
18111         
18112     });
18113     this.region = this.region || 'center'; // default..
18114     Roo.XComponent.register(this);
18115     this.modules = false;
18116     this.el = false; // where the layout goes..
18117     
18118     
18119 }
18120 Roo.extend(Roo.XComponent, Roo.util.Observable, {
18121     /**
18122      * @property el
18123      * The created element (with Roo.factory())
18124      * @type {Roo.Layout}
18125      */
18126     el  : false,
18127     
18128     /**
18129      * @property el
18130      * for BC  - use el in new code
18131      * @type {Roo.Layout}
18132      */
18133     panel : false,
18134     
18135     /**
18136      * @property layout
18137      * for BC  - use el in new code
18138      * @type {Roo.Layout}
18139      */
18140     layout : false,
18141     
18142      /**
18143      * @cfg {Function|boolean} disabled
18144      * If this module is disabled by some rule, return true from the funtion
18145      */
18146     disabled : false,
18147     
18148     /**
18149      * @cfg {String} parent 
18150      * Name of parent element which it get xtype added to..
18151      */
18152     parent: false,
18153     
18154     /**
18155      * @cfg {String} order
18156      * Used to set the order in which elements are created (usefull for multiple tabs)
18157      */
18158     
18159     order : false,
18160     /**
18161      * @cfg {String} name
18162      * String to display while loading.
18163      */
18164     name : false,
18165     /**
18166      * @cfg {String} region
18167      * Region to render component to (defaults to center)
18168      */
18169     region : 'center',
18170     
18171     /**
18172      * @cfg {Array} items
18173      * A single item array - the first element is the root of the tree..
18174      * It's done this way to stay compatible with the Xtype system...
18175      */
18176     items : false,
18177     
18178     /**
18179      * @property _tree
18180      * The method that retuns the tree of parts that make up this compoennt 
18181      * @type {function}
18182      */
18183     _tree  : false,
18184     
18185      /**
18186      * render
18187      * render element to dom or tree
18188      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
18189      */
18190     
18191     render : function(el)
18192     {
18193         
18194         el = el || false;
18195         var hp = this.parent ? 1 : 0;
18196         Roo.debug &&  Roo.log(this);
18197         
18198         var tree = this._tree ? this._tree() : this.tree();
18199
18200         
18201         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18202             // if parent is a '#.....' string, then let's use that..
18203             var ename = this.parent.substr(1);
18204             this.parent = false;
18205             Roo.debug && Roo.log(ename);
18206             switch (ename) {
18207                 case 'bootstrap-body':
18208                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
18209                         // this is the BorderLayout standard?
18210                        this.parent = { el : true };
18211                        break;
18212                     }
18213                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
18214                         // need to insert stuff...
18215                         this.parent =  {
18216                              el : new Roo.bootstrap.layout.Border({
18217                                  el : document.body, 
18218                      
18219                                  center: {
18220                                     titlebar: false,
18221                                     autoScroll:false,
18222                                     closeOnTab: true,
18223                                     tabPosition: 'top',
18224                                       //resizeTabs: true,
18225                                     alwaysShowTabs: true,
18226                                     hideTabs: false
18227                                      //minTabWidth: 140
18228                                  }
18229                              })
18230                         
18231                          };
18232                          break;
18233                     }
18234                          
18235                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18236                         this.parent = { el :  new  Roo.bootstrap.Body() };
18237                         Roo.debug && Roo.log("setting el to doc body");
18238                          
18239                     } else {
18240                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18241                     }
18242                     break;
18243                 case 'bootstrap':
18244                     this.parent = { el : true};
18245                     // fall through
18246                 default:
18247                     el = Roo.get(ename);
18248                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18249                         this.parent = { el : true};
18250                     }
18251                     
18252                     break;
18253             }
18254                 
18255             
18256             if (!el && !this.parent) {
18257                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18258                 return;
18259             }
18260         }
18261         
18262         Roo.debug && Roo.log("EL:");
18263         Roo.debug && Roo.log(el);
18264         Roo.debug && Roo.log("this.parent.el:");
18265         Roo.debug && Roo.log(this.parent.el);
18266         
18267
18268         // altertive root elements ??? - we need a better way to indicate these.
18269         var is_alt = Roo.XComponent.is_alt ||
18270                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18271                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18272                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18273         
18274         
18275         
18276         if (!this.parent && is_alt) {
18277             //el = Roo.get(document.body);
18278             this.parent = { el : true };
18279         }
18280             
18281             
18282         
18283         if (!this.parent) {
18284             
18285             Roo.debug && Roo.log("no parent - creating one");
18286             
18287             el = el ? Roo.get(el) : false;      
18288             
18289             if (typeof(Roo.BorderLayout) == 'undefined' ) {
18290                 
18291                 this.parent =  {
18292                     el : new Roo.bootstrap.layout.Border({
18293                         el: el || document.body,
18294                     
18295                         center: {
18296                             titlebar: false,
18297                             autoScroll:false,
18298                             closeOnTab: true,
18299                             tabPosition: 'top',
18300                              //resizeTabs: true,
18301                             alwaysShowTabs: false,
18302                             hideTabs: true,
18303                             minTabWidth: 140,
18304                             overflow: 'visible'
18305                          }
18306                      })
18307                 };
18308             } else {
18309             
18310                 // it's a top level one..
18311                 this.parent =  {
18312                     el : new Roo.BorderLayout(el || document.body, {
18313                         center: {
18314                             titlebar: false,
18315                             autoScroll:false,
18316                             closeOnTab: true,
18317                             tabPosition: 'top',
18318                              //resizeTabs: true,
18319                             alwaysShowTabs: el && hp? false :  true,
18320                             hideTabs: el || !hp ? true :  false,
18321                             minTabWidth: 140
18322                          }
18323                     })
18324                 };
18325             }
18326         }
18327         
18328         if (!this.parent.el) {
18329                 // probably an old style ctor, which has been disabled.
18330                 return;
18331
18332         }
18333                 // The 'tree' method is  '_tree now' 
18334             
18335         tree.region = tree.region || this.region;
18336         var is_body = false;
18337         if (this.parent.el === true) {
18338             // bootstrap... - body..
18339             if (el) {
18340                 tree.el = el;
18341             }
18342             this.parent.el = Roo.factory(tree);
18343             is_body = true;
18344         }
18345         
18346         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18347         this.fireEvent('built', this);
18348         
18349         this.panel = this.el;
18350         this.layout = this.panel.layout;
18351         this.parentLayout = this.parent.layout  || false;  
18352          
18353     }
18354     
18355 });
18356
18357 Roo.apply(Roo.XComponent, {
18358     /**
18359      * @property  hideProgress
18360      * true to disable the building progress bar.. usefull on single page renders.
18361      * @type Boolean
18362      */
18363     hideProgress : false,
18364     /**
18365      * @property  buildCompleted
18366      * True when the builder has completed building the interface.
18367      * @type Boolean
18368      */
18369     buildCompleted : false,
18370      
18371     /**
18372      * @property  topModule
18373      * the upper most module - uses document.element as it's constructor.
18374      * @type Object
18375      */
18376      
18377     topModule  : false,
18378       
18379     /**
18380      * @property  modules
18381      * array of modules to be created by registration system.
18382      * @type {Array} of Roo.XComponent
18383      */
18384     
18385     modules : [],
18386     /**
18387      * @property  elmodules
18388      * array of modules to be created by which use #ID 
18389      * @type {Array} of Roo.XComponent
18390      */
18391      
18392     elmodules : [],
18393
18394      /**
18395      * @property  is_alt
18396      * Is an alternative Root - normally used by bootstrap or other systems,
18397      *    where the top element in the tree can wrap 'body' 
18398      * @type {boolean}  (default false)
18399      */
18400      
18401     is_alt : false,
18402     /**
18403      * @property  build_from_html
18404      * Build elements from html - used by bootstrap HTML stuff 
18405      *    - this is cleared after build is completed
18406      * @type {boolean}    (default false)
18407      */
18408      
18409     build_from_html : false,
18410     /**
18411      * Register components to be built later.
18412      *
18413      * This solves the following issues
18414      * - Building is not done on page load, but after an authentication process has occured.
18415      * - Interface elements are registered on page load
18416      * - Parent Interface elements may not be loaded before child, so this handles that..
18417      * 
18418      *
18419      * example:
18420      * 
18421      * MyApp.register({
18422           order : '000001',
18423           module : 'Pman.Tab.projectMgr',
18424           region : 'center',
18425           parent : 'Pman.layout',
18426           disabled : false,  // or use a function..
18427         })
18428      
18429      * * @param {Object} details about module
18430      */
18431     register : function(obj) {
18432                 
18433         Roo.XComponent.event.fireEvent('register', obj);
18434         switch(typeof(obj.disabled) ) {
18435                 
18436             case 'undefined':
18437                 break;
18438             
18439             case 'function':
18440                 if ( obj.disabled() ) {
18441                         return;
18442                 }
18443                 break;
18444             
18445             default:
18446                 if (obj.disabled || obj.region == '#disabled') {
18447                         return;
18448                 }
18449                 break;
18450         }
18451                 
18452         this.modules.push(obj);
18453          
18454     },
18455     /**
18456      * convert a string to an object..
18457      * eg. 'AAA.BBB' -> finds AAA.BBB
18458
18459      */
18460     
18461     toObject : function(str)
18462     {
18463         if (!str || typeof(str) == 'object') {
18464             return str;
18465         }
18466         if (str.substring(0,1) == '#') {
18467             return str;
18468         }
18469
18470         var ar = str.split('.');
18471         var rt, o;
18472         rt = ar.shift();
18473             /** eval:var:o */
18474         try {
18475             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18476         } catch (e) {
18477             throw "Module not found : " + str;
18478         }
18479         
18480         if (o === false) {
18481             throw "Module not found : " + str;
18482         }
18483         Roo.each(ar, function(e) {
18484             if (typeof(o[e]) == 'undefined') {
18485                 throw "Module not found : " + str;
18486             }
18487             o = o[e];
18488         });
18489         
18490         return o;
18491         
18492     },
18493     
18494     
18495     /**
18496      * move modules into their correct place in the tree..
18497      * 
18498      */
18499     preBuild : function ()
18500     {
18501         var _t = this;
18502         Roo.each(this.modules , function (obj)
18503         {
18504             Roo.XComponent.event.fireEvent('beforebuild', obj);
18505             
18506             var opar = obj.parent;
18507             try { 
18508                 obj.parent = this.toObject(opar);
18509             } catch(e) {
18510                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18511                 return;
18512             }
18513             
18514             if (!obj.parent) {
18515                 Roo.debug && Roo.log("GOT top level module");
18516                 Roo.debug && Roo.log(obj);
18517                 obj.modules = new Roo.util.MixedCollection(false, 
18518                     function(o) { return o.order + '' }
18519                 );
18520                 this.topModule = obj;
18521                 return;
18522             }
18523                         // parent is a string (usually a dom element name..)
18524             if (typeof(obj.parent) == 'string') {
18525                 this.elmodules.push(obj);
18526                 return;
18527             }
18528             if (obj.parent.constructor != Roo.XComponent) {
18529                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18530             }
18531             if (!obj.parent.modules) {
18532                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18533                     function(o) { return o.order + '' }
18534                 );
18535             }
18536             if (obj.parent.disabled) {
18537                 obj.disabled = true;
18538             }
18539             obj.parent.modules.add(obj);
18540         }, this);
18541     },
18542     
18543      /**
18544      * make a list of modules to build.
18545      * @return {Array} list of modules. 
18546      */ 
18547     
18548     buildOrder : function()
18549     {
18550         var _this = this;
18551         var cmp = function(a,b) {   
18552             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18553         };
18554         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18555             throw "No top level modules to build";
18556         }
18557         
18558         // make a flat list in order of modules to build.
18559         var mods = this.topModule ? [ this.topModule ] : [];
18560                 
18561         
18562         // elmodules (is a list of DOM based modules )
18563         Roo.each(this.elmodules, function(e) {
18564             mods.push(e);
18565             if (!this.topModule &&
18566                 typeof(e.parent) == 'string' &&
18567                 e.parent.substring(0,1) == '#' &&
18568                 Roo.get(e.parent.substr(1))
18569                ) {
18570                 
18571                 _this.topModule = e;
18572             }
18573             
18574         });
18575
18576         
18577         // add modules to their parents..
18578         var addMod = function(m) {
18579             Roo.debug && Roo.log("build Order: add: " + m.name);
18580                 
18581             mods.push(m);
18582             if (m.modules && !m.disabled) {
18583                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18584                 m.modules.keySort('ASC',  cmp );
18585                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18586     
18587                 m.modules.each(addMod);
18588             } else {
18589                 Roo.debug && Roo.log("build Order: no child modules");
18590             }
18591             // not sure if this is used any more..
18592             if (m.finalize) {
18593                 m.finalize.name = m.name + " (clean up) ";
18594                 mods.push(m.finalize);
18595             }
18596             
18597         }
18598         if (this.topModule && this.topModule.modules) { 
18599             this.topModule.modules.keySort('ASC',  cmp );
18600             this.topModule.modules.each(addMod);
18601         } 
18602         return mods;
18603     },
18604     
18605      /**
18606      * Build the registered modules.
18607      * @param {Object} parent element.
18608      * @param {Function} optional method to call after module has been added.
18609      * 
18610      */ 
18611    
18612     build : function(opts) 
18613     {
18614         
18615         if (typeof(opts) != 'undefined') {
18616             Roo.apply(this,opts);
18617         }
18618         
18619         this.preBuild();
18620         var mods = this.buildOrder();
18621       
18622         //this.allmods = mods;
18623         //Roo.debug && Roo.log(mods);
18624         //return;
18625         if (!mods.length) { // should not happen
18626             throw "NO modules!!!";
18627         }
18628         
18629         
18630         var msg = "Building Interface...";
18631         // flash it up as modal - so we store the mask!?
18632         if (!this.hideProgress && Roo.MessageBox) {
18633             Roo.MessageBox.show({ title: 'loading' });
18634             Roo.MessageBox.show({
18635                title: "Please wait...",
18636                msg: msg,
18637                width:450,
18638                progress:true,
18639                buttons : false,
18640                closable:false,
18641                modal: false
18642               
18643             });
18644         }
18645         var total = mods.length;
18646         
18647         var _this = this;
18648         var progressRun = function() {
18649             if (!mods.length) {
18650                 Roo.debug && Roo.log('hide?');
18651                 if (!this.hideProgress && Roo.MessageBox) {
18652                     Roo.MessageBox.hide();
18653                 }
18654                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18655                 
18656                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18657                 
18658                 // THE END...
18659                 return false;   
18660             }
18661             
18662             var m = mods.shift();
18663             
18664             
18665             Roo.debug && Roo.log(m);
18666             // not sure if this is supported any more.. - modules that are are just function
18667             if (typeof(m) == 'function') { 
18668                 m.call(this);
18669                 return progressRun.defer(10, _this);
18670             } 
18671             
18672             
18673             msg = "Building Interface " + (total  - mods.length) + 
18674                     " of " + total + 
18675                     (m.name ? (' - ' + m.name) : '');
18676                         Roo.debug && Roo.log(msg);
18677             if (!_this.hideProgress &&  Roo.MessageBox) { 
18678                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18679             }
18680             
18681          
18682             // is the module disabled?
18683             var disabled = (typeof(m.disabled) == 'function') ?
18684                 m.disabled.call(m.module.disabled) : m.disabled;    
18685             
18686             
18687             if (disabled) {
18688                 return progressRun(); // we do not update the display!
18689             }
18690             
18691             // now build 
18692             
18693                         
18694                         
18695             m.render();
18696             // it's 10 on top level, and 1 on others??? why...
18697             return progressRun.defer(10, _this);
18698              
18699         }
18700         progressRun.defer(1, _this);
18701      
18702         
18703         
18704     },
18705     /**
18706      * Overlay a set of modified strings onto a component
18707      * This is dependant on our builder exporting the strings and 'named strings' elements.
18708      * 
18709      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18710      * @param {Object} associative array of 'named' string and it's new value.
18711      * 
18712      */
18713         overlayStrings : function( component, strings )
18714     {
18715         if (typeof(component['_named_strings']) == 'undefined') {
18716             throw "ERROR: component does not have _named_strings";
18717         }
18718         for ( var k in strings ) {
18719             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18720             if (md !== false) {
18721                 component['_strings'][md] = strings[k];
18722             } else {
18723                 Roo.log('could not find named string: ' + k + ' in');
18724                 Roo.log(component);
18725             }
18726             
18727         }
18728         
18729     },
18730     
18731         
18732         /**
18733          * Event Object.
18734          *
18735          *
18736          */
18737         event: false, 
18738     /**
18739          * wrapper for event.on - aliased later..  
18740          * Typically use to register a event handler for register:
18741          *
18742          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18743          *
18744          */
18745     on : false
18746    
18747     
18748     
18749 });
18750
18751 Roo.XComponent.event = new Roo.util.Observable({
18752                 events : { 
18753                         /**
18754                          * @event register
18755                          * Fires when an Component is registered,
18756                          * set the disable property on the Component to stop registration.
18757                          * @param {Roo.XComponent} c the component being registerd.
18758                          * 
18759                          */
18760                         'register' : true,
18761             /**
18762                          * @event beforebuild
18763                          * Fires before each Component is built
18764                          * can be used to apply permissions.
18765                          * @param {Roo.XComponent} c the component being registerd.
18766                          * 
18767                          */
18768                         'beforebuild' : true,
18769                         /**
18770                          * @event buildcomplete
18771                          * Fires on the top level element when all elements have been built
18772                          * @param {Roo.XComponent} the top level component.
18773                          */
18774                         'buildcomplete' : true
18775                         
18776                 }
18777 });
18778
18779 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18780  //
18781  /**
18782  * marked - a markdown parser
18783  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18784  * https://github.com/chjj/marked
18785  */
18786
18787
18788 /**
18789  *
18790  * Roo.Markdown - is a very crude wrapper around marked..
18791  *
18792  * usage:
18793  * 
18794  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18795  * 
18796  * Note: move the sample code to the bottom of this
18797  * file before uncommenting it.
18798  *
18799  */
18800
18801 Roo.Markdown = {};
18802 Roo.Markdown.toHtml = function(text) {
18803     
18804     var c = new Roo.Markdown.marked.setOptions({
18805             renderer: new Roo.Markdown.marked.Renderer(),
18806             gfm: true,
18807             tables: true,
18808             breaks: false,
18809             pedantic: false,
18810             sanitize: false,
18811             smartLists: true,
18812             smartypants: false
18813           });
18814     // A FEW HACKS!!?
18815     
18816     text = text.replace(/\\\n/g,' ');
18817     return Roo.Markdown.marked(text);
18818 };
18819 //
18820 // converter
18821 //
18822 // Wraps all "globals" so that the only thing
18823 // exposed is makeHtml().
18824 //
18825 (function() {
18826     
18827      /**
18828          * eval:var:escape
18829          * eval:var:unescape
18830          * eval:var:replace
18831          */
18832       
18833     /**
18834      * Helpers
18835      */
18836     
18837     var escape = function (html, encode) {
18838       return html
18839         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18840         .replace(/</g, '&lt;')
18841         .replace(/>/g, '&gt;')
18842         .replace(/"/g, '&quot;')
18843         .replace(/'/g, '&#39;');
18844     }
18845     
18846     var unescape = function (html) {
18847         // explicitly match decimal, hex, and named HTML entities 
18848       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18849         n = n.toLowerCase();
18850         if (n === 'colon') { return ':'; }
18851         if (n.charAt(0) === '#') {
18852           return n.charAt(1) === 'x'
18853             ? String.fromCharCode(parseInt(n.substring(2), 16))
18854             : String.fromCharCode(+n.substring(1));
18855         }
18856         return '';
18857       });
18858     }
18859     
18860     var replace = function (regex, opt) {
18861       regex = regex.source;
18862       opt = opt || '';
18863       return function self(name, val) {
18864         if (!name) { return new RegExp(regex, opt); }
18865         val = val.source || val;
18866         val = val.replace(/(^|[^\[])\^/g, '$1');
18867         regex = regex.replace(name, val);
18868         return self;
18869       };
18870     }
18871
18872
18873          /**
18874          * eval:var:noop
18875     */
18876     var noop = function () {}
18877     noop.exec = noop;
18878     
18879          /**
18880          * eval:var:merge
18881     */
18882     var merge = function (obj) {
18883       var i = 1
18884         , target
18885         , key;
18886     
18887       for (; i < arguments.length; i++) {
18888         target = arguments[i];
18889         for (key in target) {
18890           if (Object.prototype.hasOwnProperty.call(target, key)) {
18891             obj[key] = target[key];
18892           }
18893         }
18894       }
18895     
18896       return obj;
18897     }
18898     
18899     
18900     /**
18901      * Block-Level Grammar
18902      */
18903     
18904     
18905     
18906     
18907     var block = {
18908       newline: /^\n+/,
18909       code: /^( {4}[^\n]+\n*)+/,
18910       fences: noop,
18911       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18912       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18913       nptable: noop,
18914       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18915       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18916       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18917       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18918       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18919       table: noop,
18920       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18921       text: /^[^\n]+/
18922     };
18923     
18924     block.bullet = /(?:[*+-]|\d+\.)/;
18925     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18926     block.item = replace(block.item, 'gm')
18927       (/bull/g, block.bullet)
18928       ();
18929     
18930     block.list = replace(block.list)
18931       (/bull/g, block.bullet)
18932       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18933       ('def', '\\n+(?=' + block.def.source + ')')
18934       ();
18935     
18936     block.blockquote = replace(block.blockquote)
18937       ('def', block.def)
18938       ();
18939     
18940     block._tag = '(?!(?:'
18941       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18942       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18943       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18944     
18945     block.html = replace(block.html)
18946       ('comment', /<!--[\s\S]*?-->/)
18947       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18948       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18949       (/tag/g, block._tag)
18950       ();
18951     
18952     block.paragraph = replace(block.paragraph)
18953       ('hr', block.hr)
18954       ('heading', block.heading)
18955       ('lheading', block.lheading)
18956       ('blockquote', block.blockquote)
18957       ('tag', '<' + block._tag)
18958       ('def', block.def)
18959       ();
18960     
18961     /**
18962      * Normal Block Grammar
18963      */
18964     
18965     block.normal = merge({}, block);
18966     
18967     /**
18968      * GFM Block Grammar
18969      */
18970     
18971     block.gfm = merge({}, block.normal, {
18972       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18973       paragraph: /^/,
18974       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18975     });
18976     
18977     block.gfm.paragraph = replace(block.paragraph)
18978       ('(?!', '(?!'
18979         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18980         + block.list.source.replace('\\1', '\\3') + '|')
18981       ();
18982     
18983     /**
18984      * GFM + Tables Block Grammar
18985      */
18986     
18987     block.tables = merge({}, block.gfm, {
18988       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18989       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18990     });
18991     
18992     /**
18993      * Block Lexer
18994      */
18995     
18996     var Lexer = function (options) {
18997       this.tokens = [];
18998       this.tokens.links = {};
18999       this.options = options || marked.defaults;
19000       this.rules = block.normal;
19001     
19002       if (this.options.gfm) {
19003         if (this.options.tables) {
19004           this.rules = block.tables;
19005         } else {
19006           this.rules = block.gfm;
19007         }
19008       }
19009     }
19010     
19011     /**
19012      * Expose Block Rules
19013      */
19014     
19015     Lexer.rules = block;
19016     
19017     /**
19018      * Static Lex Method
19019      */
19020     
19021     Lexer.lex = function(src, options) {
19022       var lexer = new Lexer(options);
19023       return lexer.lex(src);
19024     };
19025     
19026     /**
19027      * Preprocessing
19028      */
19029     
19030     Lexer.prototype.lex = function(src) {
19031       src = src
19032         .replace(/\r\n|\r/g, '\n')
19033         .replace(/\t/g, '    ')
19034         .replace(/\u00a0/g, ' ')
19035         .replace(/\u2424/g, '\n');
19036     
19037       return this.token(src, true);
19038     };
19039     
19040     /**
19041      * Lexing
19042      */
19043     
19044     Lexer.prototype.token = function(src, top, bq) {
19045       var src = src.replace(/^ +$/gm, '')
19046         , next
19047         , loose
19048         , cap
19049         , bull
19050         , b
19051         , item
19052         , space
19053         , i
19054         , l;
19055     
19056       while (src) {
19057         // newline
19058         if (cap = this.rules.newline.exec(src)) {
19059           src = src.substring(cap[0].length);
19060           if (cap[0].length > 1) {
19061             this.tokens.push({
19062               type: 'space'
19063             });
19064           }
19065         }
19066     
19067         // code
19068         if (cap = this.rules.code.exec(src)) {
19069           src = src.substring(cap[0].length);
19070           cap = cap[0].replace(/^ {4}/gm, '');
19071           this.tokens.push({
19072             type: 'code',
19073             text: !this.options.pedantic
19074               ? cap.replace(/\n+$/, '')
19075               : cap
19076           });
19077           continue;
19078         }
19079     
19080         // fences (gfm)
19081         if (cap = this.rules.fences.exec(src)) {
19082           src = src.substring(cap[0].length);
19083           this.tokens.push({
19084             type: 'code',
19085             lang: cap[2],
19086             text: cap[3] || ''
19087           });
19088           continue;
19089         }
19090     
19091         // heading
19092         if (cap = this.rules.heading.exec(src)) {
19093           src = src.substring(cap[0].length);
19094           this.tokens.push({
19095             type: 'heading',
19096             depth: cap[1].length,
19097             text: cap[2]
19098           });
19099           continue;
19100         }
19101     
19102         // table no leading pipe (gfm)
19103         if (top && (cap = this.rules.nptable.exec(src))) {
19104           src = src.substring(cap[0].length);
19105     
19106           item = {
19107             type: 'table',
19108             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19109             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19110             cells: cap[3].replace(/\n$/, '').split('\n')
19111           };
19112     
19113           for (i = 0; i < item.align.length; i++) {
19114             if (/^ *-+: *$/.test(item.align[i])) {
19115               item.align[i] = 'right';
19116             } else if (/^ *:-+: *$/.test(item.align[i])) {
19117               item.align[i] = 'center';
19118             } else if (/^ *:-+ *$/.test(item.align[i])) {
19119               item.align[i] = 'left';
19120             } else {
19121               item.align[i] = null;
19122             }
19123           }
19124     
19125           for (i = 0; i < item.cells.length; i++) {
19126             item.cells[i] = item.cells[i].split(/ *\| */);
19127           }
19128     
19129           this.tokens.push(item);
19130     
19131           continue;
19132         }
19133     
19134         // lheading
19135         if (cap = this.rules.lheading.exec(src)) {
19136           src = src.substring(cap[0].length);
19137           this.tokens.push({
19138             type: 'heading',
19139             depth: cap[2] === '=' ? 1 : 2,
19140             text: cap[1]
19141           });
19142           continue;
19143         }
19144     
19145         // hr
19146         if (cap = this.rules.hr.exec(src)) {
19147           src = src.substring(cap[0].length);
19148           this.tokens.push({
19149             type: 'hr'
19150           });
19151           continue;
19152         }
19153     
19154         // blockquote
19155         if (cap = this.rules.blockquote.exec(src)) {
19156           src = src.substring(cap[0].length);
19157     
19158           this.tokens.push({
19159             type: 'blockquote_start'
19160           });
19161     
19162           cap = cap[0].replace(/^ *> ?/gm, '');
19163     
19164           // Pass `top` to keep the current
19165           // "toplevel" state. This is exactly
19166           // how markdown.pl works.
19167           this.token(cap, top, true);
19168     
19169           this.tokens.push({
19170             type: 'blockquote_end'
19171           });
19172     
19173           continue;
19174         }
19175     
19176         // list
19177         if (cap = this.rules.list.exec(src)) {
19178           src = src.substring(cap[0].length);
19179           bull = cap[2];
19180     
19181           this.tokens.push({
19182             type: 'list_start',
19183             ordered: bull.length > 1
19184           });
19185     
19186           // Get each top-level item.
19187           cap = cap[0].match(this.rules.item);
19188     
19189           next = false;
19190           l = cap.length;
19191           i = 0;
19192     
19193           for (; i < l; i++) {
19194             item = cap[i];
19195     
19196             // Remove the list item's bullet
19197             // so it is seen as the next token.
19198             space = item.length;
19199             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19200     
19201             // Outdent whatever the
19202             // list item contains. Hacky.
19203             if (~item.indexOf('\n ')) {
19204               space -= item.length;
19205               item = !this.options.pedantic
19206                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19207                 : item.replace(/^ {1,4}/gm, '');
19208             }
19209     
19210             // Determine whether the next list item belongs here.
19211             // Backpedal if it does not belong in this list.
19212             if (this.options.smartLists && i !== l - 1) {
19213               b = block.bullet.exec(cap[i + 1])[0];
19214               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19215                 src = cap.slice(i + 1).join('\n') + src;
19216                 i = l - 1;
19217               }
19218             }
19219     
19220             // Determine whether item is loose or not.
19221             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19222             // for discount behavior.
19223             loose = next || /\n\n(?!\s*$)/.test(item);
19224             if (i !== l - 1) {
19225               next = item.charAt(item.length - 1) === '\n';
19226               if (!loose) { loose = next; }
19227             }
19228     
19229             this.tokens.push({
19230               type: loose
19231                 ? 'loose_item_start'
19232                 : 'list_item_start'
19233             });
19234     
19235             // Recurse.
19236             this.token(item, false, bq);
19237     
19238             this.tokens.push({
19239               type: 'list_item_end'
19240             });
19241           }
19242     
19243           this.tokens.push({
19244             type: 'list_end'
19245           });
19246     
19247           continue;
19248         }
19249     
19250         // html
19251         if (cap = this.rules.html.exec(src)) {
19252           src = src.substring(cap[0].length);
19253           this.tokens.push({
19254             type: this.options.sanitize
19255               ? 'paragraph'
19256               : 'html',
19257             pre: !this.options.sanitizer
19258               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19259             text: cap[0]
19260           });
19261           continue;
19262         }
19263     
19264         // def
19265         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19266           src = src.substring(cap[0].length);
19267           this.tokens.links[cap[1].toLowerCase()] = {
19268             href: cap[2],
19269             title: cap[3]
19270           };
19271           continue;
19272         }
19273     
19274         // table (gfm)
19275         if (top && (cap = this.rules.table.exec(src))) {
19276           src = src.substring(cap[0].length);
19277     
19278           item = {
19279             type: 'table',
19280             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19281             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19282             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19283           };
19284     
19285           for (i = 0; i < item.align.length; i++) {
19286             if (/^ *-+: *$/.test(item.align[i])) {
19287               item.align[i] = 'right';
19288             } else if (/^ *:-+: *$/.test(item.align[i])) {
19289               item.align[i] = 'center';
19290             } else if (/^ *:-+ *$/.test(item.align[i])) {
19291               item.align[i] = 'left';
19292             } else {
19293               item.align[i] = null;
19294             }
19295           }
19296     
19297           for (i = 0; i < item.cells.length; i++) {
19298             item.cells[i] = item.cells[i]
19299               .replace(/^ *\| *| *\| *$/g, '')
19300               .split(/ *\| */);
19301           }
19302     
19303           this.tokens.push(item);
19304     
19305           continue;
19306         }
19307     
19308         // top-level paragraph
19309         if (top && (cap = this.rules.paragraph.exec(src))) {
19310           src = src.substring(cap[0].length);
19311           this.tokens.push({
19312             type: 'paragraph',
19313             text: cap[1].charAt(cap[1].length - 1) === '\n'
19314               ? cap[1].slice(0, -1)
19315               : cap[1]
19316           });
19317           continue;
19318         }
19319     
19320         // text
19321         if (cap = this.rules.text.exec(src)) {
19322           // Top-level should never reach here.
19323           src = src.substring(cap[0].length);
19324           this.tokens.push({
19325             type: 'text',
19326             text: cap[0]
19327           });
19328           continue;
19329         }
19330     
19331         if (src) {
19332           throw new
19333             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19334         }
19335       }
19336     
19337       return this.tokens;
19338     };
19339     
19340     /**
19341      * Inline-Level Grammar
19342      */
19343     
19344     var inline = {
19345       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19346       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19347       url: noop,
19348       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19349       link: /^!?\[(inside)\]\(href\)/,
19350       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19351       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19352       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19353       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19354       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19355       br: /^ {2,}\n(?!\s*$)/,
19356       del: noop,
19357       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19358     };
19359     
19360     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19361     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19362     
19363     inline.link = replace(inline.link)
19364       ('inside', inline._inside)
19365       ('href', inline._href)
19366       ();
19367     
19368     inline.reflink = replace(inline.reflink)
19369       ('inside', inline._inside)
19370       ();
19371     
19372     /**
19373      * Normal Inline Grammar
19374      */
19375     
19376     inline.normal = merge({}, inline);
19377     
19378     /**
19379      * Pedantic Inline Grammar
19380      */
19381     
19382     inline.pedantic = merge({}, inline.normal, {
19383       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19384       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19385     });
19386     
19387     /**
19388      * GFM Inline Grammar
19389      */
19390     
19391     inline.gfm = merge({}, inline.normal, {
19392       escape: replace(inline.escape)('])', '~|])')(),
19393       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19394       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19395       text: replace(inline.text)
19396         (']|', '~]|')
19397         ('|', '|https?://|')
19398         ()
19399     });
19400     
19401     /**
19402      * GFM + Line Breaks Inline Grammar
19403      */
19404     
19405     inline.breaks = merge({}, inline.gfm, {
19406       br: replace(inline.br)('{2,}', '*')(),
19407       text: replace(inline.gfm.text)('{2,}', '*')()
19408     });
19409     
19410     /**
19411      * Inline Lexer & Compiler
19412      */
19413     
19414     var InlineLexer  = function (links, options) {
19415       this.options = options || marked.defaults;
19416       this.links = links;
19417       this.rules = inline.normal;
19418       this.renderer = this.options.renderer || new Renderer;
19419       this.renderer.options = this.options;
19420     
19421       if (!this.links) {
19422         throw new
19423           Error('Tokens array requires a `links` property.');
19424       }
19425     
19426       if (this.options.gfm) {
19427         if (this.options.breaks) {
19428           this.rules = inline.breaks;
19429         } else {
19430           this.rules = inline.gfm;
19431         }
19432       } else if (this.options.pedantic) {
19433         this.rules = inline.pedantic;
19434       }
19435     }
19436     
19437     /**
19438      * Expose Inline Rules
19439      */
19440     
19441     InlineLexer.rules = inline;
19442     
19443     /**
19444      * Static Lexing/Compiling Method
19445      */
19446     
19447     InlineLexer.output = function(src, links, options) {
19448       var inline = new InlineLexer(links, options);
19449       return inline.output(src);
19450     };
19451     
19452     /**
19453      * Lexing/Compiling
19454      */
19455     
19456     InlineLexer.prototype.output = function(src) {
19457       var out = ''
19458         , link
19459         , text
19460         , href
19461         , cap;
19462     
19463       while (src) {
19464         // escape
19465         if (cap = this.rules.escape.exec(src)) {
19466           src = src.substring(cap[0].length);
19467           out += cap[1];
19468           continue;
19469         }
19470     
19471         // autolink
19472         if (cap = this.rules.autolink.exec(src)) {
19473           src = src.substring(cap[0].length);
19474           if (cap[2] === '@') {
19475             text = cap[1].charAt(6) === ':'
19476               ? this.mangle(cap[1].substring(7))
19477               : this.mangle(cap[1]);
19478             href = this.mangle('mailto:') + text;
19479           } else {
19480             text = escape(cap[1]);
19481             href = text;
19482           }
19483           out += this.renderer.link(href, null, text);
19484           continue;
19485         }
19486     
19487         // url (gfm)
19488         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19489           src = src.substring(cap[0].length);
19490           text = escape(cap[1]);
19491           href = text;
19492           out += this.renderer.link(href, null, text);
19493           continue;
19494         }
19495     
19496         // tag
19497         if (cap = this.rules.tag.exec(src)) {
19498           if (!this.inLink && /^<a /i.test(cap[0])) {
19499             this.inLink = true;
19500           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19501             this.inLink = false;
19502           }
19503           src = src.substring(cap[0].length);
19504           out += this.options.sanitize
19505             ? this.options.sanitizer
19506               ? this.options.sanitizer(cap[0])
19507               : escape(cap[0])
19508             : cap[0];
19509           continue;
19510         }
19511     
19512         // link
19513         if (cap = this.rules.link.exec(src)) {
19514           src = src.substring(cap[0].length);
19515           this.inLink = true;
19516           out += this.outputLink(cap, {
19517             href: cap[2],
19518             title: cap[3]
19519           });
19520           this.inLink = false;
19521           continue;
19522         }
19523     
19524         // reflink, nolink
19525         if ((cap = this.rules.reflink.exec(src))
19526             || (cap = this.rules.nolink.exec(src))) {
19527           src = src.substring(cap[0].length);
19528           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19529           link = this.links[link.toLowerCase()];
19530           if (!link || !link.href) {
19531             out += cap[0].charAt(0);
19532             src = cap[0].substring(1) + src;
19533             continue;
19534           }
19535           this.inLink = true;
19536           out += this.outputLink(cap, link);
19537           this.inLink = false;
19538           continue;
19539         }
19540     
19541         // strong
19542         if (cap = this.rules.strong.exec(src)) {
19543           src = src.substring(cap[0].length);
19544           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19545           continue;
19546         }
19547     
19548         // em
19549         if (cap = this.rules.em.exec(src)) {
19550           src = src.substring(cap[0].length);
19551           out += this.renderer.em(this.output(cap[2] || cap[1]));
19552           continue;
19553         }
19554     
19555         // code
19556         if (cap = this.rules.code.exec(src)) {
19557           src = src.substring(cap[0].length);
19558           out += this.renderer.codespan(escape(cap[2], true));
19559           continue;
19560         }
19561     
19562         // br
19563         if (cap = this.rules.br.exec(src)) {
19564           src = src.substring(cap[0].length);
19565           out += this.renderer.br();
19566           continue;
19567         }
19568     
19569         // del (gfm)
19570         if (cap = this.rules.del.exec(src)) {
19571           src = src.substring(cap[0].length);
19572           out += this.renderer.del(this.output(cap[1]));
19573           continue;
19574         }
19575     
19576         // text
19577         if (cap = this.rules.text.exec(src)) {
19578           src = src.substring(cap[0].length);
19579           out += this.renderer.text(escape(this.smartypants(cap[0])));
19580           continue;
19581         }
19582     
19583         if (src) {
19584           throw new
19585             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19586         }
19587       }
19588     
19589       return out;
19590     };
19591     
19592     /**
19593      * Compile Link
19594      */
19595     
19596     InlineLexer.prototype.outputLink = function(cap, link) {
19597       var href = escape(link.href)
19598         , title = link.title ? escape(link.title) : null;
19599     
19600       return cap[0].charAt(0) !== '!'
19601         ? this.renderer.link(href, title, this.output(cap[1]))
19602         : this.renderer.image(href, title, escape(cap[1]));
19603     };
19604     
19605     /**
19606      * Smartypants Transformations
19607      */
19608     
19609     InlineLexer.prototype.smartypants = function(text) {
19610       if (!this.options.smartypants)  { return text; }
19611       return text
19612         // em-dashes
19613         .replace(/---/g, '\u2014')
19614         // en-dashes
19615         .replace(/--/g, '\u2013')
19616         // opening singles
19617         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19618         // closing singles & apostrophes
19619         .replace(/'/g, '\u2019')
19620         // opening doubles
19621         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19622         // closing doubles
19623         .replace(/"/g, '\u201d')
19624         // ellipses
19625         .replace(/\.{3}/g, '\u2026');
19626     };
19627     
19628     /**
19629      * Mangle Links
19630      */
19631     
19632     InlineLexer.prototype.mangle = function(text) {
19633       if (!this.options.mangle) { return text; }
19634       var out = ''
19635         , l = text.length
19636         , i = 0
19637         , ch;
19638     
19639       for (; i < l; i++) {
19640         ch = text.charCodeAt(i);
19641         if (Math.random() > 0.5) {
19642           ch = 'x' + ch.toString(16);
19643         }
19644         out += '&#' + ch + ';';
19645       }
19646     
19647       return out;
19648     };
19649     
19650     /**
19651      * Renderer
19652      */
19653     
19654      /**
19655          * eval:var:Renderer
19656     */
19657     
19658     var Renderer   = function (options) {
19659       this.options = options || {};
19660     }
19661     
19662     Renderer.prototype.code = function(code, lang, escaped) {
19663       if (this.options.highlight) {
19664         var out = this.options.highlight(code, lang);
19665         if (out != null && out !== code) {
19666           escaped = true;
19667           code = out;
19668         }
19669       } else {
19670             // hack!!! - it's already escapeD?
19671             escaped = true;
19672       }
19673     
19674       if (!lang) {
19675         return '<pre><code>'
19676           + (escaped ? code : escape(code, true))
19677           + '\n</code></pre>';
19678       }
19679     
19680       return '<pre><code class="'
19681         + this.options.langPrefix
19682         + escape(lang, true)
19683         + '">'
19684         + (escaped ? code : escape(code, true))
19685         + '\n</code></pre>\n';
19686     };
19687     
19688     Renderer.prototype.blockquote = function(quote) {
19689       return '<blockquote>\n' + quote + '</blockquote>\n';
19690     };
19691     
19692     Renderer.prototype.html = function(html) {
19693       return html;
19694     };
19695     
19696     Renderer.prototype.heading = function(text, level, raw) {
19697       return '<h'
19698         + level
19699         + ' id="'
19700         + this.options.headerPrefix
19701         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19702         + '">'
19703         + text
19704         + '</h'
19705         + level
19706         + '>\n';
19707     };
19708     
19709     Renderer.prototype.hr = function() {
19710       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19711     };
19712     
19713     Renderer.prototype.list = function(body, ordered) {
19714       var type = ordered ? 'ol' : 'ul';
19715       return '<' + type + '>\n' + body + '</' + type + '>\n';
19716     };
19717     
19718     Renderer.prototype.listitem = function(text) {
19719       return '<li>' + text + '</li>\n';
19720     };
19721     
19722     Renderer.prototype.paragraph = function(text) {
19723       return '<p>' + text + '</p>\n';
19724     };
19725     
19726     Renderer.prototype.table = function(header, body) {
19727       return '<table class="table table-striped">\n'
19728         + '<thead>\n'
19729         + header
19730         + '</thead>\n'
19731         + '<tbody>\n'
19732         + body
19733         + '</tbody>\n'
19734         + '</table>\n';
19735     };
19736     
19737     Renderer.prototype.tablerow = function(content) {
19738       return '<tr>\n' + content + '</tr>\n';
19739     };
19740     
19741     Renderer.prototype.tablecell = function(content, flags) {
19742       var type = flags.header ? 'th' : 'td';
19743       var tag = flags.align
19744         ? '<' + type + ' style="text-align:' + flags.align + '">'
19745         : '<' + type + '>';
19746       return tag + content + '</' + type + '>\n';
19747     };
19748     
19749     // span level renderer
19750     Renderer.prototype.strong = function(text) {
19751       return '<strong>' + text + '</strong>';
19752     };
19753     
19754     Renderer.prototype.em = function(text) {
19755       return '<em>' + text + '</em>';
19756     };
19757     
19758     Renderer.prototype.codespan = function(text) {
19759       return '<code>' + text + '</code>';
19760     };
19761     
19762     Renderer.prototype.br = function() {
19763       return this.options.xhtml ? '<br/>' : '<br>';
19764     };
19765     
19766     Renderer.prototype.del = function(text) {
19767       return '<del>' + text + '</del>';
19768     };
19769     
19770     Renderer.prototype.link = function(href, title, text) {
19771       if (this.options.sanitize) {
19772         try {
19773           var prot = decodeURIComponent(unescape(href))
19774             .replace(/[^\w:]/g, '')
19775             .toLowerCase();
19776         } catch (e) {
19777           return '';
19778         }
19779         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19780           return '';
19781         }
19782       }
19783       var out = '<a href="' + href + '"';
19784       if (title) {
19785         out += ' title="' + title + '"';
19786       }
19787       out += '>' + text + '</a>';
19788       return out;
19789     };
19790     
19791     Renderer.prototype.image = function(href, title, text) {
19792       var out = '<img src="' + href + '" alt="' + text + '"';
19793       if (title) {
19794         out += ' title="' + title + '"';
19795       }
19796       out += this.options.xhtml ? '/>' : '>';
19797       return out;
19798     };
19799     
19800     Renderer.prototype.text = function(text) {
19801       return text;
19802     };
19803     
19804     /**
19805      * Parsing & Compiling
19806      */
19807          /**
19808          * eval:var:Parser
19809     */
19810     
19811     var Parser= function (options) {
19812       this.tokens = [];
19813       this.token = null;
19814       this.options = options || marked.defaults;
19815       this.options.renderer = this.options.renderer || new Renderer;
19816       this.renderer = this.options.renderer;
19817       this.renderer.options = this.options;
19818     }
19819     
19820     /**
19821      * Static Parse Method
19822      */
19823     
19824     Parser.parse = function(src, options, renderer) {
19825       var parser = new Parser(options, renderer);
19826       return parser.parse(src);
19827     };
19828     
19829     /**
19830      * Parse Loop
19831      */
19832     
19833     Parser.prototype.parse = function(src) {
19834       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19835       this.tokens = src.reverse();
19836     
19837       var out = '';
19838       while (this.next()) {
19839         out += this.tok();
19840       }
19841     
19842       return out;
19843     };
19844     
19845     /**
19846      * Next Token
19847      */
19848     
19849     Parser.prototype.next = function() {
19850       return this.token = this.tokens.pop();
19851     };
19852     
19853     /**
19854      * Preview Next Token
19855      */
19856     
19857     Parser.prototype.peek = function() {
19858       return this.tokens[this.tokens.length - 1] || 0;
19859     };
19860     
19861     /**
19862      * Parse Text Tokens
19863      */
19864     
19865     Parser.prototype.parseText = function() {
19866       var body = this.token.text;
19867     
19868       while (this.peek().type === 'text') {
19869         body += '\n' + this.next().text;
19870       }
19871     
19872       return this.inline.output(body);
19873     };
19874     
19875     /**
19876      * Parse Current Token
19877      */
19878     
19879     Parser.prototype.tok = function() {
19880       switch (this.token.type) {
19881         case 'space': {
19882           return '';
19883         }
19884         case 'hr': {
19885           return this.renderer.hr();
19886         }
19887         case 'heading': {
19888           return this.renderer.heading(
19889             this.inline.output(this.token.text),
19890             this.token.depth,
19891             this.token.text);
19892         }
19893         case 'code': {
19894           return this.renderer.code(this.token.text,
19895             this.token.lang,
19896             this.token.escaped);
19897         }
19898         case 'table': {
19899           var header = ''
19900             , body = ''
19901             , i
19902             , row
19903             , cell
19904             , flags
19905             , j;
19906     
19907           // header
19908           cell = '';
19909           for (i = 0; i < this.token.header.length; i++) {
19910             flags = { header: true, align: this.token.align[i] };
19911             cell += this.renderer.tablecell(
19912               this.inline.output(this.token.header[i]),
19913               { header: true, align: this.token.align[i] }
19914             );
19915           }
19916           header += this.renderer.tablerow(cell);
19917     
19918           for (i = 0; i < this.token.cells.length; i++) {
19919             row = this.token.cells[i];
19920     
19921             cell = '';
19922             for (j = 0; j < row.length; j++) {
19923               cell += this.renderer.tablecell(
19924                 this.inline.output(row[j]),
19925                 { header: false, align: this.token.align[j] }
19926               );
19927             }
19928     
19929             body += this.renderer.tablerow(cell);
19930           }
19931           return this.renderer.table(header, body);
19932         }
19933         case 'blockquote_start': {
19934           var body = '';
19935     
19936           while (this.next().type !== 'blockquote_end') {
19937             body += this.tok();
19938           }
19939     
19940           return this.renderer.blockquote(body);
19941         }
19942         case 'list_start': {
19943           var body = ''
19944             , ordered = this.token.ordered;
19945     
19946           while (this.next().type !== 'list_end') {
19947             body += this.tok();
19948           }
19949     
19950           return this.renderer.list(body, ordered);
19951         }
19952         case 'list_item_start': {
19953           var body = '';
19954     
19955           while (this.next().type !== 'list_item_end') {
19956             body += this.token.type === 'text'
19957               ? this.parseText()
19958               : this.tok();
19959           }
19960     
19961           return this.renderer.listitem(body);
19962         }
19963         case 'loose_item_start': {
19964           var body = '';
19965     
19966           while (this.next().type !== 'list_item_end') {
19967             body += this.tok();
19968           }
19969     
19970           return this.renderer.listitem(body);
19971         }
19972         case 'html': {
19973           var html = !this.token.pre && !this.options.pedantic
19974             ? this.inline.output(this.token.text)
19975             : this.token.text;
19976           return this.renderer.html(html);
19977         }
19978         case 'paragraph': {
19979           return this.renderer.paragraph(this.inline.output(this.token.text));
19980         }
19981         case 'text': {
19982           return this.renderer.paragraph(this.parseText());
19983         }
19984       }
19985     };
19986   
19987     
19988     /**
19989      * Marked
19990      */
19991          /**
19992          * eval:var:marked
19993     */
19994     var marked = function (src, opt, callback) {
19995       if (callback || typeof opt === 'function') {
19996         if (!callback) {
19997           callback = opt;
19998           opt = null;
19999         }
20000     
20001         opt = merge({}, marked.defaults, opt || {});
20002     
20003         var highlight = opt.highlight
20004           , tokens
20005           , pending
20006           , i = 0;
20007     
20008         try {
20009           tokens = Lexer.lex(src, opt)
20010         } catch (e) {
20011           return callback(e);
20012         }
20013     
20014         pending = tokens.length;
20015          /**
20016          * eval:var:done
20017     */
20018         var done = function(err) {
20019           if (err) {
20020             opt.highlight = highlight;
20021             return callback(err);
20022           }
20023     
20024           var out;
20025     
20026           try {
20027             out = Parser.parse(tokens, opt);
20028           } catch (e) {
20029             err = e;
20030           }
20031     
20032           opt.highlight = highlight;
20033     
20034           return err
20035             ? callback(err)
20036             : callback(null, out);
20037         };
20038     
20039         if (!highlight || highlight.length < 3) {
20040           return done();
20041         }
20042     
20043         delete opt.highlight;
20044     
20045         if (!pending) { return done(); }
20046     
20047         for (; i < tokens.length; i++) {
20048           (function(token) {
20049             if (token.type !== 'code') {
20050               return --pending || done();
20051             }
20052             return highlight(token.text, token.lang, function(err, code) {
20053               if (err) { return done(err); }
20054               if (code == null || code === token.text) {
20055                 return --pending || done();
20056               }
20057               token.text = code;
20058               token.escaped = true;
20059               --pending || done();
20060             });
20061           })(tokens[i]);
20062         }
20063     
20064         return;
20065       }
20066       try {
20067         if (opt) { opt = merge({}, marked.defaults, opt); }
20068         return Parser.parse(Lexer.lex(src, opt), opt);
20069       } catch (e) {
20070         e.message += '\nPlease report this to https://github.com/chjj/marked.';
20071         if ((opt || marked.defaults).silent) {
20072           return '<p>An error occured:</p><pre>'
20073             + escape(e.message + '', true)
20074             + '</pre>';
20075         }
20076         throw e;
20077       }
20078     }
20079     
20080     /**
20081      * Options
20082      */
20083     
20084     marked.options =
20085     marked.setOptions = function(opt) {
20086       merge(marked.defaults, opt);
20087       return marked;
20088     };
20089     
20090     marked.defaults = {
20091       gfm: true,
20092       tables: true,
20093       breaks: false,
20094       pedantic: false,
20095       sanitize: false,
20096       sanitizer: null,
20097       mangle: true,
20098       smartLists: false,
20099       silent: false,
20100       highlight: null,
20101       langPrefix: 'lang-',
20102       smartypants: false,
20103       headerPrefix: '',
20104       renderer: new Renderer,
20105       xhtml: false
20106     };
20107     
20108     /**
20109      * Expose
20110      */
20111     
20112     marked.Parser = Parser;
20113     marked.parser = Parser.parse;
20114     
20115     marked.Renderer = Renderer;
20116     
20117     marked.Lexer = Lexer;
20118     marked.lexer = Lexer.lex;
20119     
20120     marked.InlineLexer = InlineLexer;
20121     marked.inlineLexer = InlineLexer.output;
20122     
20123     marked.parse = marked;
20124     
20125     Roo.Markdown.marked = marked;
20126
20127 })();/*
20128  * Based on:
20129  * Ext JS Library 1.1.1
20130  * Copyright(c) 2006-2007, Ext JS, LLC.
20131  *
20132  * Originally Released Under LGPL - original licence link has changed is not relivant.
20133  *
20134  * Fork - LGPL
20135  * <script type="text/javascript">
20136  */
20137
20138
20139
20140 /*
20141  * These classes are derivatives of the similarly named classes in the YUI Library.
20142  * The original license:
20143  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
20144  * Code licensed under the BSD License:
20145  * http://developer.yahoo.net/yui/license.txt
20146  */
20147
20148 (function() {
20149
20150 var Event=Roo.EventManager;
20151 var Dom=Roo.lib.Dom;
20152
20153 /**
20154  * @class Roo.dd.DragDrop
20155  * @extends Roo.util.Observable
20156  * Defines the interface and base operation of items that that can be
20157  * dragged or can be drop targets.  It was designed to be extended, overriding
20158  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
20159  * Up to three html elements can be associated with a DragDrop instance:
20160  * <ul>
20161  * <li>linked element: the element that is passed into the constructor.
20162  * This is the element which defines the boundaries for interaction with
20163  * other DragDrop objects.</li>
20164  * <li>handle element(s): The drag operation only occurs if the element that
20165  * was clicked matches a handle element.  By default this is the linked
20166  * element, but there are times that you will want only a portion of the
20167  * linked element to initiate the drag operation, and the setHandleElId()
20168  * method provides a way to define this.</li>
20169  * <li>drag element: this represents the element that would be moved along
20170  * with the cursor during a drag operation.  By default, this is the linked
20171  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
20172  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
20173  * </li>
20174  * </ul>
20175  * This class should not be instantiated until the onload event to ensure that
20176  * the associated elements are available.
20177  * The following would define a DragDrop obj that would interact with any
20178  * other DragDrop obj in the "group1" group:
20179  * <pre>
20180  *  dd = new Roo.dd.DragDrop("div1", "group1");
20181  * </pre>
20182  * Since none of the event handlers have been implemented, nothing would
20183  * actually happen if you were to run the code above.  Normally you would
20184  * override this class or one of the default implementations, but you can
20185  * also override the methods you want on an instance of the class...
20186  * <pre>
20187  *  dd.onDragDrop = function(e, id) {
20188  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
20189  *  }
20190  * </pre>
20191  * @constructor
20192  * @param {String} id of the element that is linked to this instance
20193  * @param {String} sGroup the group of related DragDrop objects
20194  * @param {object} config an object containing configurable attributes
20195  *                Valid properties for DragDrop:
20196  *                    padding, isTarget, maintainOffset, primaryButtonOnly
20197  */
20198 Roo.dd.DragDrop = function(id, sGroup, config) {
20199     if (id) {
20200         this.init(id, sGroup, config);
20201     }
20202     
20203 };
20204
20205 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20206
20207     /**
20208      * The id of the element associated with this object.  This is what we
20209      * refer to as the "linked element" because the size and position of
20210      * this element is used to determine when the drag and drop objects have
20211      * interacted.
20212      * @property id
20213      * @type String
20214      */
20215     id: null,
20216
20217     /**
20218      * Configuration attributes passed into the constructor
20219      * @property config
20220      * @type object
20221      */
20222     config: null,
20223
20224     /**
20225      * The id of the element that will be dragged.  By default this is same
20226      * as the linked element , but could be changed to another element. Ex:
20227      * Roo.dd.DDProxy
20228      * @property dragElId
20229      * @type String
20230      * @private
20231      */
20232     dragElId: null,
20233
20234     /**
20235      * the id of the element that initiates the drag operation.  By default
20236      * this is the linked element, but could be changed to be a child of this
20237      * element.  This lets us do things like only starting the drag when the
20238      * header element within the linked html element is clicked.
20239      * @property handleElId
20240      * @type String
20241      * @private
20242      */
20243     handleElId: null,
20244
20245     /**
20246      * An associative array of HTML tags that will be ignored if clicked.
20247      * @property invalidHandleTypes
20248      * @type {string: string}
20249      */
20250     invalidHandleTypes: null,
20251
20252     /**
20253      * An associative array of ids for elements that will be ignored if clicked
20254      * @property invalidHandleIds
20255      * @type {string: string}
20256      */
20257     invalidHandleIds: null,
20258
20259     /**
20260      * An indexted array of css class names for elements that will be ignored
20261      * if clicked.
20262      * @property invalidHandleClasses
20263      * @type string[]
20264      */
20265     invalidHandleClasses: null,
20266
20267     /**
20268      * The linked element's absolute X position at the time the drag was
20269      * started
20270      * @property startPageX
20271      * @type int
20272      * @private
20273      */
20274     startPageX: 0,
20275
20276     /**
20277      * The linked element's absolute X position at the time the drag was
20278      * started
20279      * @property startPageY
20280      * @type int
20281      * @private
20282      */
20283     startPageY: 0,
20284
20285     /**
20286      * The group defines a logical collection of DragDrop objects that are
20287      * related.  Instances only get events when interacting with other
20288      * DragDrop object in the same group.  This lets us define multiple
20289      * groups using a single DragDrop subclass if we want.
20290      * @property groups
20291      * @type {string: string}
20292      */
20293     groups: null,
20294
20295     /**
20296      * Individual drag/drop instances can be locked.  This will prevent
20297      * onmousedown start drag.
20298      * @property locked
20299      * @type boolean
20300      * @private
20301      */
20302     locked: false,
20303
20304     /**
20305      * Lock this instance
20306      * @method lock
20307      */
20308     lock: function() { this.locked = true; },
20309
20310     /**
20311      * Unlock this instace
20312      * @method unlock
20313      */
20314     unlock: function() { this.locked = false; },
20315
20316     /**
20317      * By default, all insances can be a drop target.  This can be disabled by
20318      * setting isTarget to false.
20319      * @method isTarget
20320      * @type boolean
20321      */
20322     isTarget: true,
20323
20324     /**
20325      * The padding configured for this drag and drop object for calculating
20326      * the drop zone intersection with this object.
20327      * @method padding
20328      * @type int[]
20329      */
20330     padding: null,
20331
20332     /**
20333      * Cached reference to the linked element
20334      * @property _domRef
20335      * @private
20336      */
20337     _domRef: null,
20338
20339     /**
20340      * Internal typeof flag
20341      * @property __ygDragDrop
20342      * @private
20343      */
20344     __ygDragDrop: true,
20345
20346     /**
20347      * Set to true when horizontal contraints are applied
20348      * @property constrainX
20349      * @type boolean
20350      * @private
20351      */
20352     constrainX: false,
20353
20354     /**
20355      * Set to true when vertical contraints are applied
20356      * @property constrainY
20357      * @type boolean
20358      * @private
20359      */
20360     constrainY: false,
20361
20362     /**
20363      * The left constraint
20364      * @property minX
20365      * @type int
20366      * @private
20367      */
20368     minX: 0,
20369
20370     /**
20371      * The right constraint
20372      * @property maxX
20373      * @type int
20374      * @private
20375      */
20376     maxX: 0,
20377
20378     /**
20379      * The up constraint
20380      * @property minY
20381      * @type int
20382      * @type int
20383      * @private
20384      */
20385     minY: 0,
20386
20387     /**
20388      * The down constraint
20389      * @property maxY
20390      * @type int
20391      * @private
20392      */
20393     maxY: 0,
20394
20395     /**
20396      * Maintain offsets when we resetconstraints.  Set to true when you want
20397      * the position of the element relative to its parent to stay the same
20398      * when the page changes
20399      *
20400      * @property maintainOffset
20401      * @type boolean
20402      */
20403     maintainOffset: false,
20404
20405     /**
20406      * Array of pixel locations the element will snap to if we specified a
20407      * horizontal graduation/interval.  This array is generated automatically
20408      * when you define a tick interval.
20409      * @property xTicks
20410      * @type int[]
20411      */
20412     xTicks: null,
20413
20414     /**
20415      * Array of pixel locations the element will snap to if we specified a
20416      * vertical graduation/interval.  This array is generated automatically
20417      * when you define a tick interval.
20418      * @property yTicks
20419      * @type int[]
20420      */
20421     yTicks: null,
20422
20423     /**
20424      * By default the drag and drop instance will only respond to the primary
20425      * button click (left button for a right-handed mouse).  Set to true to
20426      * allow drag and drop to start with any mouse click that is propogated
20427      * by the browser
20428      * @property primaryButtonOnly
20429      * @type boolean
20430      */
20431     primaryButtonOnly: true,
20432
20433     /**
20434      * The availabe property is false until the linked dom element is accessible.
20435      * @property available
20436      * @type boolean
20437      */
20438     available: false,
20439
20440     /**
20441      * By default, drags can only be initiated if the mousedown occurs in the
20442      * region the linked element is.  This is done in part to work around a
20443      * bug in some browsers that mis-report the mousedown if the previous
20444      * mouseup happened outside of the window.  This property is set to true
20445      * if outer handles are defined.
20446      *
20447      * @property hasOuterHandles
20448      * @type boolean
20449      * @default false
20450      */
20451     hasOuterHandles: false,
20452
20453     /**
20454      * Code that executes immediately before the startDrag event
20455      * @method b4StartDrag
20456      * @private
20457      */
20458     b4StartDrag: function(x, y) { },
20459
20460     /**
20461      * Abstract method called after a drag/drop object is clicked
20462      * and the drag or mousedown time thresholds have beeen met.
20463      * @method startDrag
20464      * @param {int} X click location
20465      * @param {int} Y click location
20466      */
20467     startDrag: function(x, y) { /* override this */ },
20468
20469     /**
20470      * Code that executes immediately before the onDrag event
20471      * @method b4Drag
20472      * @private
20473      */
20474     b4Drag: function(e) { },
20475
20476     /**
20477      * Abstract method called during the onMouseMove event while dragging an
20478      * object.
20479      * @method onDrag
20480      * @param {Event} e the mousemove event
20481      */
20482     onDrag: function(e) { /* override this */ },
20483
20484     /**
20485      * Abstract method called when this element fist begins hovering over
20486      * another DragDrop obj
20487      * @method onDragEnter
20488      * @param {Event} e the mousemove event
20489      * @param {String|DragDrop[]} id In POINT mode, the element
20490      * id this is hovering over.  In INTERSECT mode, an array of one or more
20491      * dragdrop items being hovered over.
20492      */
20493     onDragEnter: function(e, id) { /* override this */ },
20494
20495     /**
20496      * Code that executes immediately before the onDragOver event
20497      * @method b4DragOver
20498      * @private
20499      */
20500     b4DragOver: function(e) { },
20501
20502     /**
20503      * Abstract method called when this element is hovering over another
20504      * DragDrop obj
20505      * @method onDragOver
20506      * @param {Event} e the mousemove event
20507      * @param {String|DragDrop[]} id In POINT mode, the element
20508      * id this is hovering over.  In INTERSECT mode, an array of dd items
20509      * being hovered over.
20510      */
20511     onDragOver: function(e, id) { /* override this */ },
20512
20513     /**
20514      * Code that executes immediately before the onDragOut event
20515      * @method b4DragOut
20516      * @private
20517      */
20518     b4DragOut: function(e) { },
20519
20520     /**
20521      * Abstract method called when we are no longer hovering over an element
20522      * @method onDragOut
20523      * @param {Event} e the mousemove event
20524      * @param {String|DragDrop[]} id In POINT mode, the element
20525      * id this was hovering over.  In INTERSECT mode, an array of dd items
20526      * that the mouse is no longer over.
20527      */
20528     onDragOut: function(e, id) { /* override this */ },
20529
20530     /**
20531      * Code that executes immediately before the onDragDrop event
20532      * @method b4DragDrop
20533      * @private
20534      */
20535     b4DragDrop: function(e) { },
20536
20537     /**
20538      * Abstract method called when this item is dropped on another DragDrop
20539      * obj
20540      * @method onDragDrop
20541      * @param {Event} e the mouseup event
20542      * @param {String|DragDrop[]} id In POINT mode, the element
20543      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20544      * was dropped on.
20545      */
20546     onDragDrop: function(e, id) { /* override this */ },
20547
20548     /**
20549      * Abstract method called when this item is dropped on an area with no
20550      * drop target
20551      * @method onInvalidDrop
20552      * @param {Event} e the mouseup event
20553      */
20554     onInvalidDrop: function(e) { /* override this */ },
20555
20556     /**
20557      * Code that executes immediately before the endDrag event
20558      * @method b4EndDrag
20559      * @private
20560      */
20561     b4EndDrag: function(e) { },
20562
20563     /**
20564      * Fired when we are done dragging the object
20565      * @method endDrag
20566      * @param {Event} e the mouseup event
20567      */
20568     endDrag: function(e) { /* override this */ },
20569
20570     /**
20571      * Code executed immediately before the onMouseDown event
20572      * @method b4MouseDown
20573      * @param {Event} e the mousedown event
20574      * @private
20575      */
20576     b4MouseDown: function(e) {  },
20577
20578     /**
20579      * Event handler that fires when a drag/drop obj gets a mousedown
20580      * @method onMouseDown
20581      * @param {Event} e the mousedown event
20582      */
20583     onMouseDown: function(e) { /* override this */ },
20584
20585     /**
20586      * Event handler that fires when a drag/drop obj gets a mouseup
20587      * @method onMouseUp
20588      * @param {Event} e the mouseup event
20589      */
20590     onMouseUp: function(e) { /* override this */ },
20591
20592     /**
20593      * Override the onAvailable method to do what is needed after the initial
20594      * position was determined.
20595      * @method onAvailable
20596      */
20597     onAvailable: function () {
20598     },
20599
20600     /*
20601      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20602      * @type Object
20603      */
20604     defaultPadding : {left:0, right:0, top:0, bottom:0},
20605
20606     /*
20607      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20608  *
20609  * Usage:
20610  <pre><code>
20611  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20612                 { dragElId: "existingProxyDiv" });
20613  dd.startDrag = function(){
20614      this.constrainTo("parent-id");
20615  };
20616  </code></pre>
20617  * Or you can initalize it using the {@link Roo.Element} object:
20618  <pre><code>
20619  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20620      startDrag : function(){
20621          this.constrainTo("parent-id");
20622      }
20623  });
20624  </code></pre>
20625      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20626      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20627      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20628      * an object containing the sides to pad. For example: {right:10, bottom:10}
20629      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20630      */
20631     constrainTo : function(constrainTo, pad, inContent){
20632         if(typeof pad == "number"){
20633             pad = {left: pad, right:pad, top:pad, bottom:pad};
20634         }
20635         pad = pad || this.defaultPadding;
20636         var b = Roo.get(this.getEl()).getBox();
20637         var ce = Roo.get(constrainTo);
20638         var s = ce.getScroll();
20639         var c, cd = ce.dom;
20640         if(cd == document.body){
20641             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20642         }else{
20643             xy = ce.getXY();
20644             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20645         }
20646
20647
20648         var topSpace = b.y - c.y;
20649         var leftSpace = b.x - c.x;
20650
20651         this.resetConstraints();
20652         this.setXConstraint(leftSpace - (pad.left||0), // left
20653                 c.width - leftSpace - b.width - (pad.right||0) //right
20654         );
20655         this.setYConstraint(topSpace - (pad.top||0), //top
20656                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20657         );
20658     },
20659
20660     /**
20661      * Returns a reference to the linked element
20662      * @method getEl
20663      * @return {HTMLElement} the html element
20664      */
20665     getEl: function() {
20666         if (!this._domRef) {
20667             this._domRef = Roo.getDom(this.id);
20668         }
20669
20670         return this._domRef;
20671     },
20672
20673     /**
20674      * Returns a reference to the actual element to drag.  By default this is
20675      * the same as the html element, but it can be assigned to another
20676      * element. An example of this can be found in Roo.dd.DDProxy
20677      * @method getDragEl
20678      * @return {HTMLElement} the html element
20679      */
20680     getDragEl: function() {
20681         return Roo.getDom(this.dragElId);
20682     },
20683
20684     /**
20685      * Sets up the DragDrop object.  Must be called in the constructor of any
20686      * Roo.dd.DragDrop subclass
20687      * @method init
20688      * @param id the id of the linked element
20689      * @param {String} sGroup the group of related items
20690      * @param {object} config configuration attributes
20691      */
20692     init: function(id, sGroup, config) {
20693         this.initTarget(id, sGroup, config);
20694         if (!Roo.isTouch) {
20695             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20696         }
20697         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20698         // Event.on(this.id, "selectstart", Event.preventDefault);
20699     },
20700
20701     /**
20702      * Initializes Targeting functionality only... the object does not
20703      * get a mousedown handler.
20704      * @method initTarget
20705      * @param id the id of the linked element
20706      * @param {String} sGroup the group of related items
20707      * @param {object} config configuration attributes
20708      */
20709     initTarget: function(id, sGroup, config) {
20710
20711         // configuration attributes
20712         this.config = config || {};
20713
20714         // create a local reference to the drag and drop manager
20715         this.DDM = Roo.dd.DDM;
20716         // initialize the groups array
20717         this.groups = {};
20718
20719         // assume that we have an element reference instead of an id if the
20720         // parameter is not a string
20721         if (typeof id !== "string") {
20722             id = Roo.id(id);
20723         }
20724
20725         // set the id
20726         this.id = id;
20727
20728         // add to an interaction group
20729         this.addToGroup((sGroup) ? sGroup : "default");
20730
20731         // We don't want to register this as the handle with the manager
20732         // so we just set the id rather than calling the setter.
20733         this.handleElId = id;
20734
20735         // the linked element is the element that gets dragged by default
20736         this.setDragElId(id);
20737
20738         // by default, clicked anchors will not start drag operations.
20739         this.invalidHandleTypes = { A: "A" };
20740         this.invalidHandleIds = {};
20741         this.invalidHandleClasses = [];
20742
20743         this.applyConfig();
20744
20745         this.handleOnAvailable();
20746     },
20747
20748     /**
20749      * Applies the configuration parameters that were passed into the constructor.
20750      * This is supposed to happen at each level through the inheritance chain.  So
20751      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20752      * DragDrop in order to get all of the parameters that are available in
20753      * each object.
20754      * @method applyConfig
20755      */
20756     applyConfig: function() {
20757
20758         // configurable properties:
20759         //    padding, isTarget, maintainOffset, primaryButtonOnly
20760         this.padding           = this.config.padding || [0, 0, 0, 0];
20761         this.isTarget          = (this.config.isTarget !== false);
20762         this.maintainOffset    = (this.config.maintainOffset);
20763         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20764
20765     },
20766
20767     /**
20768      * Executed when the linked element is available
20769      * @method handleOnAvailable
20770      * @private
20771      */
20772     handleOnAvailable: function() {
20773         this.available = true;
20774         this.resetConstraints();
20775         this.onAvailable();
20776     },
20777
20778      /**
20779      * Configures the padding for the target zone in px.  Effectively expands
20780      * (or reduces) the virtual object size for targeting calculations.
20781      * Supports css-style shorthand; if only one parameter is passed, all sides
20782      * will have that padding, and if only two are passed, the top and bottom
20783      * will have the first param, the left and right the second.
20784      * @method setPadding
20785      * @param {int} iTop    Top pad
20786      * @param {int} iRight  Right pad
20787      * @param {int} iBot    Bot pad
20788      * @param {int} iLeft   Left pad
20789      */
20790     setPadding: function(iTop, iRight, iBot, iLeft) {
20791         // this.padding = [iLeft, iRight, iTop, iBot];
20792         if (!iRight && 0 !== iRight) {
20793             this.padding = [iTop, iTop, iTop, iTop];
20794         } else if (!iBot && 0 !== iBot) {
20795             this.padding = [iTop, iRight, iTop, iRight];
20796         } else {
20797             this.padding = [iTop, iRight, iBot, iLeft];
20798         }
20799     },
20800
20801     /**
20802      * Stores the initial placement of the linked element.
20803      * @method setInitialPosition
20804      * @param {int} diffX   the X offset, default 0
20805      * @param {int} diffY   the Y offset, default 0
20806      */
20807     setInitPosition: function(diffX, diffY) {
20808         var el = this.getEl();
20809
20810         if (!this.DDM.verifyEl(el)) {
20811             return;
20812         }
20813
20814         var dx = diffX || 0;
20815         var dy = diffY || 0;
20816
20817         var p = Dom.getXY( el );
20818
20819         this.initPageX = p[0] - dx;
20820         this.initPageY = p[1] - dy;
20821
20822         this.lastPageX = p[0];
20823         this.lastPageY = p[1];
20824
20825
20826         this.setStartPosition(p);
20827     },
20828
20829     /**
20830      * Sets the start position of the element.  This is set when the obj
20831      * is initialized, the reset when a drag is started.
20832      * @method setStartPosition
20833      * @param pos current position (from previous lookup)
20834      * @private
20835      */
20836     setStartPosition: function(pos) {
20837         var p = pos || Dom.getXY( this.getEl() );
20838         this.deltaSetXY = null;
20839
20840         this.startPageX = p[0];
20841         this.startPageY = p[1];
20842     },
20843
20844     /**
20845      * Add this instance to a group of related drag/drop objects.  All
20846      * instances belong to at least one group, and can belong to as many
20847      * groups as needed.
20848      * @method addToGroup
20849      * @param sGroup {string} the name of the group
20850      */
20851     addToGroup: function(sGroup) {
20852         this.groups[sGroup] = true;
20853         this.DDM.regDragDrop(this, sGroup);
20854     },
20855
20856     /**
20857      * Remove's this instance from the supplied interaction group
20858      * @method removeFromGroup
20859      * @param {string}  sGroup  The group to drop
20860      */
20861     removeFromGroup: function(sGroup) {
20862         if (this.groups[sGroup]) {
20863             delete this.groups[sGroup];
20864         }
20865
20866         this.DDM.removeDDFromGroup(this, sGroup);
20867     },
20868
20869     /**
20870      * Allows you to specify that an element other than the linked element
20871      * will be moved with the cursor during a drag
20872      * @method setDragElId
20873      * @param id {string} the id of the element that will be used to initiate the drag
20874      */
20875     setDragElId: function(id) {
20876         this.dragElId = id;
20877     },
20878
20879     /**
20880      * Allows you to specify a child of the linked element that should be
20881      * used to initiate the drag operation.  An example of this would be if
20882      * you have a content div with text and links.  Clicking anywhere in the
20883      * content area would normally start the drag operation.  Use this method
20884      * to specify that an element inside of the content div is the element
20885      * that starts the drag operation.
20886      * @method setHandleElId
20887      * @param id {string} the id of the element that will be used to
20888      * initiate the drag.
20889      */
20890     setHandleElId: function(id) {
20891         if (typeof id !== "string") {
20892             id = Roo.id(id);
20893         }
20894         this.handleElId = id;
20895         this.DDM.regHandle(this.id, id);
20896     },
20897
20898     /**
20899      * Allows you to set an element outside of the linked element as a drag
20900      * handle
20901      * @method setOuterHandleElId
20902      * @param id the id of the element that will be used to initiate the drag
20903      */
20904     setOuterHandleElId: function(id) {
20905         if (typeof id !== "string") {
20906             id = Roo.id(id);
20907         }
20908         Event.on(id, "mousedown",
20909                 this.handleMouseDown, this);
20910         this.setHandleElId(id);
20911
20912         this.hasOuterHandles = true;
20913     },
20914
20915     /**
20916      * Remove all drag and drop hooks for this element
20917      * @method unreg
20918      */
20919     unreg: function() {
20920         Event.un(this.id, "mousedown",
20921                 this.handleMouseDown);
20922         Event.un(this.id, "touchstart",
20923                 this.handleMouseDown);
20924         this._domRef = null;
20925         this.DDM._remove(this);
20926     },
20927
20928     destroy : function(){
20929         this.unreg();
20930     },
20931
20932     /**
20933      * Returns true if this instance is locked, or the drag drop mgr is locked
20934      * (meaning that all drag/drop is disabled on the page.)
20935      * @method isLocked
20936      * @return {boolean} true if this obj or all drag/drop is locked, else
20937      * false
20938      */
20939     isLocked: function() {
20940         return (this.DDM.isLocked() || this.locked);
20941     },
20942
20943     /**
20944      * Fired when this object is clicked
20945      * @method handleMouseDown
20946      * @param {Event} e
20947      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20948      * @private
20949      */
20950     handleMouseDown: function(e, oDD){
20951      
20952         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20953             //Roo.log('not touch/ button !=0');
20954             return;
20955         }
20956         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20957             return; // double touch..
20958         }
20959         
20960
20961         if (this.isLocked()) {
20962             //Roo.log('locked');
20963             return;
20964         }
20965
20966         this.DDM.refreshCache(this.groups);
20967 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20968         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20969         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20970             //Roo.log('no outer handes or not over target');
20971                 // do nothing.
20972         } else {
20973 //            Roo.log('check validator');
20974             if (this.clickValidator(e)) {
20975 //                Roo.log('validate success');
20976                 // set the initial element position
20977                 this.setStartPosition();
20978
20979
20980                 this.b4MouseDown(e);
20981                 this.onMouseDown(e);
20982
20983                 this.DDM.handleMouseDown(e, this);
20984
20985                 this.DDM.stopEvent(e);
20986             } else {
20987
20988
20989             }
20990         }
20991     },
20992
20993     clickValidator: function(e) {
20994         var target = e.getTarget();
20995         return ( this.isValidHandleChild(target) &&
20996                     (this.id == this.handleElId ||
20997                         this.DDM.handleWasClicked(target, this.id)) );
20998     },
20999
21000     /**
21001      * Allows you to specify a tag name that should not start a drag operation
21002      * when clicked.  This is designed to facilitate embedding links within a
21003      * drag handle that do something other than start the drag.
21004      * @method addInvalidHandleType
21005      * @param {string} tagName the type of element to exclude
21006      */
21007     addInvalidHandleType: function(tagName) {
21008         var type = tagName.toUpperCase();
21009         this.invalidHandleTypes[type] = type;
21010     },
21011
21012     /**
21013      * Lets you to specify an element id for a child of a drag handle
21014      * that should not initiate a drag
21015      * @method addInvalidHandleId
21016      * @param {string} id the element id of the element you wish to ignore
21017      */
21018     addInvalidHandleId: function(id) {
21019         if (typeof id !== "string") {
21020             id = Roo.id(id);
21021         }
21022         this.invalidHandleIds[id] = id;
21023     },
21024
21025     /**
21026      * Lets you specify a css class of elements that will not initiate a drag
21027      * @method addInvalidHandleClass
21028      * @param {string} cssClass the class of the elements you wish to ignore
21029      */
21030     addInvalidHandleClass: function(cssClass) {
21031         this.invalidHandleClasses.push(cssClass);
21032     },
21033
21034     /**
21035      * Unsets an excluded tag name set by addInvalidHandleType
21036      * @method removeInvalidHandleType
21037      * @param {string} tagName the type of element to unexclude
21038      */
21039     removeInvalidHandleType: function(tagName) {
21040         var type = tagName.toUpperCase();
21041         // this.invalidHandleTypes[type] = null;
21042         delete this.invalidHandleTypes[type];
21043     },
21044
21045     /**
21046      * Unsets an invalid handle id
21047      * @method removeInvalidHandleId
21048      * @param {string} id the id of the element to re-enable
21049      */
21050     removeInvalidHandleId: function(id) {
21051         if (typeof id !== "string") {
21052             id = Roo.id(id);
21053         }
21054         delete this.invalidHandleIds[id];
21055     },
21056
21057     /**
21058      * Unsets an invalid css class
21059      * @method removeInvalidHandleClass
21060      * @param {string} cssClass the class of the element(s) you wish to
21061      * re-enable
21062      */
21063     removeInvalidHandleClass: function(cssClass) {
21064         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
21065             if (this.invalidHandleClasses[i] == cssClass) {
21066                 delete this.invalidHandleClasses[i];
21067             }
21068         }
21069     },
21070
21071     /**
21072      * Checks the tag exclusion list to see if this click should be ignored
21073      * @method isValidHandleChild
21074      * @param {HTMLElement} node the HTMLElement to evaluate
21075      * @return {boolean} true if this is a valid tag type, false if not
21076      */
21077     isValidHandleChild: function(node) {
21078
21079         var valid = true;
21080         // var n = (node.nodeName == "#text") ? node.parentNode : node;
21081         var nodeName;
21082         try {
21083             nodeName = node.nodeName.toUpperCase();
21084         } catch(e) {
21085             nodeName = node.nodeName;
21086         }
21087         valid = valid && !this.invalidHandleTypes[nodeName];
21088         valid = valid && !this.invalidHandleIds[node.id];
21089
21090         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
21091             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
21092         }
21093
21094
21095         return valid;
21096
21097     },
21098
21099     /**
21100      * Create the array of horizontal tick marks if an interval was specified
21101      * in setXConstraint().
21102      * @method setXTicks
21103      * @private
21104      */
21105     setXTicks: function(iStartX, iTickSize) {
21106         this.xTicks = [];
21107         this.xTickSize = iTickSize;
21108
21109         var tickMap = {};
21110
21111         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
21112             if (!tickMap[i]) {
21113                 this.xTicks[this.xTicks.length] = i;
21114                 tickMap[i] = true;
21115             }
21116         }
21117
21118         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
21119             if (!tickMap[i]) {
21120                 this.xTicks[this.xTicks.length] = i;
21121                 tickMap[i] = true;
21122             }
21123         }
21124
21125         this.xTicks.sort(this.DDM.numericSort) ;
21126     },
21127
21128     /**
21129      * Create the array of vertical tick marks if an interval was specified in
21130      * setYConstraint().
21131      * @method setYTicks
21132      * @private
21133      */
21134     setYTicks: function(iStartY, iTickSize) {
21135         this.yTicks = [];
21136         this.yTickSize = iTickSize;
21137
21138         var tickMap = {};
21139
21140         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
21141             if (!tickMap[i]) {
21142                 this.yTicks[this.yTicks.length] = i;
21143                 tickMap[i] = true;
21144             }
21145         }
21146
21147         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
21148             if (!tickMap[i]) {
21149                 this.yTicks[this.yTicks.length] = i;
21150                 tickMap[i] = true;
21151             }
21152         }
21153
21154         this.yTicks.sort(this.DDM.numericSort) ;
21155     },
21156
21157     /**
21158      * By default, the element can be dragged any place on the screen.  Use
21159      * this method to limit the horizontal travel of the element.  Pass in
21160      * 0,0 for the parameters if you want to lock the drag to the y axis.
21161      * @method setXConstraint
21162      * @param {int} iLeft the number of pixels the element can move to the left
21163      * @param {int} iRight the number of pixels the element can move to the
21164      * right
21165      * @param {int} iTickSize optional parameter for specifying that the
21166      * element
21167      * should move iTickSize pixels at a time.
21168      */
21169     setXConstraint: function(iLeft, iRight, iTickSize) {
21170         this.leftConstraint = iLeft;
21171         this.rightConstraint = iRight;
21172
21173         this.minX = this.initPageX - iLeft;
21174         this.maxX = this.initPageX + iRight;
21175         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
21176
21177         this.constrainX = true;
21178     },
21179
21180     /**
21181      * Clears any constraints applied to this instance.  Also clears ticks
21182      * since they can't exist independent of a constraint at this time.
21183      * @method clearConstraints
21184      */
21185     clearConstraints: function() {
21186         this.constrainX = false;
21187         this.constrainY = false;
21188         this.clearTicks();
21189     },
21190
21191     /**
21192      * Clears any tick interval defined for this instance
21193      * @method clearTicks
21194      */
21195     clearTicks: function() {
21196         this.xTicks = null;
21197         this.yTicks = null;
21198         this.xTickSize = 0;
21199         this.yTickSize = 0;
21200     },
21201
21202     /**
21203      * By default, the element can be dragged any place on the screen.  Set
21204      * this to limit the vertical travel of the element.  Pass in 0,0 for the
21205      * parameters if you want to lock the drag to the x axis.
21206      * @method setYConstraint
21207      * @param {int} iUp the number of pixels the element can move up
21208      * @param {int} iDown the number of pixels the element can move down
21209      * @param {int} iTickSize optional parameter for specifying that the
21210      * element should move iTickSize pixels at a time.
21211      */
21212     setYConstraint: function(iUp, iDown, iTickSize) {
21213         this.topConstraint = iUp;
21214         this.bottomConstraint = iDown;
21215
21216         this.minY = this.initPageY - iUp;
21217         this.maxY = this.initPageY + iDown;
21218         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21219
21220         this.constrainY = true;
21221
21222     },
21223
21224     /**
21225      * resetConstraints must be called if you manually reposition a dd element.
21226      * @method resetConstraints
21227      * @param {boolean} maintainOffset
21228      */
21229     resetConstraints: function() {
21230
21231
21232         // Maintain offsets if necessary
21233         if (this.initPageX || this.initPageX === 0) {
21234             // figure out how much this thing has moved
21235             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21236             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21237
21238             this.setInitPosition(dx, dy);
21239
21240         // This is the first time we have detected the element's position
21241         } else {
21242             this.setInitPosition();
21243         }
21244
21245         if (this.constrainX) {
21246             this.setXConstraint( this.leftConstraint,
21247                                  this.rightConstraint,
21248                                  this.xTickSize        );
21249         }
21250
21251         if (this.constrainY) {
21252             this.setYConstraint( this.topConstraint,
21253                                  this.bottomConstraint,
21254                                  this.yTickSize         );
21255         }
21256     },
21257
21258     /**
21259      * Normally the drag element is moved pixel by pixel, but we can specify
21260      * that it move a number of pixels at a time.  This method resolves the
21261      * location when we have it set up like this.
21262      * @method getTick
21263      * @param {int} val where we want to place the object
21264      * @param {int[]} tickArray sorted array of valid points
21265      * @return {int} the closest tick
21266      * @private
21267      */
21268     getTick: function(val, tickArray) {
21269
21270         if (!tickArray) {
21271             // If tick interval is not defined, it is effectively 1 pixel,
21272             // so we return the value passed to us.
21273             return val;
21274         } else if (tickArray[0] >= val) {
21275             // The value is lower than the first tick, so we return the first
21276             // tick.
21277             return tickArray[0];
21278         } else {
21279             for (var i=0, len=tickArray.length; i<len; ++i) {
21280                 var next = i + 1;
21281                 if (tickArray[next] && tickArray[next] >= val) {
21282                     var diff1 = val - tickArray[i];
21283                     var diff2 = tickArray[next] - val;
21284                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21285                 }
21286             }
21287
21288             // The value is larger than the last tick, so we return the last
21289             // tick.
21290             return tickArray[tickArray.length - 1];
21291         }
21292     },
21293
21294     /**
21295      * toString method
21296      * @method toString
21297      * @return {string} string representation of the dd obj
21298      */
21299     toString: function() {
21300         return ("DragDrop " + this.id);
21301     }
21302
21303 });
21304
21305 })();
21306 /*
21307  * Based on:
21308  * Ext JS Library 1.1.1
21309  * Copyright(c) 2006-2007, Ext JS, LLC.
21310  *
21311  * Originally Released Under LGPL - original licence link has changed is not relivant.
21312  *
21313  * Fork - LGPL
21314  * <script type="text/javascript">
21315  */
21316
21317
21318 /**
21319  * The drag and drop utility provides a framework for building drag and drop
21320  * applications.  In addition to enabling drag and drop for specific elements,
21321  * the drag and drop elements are tracked by the manager class, and the
21322  * interactions between the various elements are tracked during the drag and
21323  * the implementing code is notified about these important moments.
21324  */
21325
21326 // Only load the library once.  Rewriting the manager class would orphan
21327 // existing drag and drop instances.
21328 if (!Roo.dd.DragDropMgr) {
21329
21330 /**
21331  * @class Roo.dd.DragDropMgr
21332  * DragDropMgr is a singleton that tracks the element interaction for
21333  * all DragDrop items in the window.  Generally, you will not call
21334  * this class directly, but it does have helper methods that could
21335  * be useful in your DragDrop implementations.
21336  * @static
21337  */
21338 Roo.dd.DragDropMgr = function() {
21339
21340     var Event = Roo.EventManager;
21341
21342     return {
21343
21344         /**
21345          * Two dimensional Array of registered DragDrop objects.  The first
21346          * dimension is the DragDrop item group, the second the DragDrop
21347          * object.
21348          * @property ids
21349          * @type {string: string}
21350          * @private
21351          * @static
21352          */
21353         ids: {},
21354
21355         /**
21356          * Array of element ids defined as drag handles.  Used to determine
21357          * if the element that generated the mousedown event is actually the
21358          * handle and not the html element itself.
21359          * @property handleIds
21360          * @type {string: string}
21361          * @private
21362          * @static
21363          */
21364         handleIds: {},
21365
21366         /**
21367          * the DragDrop object that is currently being dragged
21368          * @property dragCurrent
21369          * @type DragDrop
21370          * @private
21371          * @static
21372          **/
21373         dragCurrent: null,
21374
21375         /**
21376          * the DragDrop object(s) that are being hovered over
21377          * @property dragOvers
21378          * @type Array
21379          * @private
21380          * @static
21381          */
21382         dragOvers: {},
21383
21384         /**
21385          * the X distance between the cursor and the object being dragged
21386          * @property deltaX
21387          * @type int
21388          * @private
21389          * @static
21390          */
21391         deltaX: 0,
21392
21393         /**
21394          * the Y distance between the cursor and the object being dragged
21395          * @property deltaY
21396          * @type int
21397          * @private
21398          * @static
21399          */
21400         deltaY: 0,
21401
21402         /**
21403          * Flag to determine if we should prevent the default behavior of the
21404          * events we define. By default this is true, but this can be set to
21405          * false if you need the default behavior (not recommended)
21406          * @property preventDefault
21407          * @type boolean
21408          * @static
21409          */
21410         preventDefault: true,
21411
21412         /**
21413          * Flag to determine if we should stop the propagation of the events
21414          * we generate. This is true by default but you may want to set it to
21415          * false if the html element contains other features that require the
21416          * mouse click.
21417          * @property stopPropagation
21418          * @type boolean
21419          * @static
21420          */
21421         stopPropagation: true,
21422
21423         /**
21424          * Internal flag that is set to true when drag and drop has been
21425          * intialized
21426          * @property initialized
21427          * @private
21428          * @static
21429          */
21430         initalized: false,
21431
21432         /**
21433          * All drag and drop can be disabled.
21434          * @property locked
21435          * @private
21436          * @static
21437          */
21438         locked: false,
21439
21440         /**
21441          * Called the first time an element is registered.
21442          * @method init
21443          * @private
21444          * @static
21445          */
21446         init: function() {
21447             this.initialized = true;
21448         },
21449
21450         /**
21451          * In point mode, drag and drop interaction is defined by the
21452          * location of the cursor during the drag/drop
21453          * @property POINT
21454          * @type int
21455          * @static
21456          */
21457         POINT: 0,
21458
21459         /**
21460          * In intersect mode, drag and drop interactio nis defined by the
21461          * overlap of two or more drag and drop objects.
21462          * @property INTERSECT
21463          * @type int
21464          * @static
21465          */
21466         INTERSECT: 1,
21467
21468         /**
21469          * The current drag and drop mode.  Default: POINT
21470          * @property mode
21471          * @type int
21472          * @static
21473          */
21474         mode: 0,
21475
21476         /**
21477          * Runs method on all drag and drop objects
21478          * @method _execOnAll
21479          * @private
21480          * @static
21481          */
21482         _execOnAll: function(sMethod, args) {
21483             for (var i in this.ids) {
21484                 for (var j in this.ids[i]) {
21485                     var oDD = this.ids[i][j];
21486                     if (! this.isTypeOfDD(oDD)) {
21487                         continue;
21488                     }
21489                     oDD[sMethod].apply(oDD, args);
21490                 }
21491             }
21492         },
21493
21494         /**
21495          * Drag and drop initialization.  Sets up the global event handlers
21496          * @method _onLoad
21497          * @private
21498          * @static
21499          */
21500         _onLoad: function() {
21501
21502             this.init();
21503
21504             if (!Roo.isTouch) {
21505                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21506                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21507             }
21508             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21509             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21510             
21511             Event.on(window,   "unload",    this._onUnload, this, true);
21512             Event.on(window,   "resize",    this._onResize, this, true);
21513             // Event.on(window,   "mouseout",    this._test);
21514
21515         },
21516
21517         /**
21518          * Reset constraints on all drag and drop objs
21519          * @method _onResize
21520          * @private
21521          * @static
21522          */
21523         _onResize: function(e) {
21524             this._execOnAll("resetConstraints", []);
21525         },
21526
21527         /**
21528          * Lock all drag and drop functionality
21529          * @method lock
21530          * @static
21531          */
21532         lock: function() { this.locked = true; },
21533
21534         /**
21535          * Unlock all drag and drop functionality
21536          * @method unlock
21537          * @static
21538          */
21539         unlock: function() { this.locked = false; },
21540
21541         /**
21542          * Is drag and drop locked?
21543          * @method isLocked
21544          * @return {boolean} True if drag and drop is locked, false otherwise.
21545          * @static
21546          */
21547         isLocked: function() { return this.locked; },
21548
21549         /**
21550          * Location cache that is set for all drag drop objects when a drag is
21551          * initiated, cleared when the drag is finished.
21552          * @property locationCache
21553          * @private
21554          * @static
21555          */
21556         locationCache: {},
21557
21558         /**
21559          * Set useCache to false if you want to force object the lookup of each
21560          * drag and drop linked element constantly during a drag.
21561          * @property useCache
21562          * @type boolean
21563          * @static
21564          */
21565         useCache: true,
21566
21567         /**
21568          * The number of pixels that the mouse needs to move after the
21569          * mousedown before the drag is initiated.  Default=3;
21570          * @property clickPixelThresh
21571          * @type int
21572          * @static
21573          */
21574         clickPixelThresh: 3,
21575
21576         /**
21577          * The number of milliseconds after the mousedown event to initiate the
21578          * drag if we don't get a mouseup event. Default=1000
21579          * @property clickTimeThresh
21580          * @type int
21581          * @static
21582          */
21583         clickTimeThresh: 350,
21584
21585         /**
21586          * Flag that indicates that either the drag pixel threshold or the
21587          * mousdown time threshold has been met
21588          * @property dragThreshMet
21589          * @type boolean
21590          * @private
21591          * @static
21592          */
21593         dragThreshMet: false,
21594
21595         /**
21596          * Timeout used for the click time threshold
21597          * @property clickTimeout
21598          * @type Object
21599          * @private
21600          * @static
21601          */
21602         clickTimeout: null,
21603
21604         /**
21605          * The X position of the mousedown event stored for later use when a
21606          * drag threshold is met.
21607          * @property startX
21608          * @type int
21609          * @private
21610          * @static
21611          */
21612         startX: 0,
21613
21614         /**
21615          * The Y position of the mousedown event stored for later use when a
21616          * drag threshold is met.
21617          * @property startY
21618          * @type int
21619          * @private
21620          * @static
21621          */
21622         startY: 0,
21623
21624         /**
21625          * Each DragDrop instance must be registered with the DragDropMgr.
21626          * This is executed in DragDrop.init()
21627          * @method regDragDrop
21628          * @param {DragDrop} oDD the DragDrop object to register
21629          * @param {String} sGroup the name of the group this element belongs to
21630          * @static
21631          */
21632         regDragDrop: function(oDD, sGroup) {
21633             if (!this.initialized) { this.init(); }
21634
21635             if (!this.ids[sGroup]) {
21636                 this.ids[sGroup] = {};
21637             }
21638             this.ids[sGroup][oDD.id] = oDD;
21639         },
21640
21641         /**
21642          * Removes the supplied dd instance from the supplied group. Executed
21643          * by DragDrop.removeFromGroup, so don't call this function directly.
21644          * @method removeDDFromGroup
21645          * @private
21646          * @static
21647          */
21648         removeDDFromGroup: function(oDD, sGroup) {
21649             if (!this.ids[sGroup]) {
21650                 this.ids[sGroup] = {};
21651             }
21652
21653             var obj = this.ids[sGroup];
21654             if (obj && obj[oDD.id]) {
21655                 delete obj[oDD.id];
21656             }
21657         },
21658
21659         /**
21660          * Unregisters a drag and drop item.  This is executed in
21661          * DragDrop.unreg, use that method instead of calling this directly.
21662          * @method _remove
21663          * @private
21664          * @static
21665          */
21666         _remove: function(oDD) {
21667             for (var g in oDD.groups) {
21668                 if (g && this.ids[g][oDD.id]) {
21669                     delete this.ids[g][oDD.id];
21670                 }
21671             }
21672             delete this.handleIds[oDD.id];
21673         },
21674
21675         /**
21676          * Each DragDrop handle element must be registered.  This is done
21677          * automatically when executing DragDrop.setHandleElId()
21678          * @method regHandle
21679          * @param {String} sDDId the DragDrop id this element is a handle for
21680          * @param {String} sHandleId the id of the element that is the drag
21681          * handle
21682          * @static
21683          */
21684         regHandle: function(sDDId, sHandleId) {
21685             if (!this.handleIds[sDDId]) {
21686                 this.handleIds[sDDId] = {};
21687             }
21688             this.handleIds[sDDId][sHandleId] = sHandleId;
21689         },
21690
21691         /**
21692          * Utility function to determine if a given element has been
21693          * registered as a drag drop item.
21694          * @method isDragDrop
21695          * @param {String} id the element id to check
21696          * @return {boolean} true if this element is a DragDrop item,
21697          * false otherwise
21698          * @static
21699          */
21700         isDragDrop: function(id) {
21701             return ( this.getDDById(id) ) ? true : false;
21702         },
21703
21704         /**
21705          * Returns the drag and drop instances that are in all groups the
21706          * passed in instance belongs to.
21707          * @method getRelated
21708          * @param {DragDrop} p_oDD the obj to get related data for
21709          * @param {boolean} bTargetsOnly if true, only return targetable objs
21710          * @return {DragDrop[]} the related instances
21711          * @static
21712          */
21713         getRelated: function(p_oDD, bTargetsOnly) {
21714             var oDDs = [];
21715             for (var i in p_oDD.groups) {
21716                 for (j in this.ids[i]) {
21717                     var dd = this.ids[i][j];
21718                     if (! this.isTypeOfDD(dd)) {
21719                         continue;
21720                     }
21721                     if (!bTargetsOnly || dd.isTarget) {
21722                         oDDs[oDDs.length] = dd;
21723                     }
21724                 }
21725             }
21726
21727             return oDDs;
21728         },
21729
21730         /**
21731          * Returns true if the specified dd target is a legal target for
21732          * the specifice drag obj
21733          * @method isLegalTarget
21734          * @param {DragDrop} the drag obj
21735          * @param {DragDrop} the target
21736          * @return {boolean} true if the target is a legal target for the
21737          * dd obj
21738          * @static
21739          */
21740         isLegalTarget: function (oDD, oTargetDD) {
21741             var targets = this.getRelated(oDD, true);
21742             for (var i=0, len=targets.length;i<len;++i) {
21743                 if (targets[i].id == oTargetDD.id) {
21744                     return true;
21745                 }
21746             }
21747
21748             return false;
21749         },
21750
21751         /**
21752          * My goal is to be able to transparently determine if an object is
21753          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21754          * returns "object", oDD.constructor.toString() always returns
21755          * "DragDrop" and not the name of the subclass.  So for now it just
21756          * evaluates a well-known variable in DragDrop.
21757          * @method isTypeOfDD
21758          * @param {Object} the object to evaluate
21759          * @return {boolean} true if typeof oDD = DragDrop
21760          * @static
21761          */
21762         isTypeOfDD: function (oDD) {
21763             return (oDD && oDD.__ygDragDrop);
21764         },
21765
21766         /**
21767          * Utility function to determine if a given element has been
21768          * registered as a drag drop handle for the given Drag Drop object.
21769          * @method isHandle
21770          * @param {String} id the element id to check
21771          * @return {boolean} true if this element is a DragDrop handle, false
21772          * otherwise
21773          * @static
21774          */
21775         isHandle: function(sDDId, sHandleId) {
21776             return ( this.handleIds[sDDId] &&
21777                             this.handleIds[sDDId][sHandleId] );
21778         },
21779
21780         /**
21781          * Returns the DragDrop instance for a given id
21782          * @method getDDById
21783          * @param {String} id the id of the DragDrop object
21784          * @return {DragDrop} the drag drop object, null if it is not found
21785          * @static
21786          */
21787         getDDById: function(id) {
21788             for (var i in this.ids) {
21789                 if (this.ids[i][id]) {
21790                     return this.ids[i][id];
21791                 }
21792             }
21793             return null;
21794         },
21795
21796         /**
21797          * Fired after a registered DragDrop object gets the mousedown event.
21798          * Sets up the events required to track the object being dragged
21799          * @method handleMouseDown
21800          * @param {Event} e the event
21801          * @param oDD the DragDrop object being dragged
21802          * @private
21803          * @static
21804          */
21805         handleMouseDown: function(e, oDD) {
21806             if(Roo.QuickTips){
21807                 Roo.QuickTips.disable();
21808             }
21809             this.currentTarget = e.getTarget();
21810
21811             this.dragCurrent = oDD;
21812
21813             var el = oDD.getEl();
21814
21815             // track start position
21816             this.startX = e.getPageX();
21817             this.startY = e.getPageY();
21818
21819             this.deltaX = this.startX - el.offsetLeft;
21820             this.deltaY = this.startY - el.offsetTop;
21821
21822             this.dragThreshMet = false;
21823
21824             this.clickTimeout = setTimeout(
21825                     function() {
21826                         var DDM = Roo.dd.DDM;
21827                         DDM.startDrag(DDM.startX, DDM.startY);
21828                     },
21829                     this.clickTimeThresh );
21830         },
21831
21832         /**
21833          * Fired when either the drag pixel threshol or the mousedown hold
21834          * time threshold has been met.
21835          * @method startDrag
21836          * @param x {int} the X position of the original mousedown
21837          * @param y {int} the Y position of the original mousedown
21838          * @static
21839          */
21840         startDrag: function(x, y) {
21841             clearTimeout(this.clickTimeout);
21842             if (this.dragCurrent) {
21843                 this.dragCurrent.b4StartDrag(x, y);
21844                 this.dragCurrent.startDrag(x, y);
21845             }
21846             this.dragThreshMet = true;
21847         },
21848
21849         /**
21850          * Internal function to handle the mouseup event.  Will be invoked
21851          * from the context of the document.
21852          * @method handleMouseUp
21853          * @param {Event} e the event
21854          * @private
21855          * @static
21856          */
21857         handleMouseUp: function(e) {
21858
21859             if(Roo.QuickTips){
21860                 Roo.QuickTips.enable();
21861             }
21862             if (! this.dragCurrent) {
21863                 return;
21864             }
21865
21866             clearTimeout(this.clickTimeout);
21867
21868             if (this.dragThreshMet) {
21869                 this.fireEvents(e, true);
21870             } else {
21871             }
21872
21873             this.stopDrag(e);
21874
21875             this.stopEvent(e);
21876         },
21877
21878         /**
21879          * Utility to stop event propagation and event default, if these
21880          * features are turned on.
21881          * @method stopEvent
21882          * @param {Event} e the event as returned by this.getEvent()
21883          * @static
21884          */
21885         stopEvent: function(e){
21886             if(this.stopPropagation) {
21887                 e.stopPropagation();
21888             }
21889
21890             if (this.preventDefault) {
21891                 e.preventDefault();
21892             }
21893         },
21894
21895         /**
21896          * Internal function to clean up event handlers after the drag
21897          * operation is complete
21898          * @method stopDrag
21899          * @param {Event} e the event
21900          * @private
21901          * @static
21902          */
21903         stopDrag: function(e) {
21904             // Fire the drag end event for the item that was dragged
21905             if (this.dragCurrent) {
21906                 if (this.dragThreshMet) {
21907                     this.dragCurrent.b4EndDrag(e);
21908                     this.dragCurrent.endDrag(e);
21909                 }
21910
21911                 this.dragCurrent.onMouseUp(e);
21912             }
21913
21914             this.dragCurrent = null;
21915             this.dragOvers = {};
21916         },
21917
21918         /**
21919          * Internal function to handle the mousemove event.  Will be invoked
21920          * from the context of the html element.
21921          *
21922          * @TODO figure out what we can do about mouse events lost when the
21923          * user drags objects beyond the window boundary.  Currently we can
21924          * detect this in internet explorer by verifying that the mouse is
21925          * down during the mousemove event.  Firefox doesn't give us the
21926          * button state on the mousemove event.
21927          * @method handleMouseMove
21928          * @param {Event} e the event
21929          * @private
21930          * @static
21931          */
21932         handleMouseMove: function(e) {
21933             if (! this.dragCurrent) {
21934                 return true;
21935             }
21936
21937             // var button = e.which || e.button;
21938
21939             // check for IE mouseup outside of page boundary
21940             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21941                 this.stopEvent(e);
21942                 return this.handleMouseUp(e);
21943             }
21944
21945             if (!this.dragThreshMet) {
21946                 var diffX = Math.abs(this.startX - e.getPageX());
21947                 var diffY = Math.abs(this.startY - e.getPageY());
21948                 if (diffX > this.clickPixelThresh ||
21949                             diffY > this.clickPixelThresh) {
21950                     this.startDrag(this.startX, this.startY);
21951                 }
21952             }
21953
21954             if (this.dragThreshMet) {
21955                 this.dragCurrent.b4Drag(e);
21956                 this.dragCurrent.onDrag(e);
21957                 if(!this.dragCurrent.moveOnly){
21958                     this.fireEvents(e, false);
21959                 }
21960             }
21961
21962             this.stopEvent(e);
21963
21964             return true;
21965         },
21966
21967         /**
21968          * Iterates over all of the DragDrop elements to find ones we are
21969          * hovering over or dropping on
21970          * @method fireEvents
21971          * @param {Event} e the event
21972          * @param {boolean} isDrop is this a drop op or a mouseover op?
21973          * @private
21974          * @static
21975          */
21976         fireEvents: function(e, isDrop) {
21977             var dc = this.dragCurrent;
21978
21979             // If the user did the mouse up outside of the window, we could
21980             // get here even though we have ended the drag.
21981             if (!dc || dc.isLocked()) {
21982                 return;
21983             }
21984
21985             var pt = e.getPoint();
21986
21987             // cache the previous dragOver array
21988             var oldOvers = [];
21989
21990             var outEvts   = [];
21991             var overEvts  = [];
21992             var dropEvts  = [];
21993             var enterEvts = [];
21994
21995             // Check to see if the object(s) we were hovering over is no longer
21996             // being hovered over so we can fire the onDragOut event
21997             for (var i in this.dragOvers) {
21998
21999                 var ddo = this.dragOvers[i];
22000
22001                 if (! this.isTypeOfDD(ddo)) {
22002                     continue;
22003                 }
22004
22005                 if (! this.isOverTarget(pt, ddo, this.mode)) {
22006                     outEvts.push( ddo );
22007                 }
22008
22009                 oldOvers[i] = true;
22010                 delete this.dragOvers[i];
22011             }
22012
22013             for (var sGroup in dc.groups) {
22014
22015                 if ("string" != typeof sGroup) {
22016                     continue;
22017                 }
22018
22019                 for (i in this.ids[sGroup]) {
22020                     var oDD = this.ids[sGroup][i];
22021                     if (! this.isTypeOfDD(oDD)) {
22022                         continue;
22023                     }
22024
22025                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
22026                         if (this.isOverTarget(pt, oDD, this.mode)) {
22027                             // look for drop interactions
22028                             if (isDrop) {
22029                                 dropEvts.push( oDD );
22030                             // look for drag enter and drag over interactions
22031                             } else {
22032
22033                                 // initial drag over: dragEnter fires
22034                                 if (!oldOvers[oDD.id]) {
22035                                     enterEvts.push( oDD );
22036                                 // subsequent drag overs: dragOver fires
22037                                 } else {
22038                                     overEvts.push( oDD );
22039                                 }
22040
22041                                 this.dragOvers[oDD.id] = oDD;
22042                             }
22043                         }
22044                     }
22045                 }
22046             }
22047
22048             if (this.mode) {
22049                 if (outEvts.length) {
22050                     dc.b4DragOut(e, outEvts);
22051                     dc.onDragOut(e, outEvts);
22052                 }
22053
22054                 if (enterEvts.length) {
22055                     dc.onDragEnter(e, enterEvts);
22056                 }
22057
22058                 if (overEvts.length) {
22059                     dc.b4DragOver(e, overEvts);
22060                     dc.onDragOver(e, overEvts);
22061                 }
22062
22063                 if (dropEvts.length) {
22064                     dc.b4DragDrop(e, dropEvts);
22065                     dc.onDragDrop(e, dropEvts);
22066                 }
22067
22068             } else {
22069                 // fire dragout events
22070                 var len = 0;
22071                 for (i=0, len=outEvts.length; i<len; ++i) {
22072                     dc.b4DragOut(e, outEvts[i].id);
22073                     dc.onDragOut(e, outEvts[i].id);
22074                 }
22075
22076                 // fire enter events
22077                 for (i=0,len=enterEvts.length; i<len; ++i) {
22078                     // dc.b4DragEnter(e, oDD.id);
22079                     dc.onDragEnter(e, enterEvts[i].id);
22080                 }
22081
22082                 // fire over events
22083                 for (i=0,len=overEvts.length; i<len; ++i) {
22084                     dc.b4DragOver(e, overEvts[i].id);
22085                     dc.onDragOver(e, overEvts[i].id);
22086                 }
22087
22088                 // fire drop events
22089                 for (i=0, len=dropEvts.length; i<len; ++i) {
22090                     dc.b4DragDrop(e, dropEvts[i].id);
22091                     dc.onDragDrop(e, dropEvts[i].id);
22092                 }
22093
22094             }
22095
22096             // notify about a drop that did not find a target
22097             if (isDrop && !dropEvts.length) {
22098                 dc.onInvalidDrop(e);
22099             }
22100
22101         },
22102
22103         /**
22104          * Helper function for getting the best match from the list of drag
22105          * and drop objects returned by the drag and drop events when we are
22106          * in INTERSECT mode.  It returns either the first object that the
22107          * cursor is over, or the object that has the greatest overlap with
22108          * the dragged element.
22109          * @method getBestMatch
22110          * @param  {DragDrop[]} dds The array of drag and drop objects
22111          * targeted
22112          * @return {DragDrop}       The best single match
22113          * @static
22114          */
22115         getBestMatch: function(dds) {
22116             var winner = null;
22117             // Return null if the input is not what we expect
22118             //if (!dds || !dds.length || dds.length == 0) {
22119                // winner = null;
22120             // If there is only one item, it wins
22121             //} else if (dds.length == 1) {
22122
22123             var len = dds.length;
22124
22125             if (len == 1) {
22126                 winner = dds[0];
22127             } else {
22128                 // Loop through the targeted items
22129                 for (var i=0; i<len; ++i) {
22130                     var dd = dds[i];
22131                     // If the cursor is over the object, it wins.  If the
22132                     // cursor is over multiple matches, the first one we come
22133                     // to wins.
22134                     if (dd.cursorIsOver) {
22135                         winner = dd;
22136                         break;
22137                     // Otherwise the object with the most overlap wins
22138                     } else {
22139                         if (!winner ||
22140                             winner.overlap.getArea() < dd.overlap.getArea()) {
22141                             winner = dd;
22142                         }
22143                     }
22144                 }
22145             }
22146
22147             return winner;
22148         },
22149
22150         /**
22151          * Refreshes the cache of the top-left and bottom-right points of the
22152          * drag and drop objects in the specified group(s).  This is in the
22153          * format that is stored in the drag and drop instance, so typical
22154          * usage is:
22155          * <code>
22156          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
22157          * </code>
22158          * Alternatively:
22159          * <code>
22160          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
22161          * </code>
22162          * @TODO this really should be an indexed array.  Alternatively this
22163          * method could accept both.
22164          * @method refreshCache
22165          * @param {Object} groups an associative array of groups to refresh
22166          * @static
22167          */
22168         refreshCache: function(groups) {
22169             for (var sGroup in groups) {
22170                 if ("string" != typeof sGroup) {
22171                     continue;
22172                 }
22173                 for (var i in this.ids[sGroup]) {
22174                     var oDD = this.ids[sGroup][i];
22175
22176                     if (this.isTypeOfDD(oDD)) {
22177                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
22178                         var loc = this.getLocation(oDD);
22179                         if (loc) {
22180                             this.locationCache[oDD.id] = loc;
22181                         } else {
22182                             delete this.locationCache[oDD.id];
22183                             // this will unregister the drag and drop object if
22184                             // the element is not in a usable state
22185                             // oDD.unreg();
22186                         }
22187                     }
22188                 }
22189             }
22190         },
22191
22192         /**
22193          * This checks to make sure an element exists and is in the DOM.  The
22194          * main purpose is to handle cases where innerHTML is used to remove
22195          * drag and drop objects from the DOM.  IE provides an 'unspecified
22196          * error' when trying to access the offsetParent of such an element
22197          * @method verifyEl
22198          * @param {HTMLElement} el the element to check
22199          * @return {boolean} true if the element looks usable
22200          * @static
22201          */
22202         verifyEl: function(el) {
22203             if (el) {
22204                 var parent;
22205                 if(Roo.isIE){
22206                     try{
22207                         parent = el.offsetParent;
22208                     }catch(e){}
22209                 }else{
22210                     parent = el.offsetParent;
22211                 }
22212                 if (parent) {
22213                     return true;
22214                 }
22215             }
22216
22217             return false;
22218         },
22219
22220         /**
22221          * Returns a Region object containing the drag and drop element's position
22222          * and size, including the padding configured for it
22223          * @method getLocation
22224          * @param {DragDrop} oDD the drag and drop object to get the
22225          *                       location for
22226          * @return {Roo.lib.Region} a Region object representing the total area
22227          *                             the element occupies, including any padding
22228          *                             the instance is configured for.
22229          * @static
22230          */
22231         getLocation: function(oDD) {
22232             if (! this.isTypeOfDD(oDD)) {
22233                 return null;
22234             }
22235
22236             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22237
22238             try {
22239                 pos= Roo.lib.Dom.getXY(el);
22240             } catch (e) { }
22241
22242             if (!pos) {
22243                 return null;
22244             }
22245
22246             x1 = pos[0];
22247             x2 = x1 + el.offsetWidth;
22248             y1 = pos[1];
22249             y2 = y1 + el.offsetHeight;
22250
22251             t = y1 - oDD.padding[0];
22252             r = x2 + oDD.padding[1];
22253             b = y2 + oDD.padding[2];
22254             l = x1 - oDD.padding[3];
22255
22256             return new Roo.lib.Region( t, r, b, l );
22257         },
22258
22259         /**
22260          * Checks the cursor location to see if it over the target
22261          * @method isOverTarget
22262          * @param {Roo.lib.Point} pt The point to evaluate
22263          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22264          * @return {boolean} true if the mouse is over the target
22265          * @private
22266          * @static
22267          */
22268         isOverTarget: function(pt, oTarget, intersect) {
22269             // use cache if available
22270             var loc = this.locationCache[oTarget.id];
22271             if (!loc || !this.useCache) {
22272                 loc = this.getLocation(oTarget);
22273                 this.locationCache[oTarget.id] = loc;
22274
22275             }
22276
22277             if (!loc) {
22278                 return false;
22279             }
22280
22281             oTarget.cursorIsOver = loc.contains( pt );
22282
22283             // DragDrop is using this as a sanity check for the initial mousedown
22284             // in this case we are done.  In POINT mode, if the drag obj has no
22285             // contraints, we are also done. Otherwise we need to evaluate the
22286             // location of the target as related to the actual location of the
22287             // dragged element.
22288             var dc = this.dragCurrent;
22289             if (!dc || !dc.getTargetCoord ||
22290                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22291                 return oTarget.cursorIsOver;
22292             }
22293
22294             oTarget.overlap = null;
22295
22296             // Get the current location of the drag element, this is the
22297             // location of the mouse event less the delta that represents
22298             // where the original mousedown happened on the element.  We
22299             // need to consider constraints and ticks as well.
22300             var pos = dc.getTargetCoord(pt.x, pt.y);
22301
22302             var el = dc.getDragEl();
22303             var curRegion = new Roo.lib.Region( pos.y,
22304                                                    pos.x + el.offsetWidth,
22305                                                    pos.y + el.offsetHeight,
22306                                                    pos.x );
22307
22308             var overlap = curRegion.intersect(loc);
22309
22310             if (overlap) {
22311                 oTarget.overlap = overlap;
22312                 return (intersect) ? true : oTarget.cursorIsOver;
22313             } else {
22314                 return false;
22315             }
22316         },
22317
22318         /**
22319          * unload event handler
22320          * @method _onUnload
22321          * @private
22322          * @static
22323          */
22324         _onUnload: function(e, me) {
22325             Roo.dd.DragDropMgr.unregAll();
22326         },
22327
22328         /**
22329          * Cleans up the drag and drop events and objects.
22330          * @method unregAll
22331          * @private
22332          * @static
22333          */
22334         unregAll: function() {
22335
22336             if (this.dragCurrent) {
22337                 this.stopDrag();
22338                 this.dragCurrent = null;
22339             }
22340
22341             this._execOnAll("unreg", []);
22342
22343             for (i in this.elementCache) {
22344                 delete this.elementCache[i];
22345             }
22346
22347             this.elementCache = {};
22348             this.ids = {};
22349         },
22350
22351         /**
22352          * A cache of DOM elements
22353          * @property elementCache
22354          * @private
22355          * @static
22356          */
22357         elementCache: {},
22358
22359         /**
22360          * Get the wrapper for the DOM element specified
22361          * @method getElWrapper
22362          * @param {String} id the id of the element to get
22363          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22364          * @private
22365          * @deprecated This wrapper isn't that useful
22366          * @static
22367          */
22368         getElWrapper: function(id) {
22369             var oWrapper = this.elementCache[id];
22370             if (!oWrapper || !oWrapper.el) {
22371                 oWrapper = this.elementCache[id] =
22372                     new this.ElementWrapper(Roo.getDom(id));
22373             }
22374             return oWrapper;
22375         },
22376
22377         /**
22378          * Returns the actual DOM element
22379          * @method getElement
22380          * @param {String} id the id of the elment to get
22381          * @return {Object} The element
22382          * @deprecated use Roo.getDom instead
22383          * @static
22384          */
22385         getElement: function(id) {
22386             return Roo.getDom(id);
22387         },
22388
22389         /**
22390          * Returns the style property for the DOM element (i.e.,
22391          * document.getElById(id).style)
22392          * @method getCss
22393          * @param {String} id the id of the elment to get
22394          * @return {Object} The style property of the element
22395          * @deprecated use Roo.getDom instead
22396          * @static
22397          */
22398         getCss: function(id) {
22399             var el = Roo.getDom(id);
22400             return (el) ? el.style : null;
22401         },
22402
22403         /**
22404          * Inner class for cached elements
22405          * @class DragDropMgr.ElementWrapper
22406          * @for DragDropMgr
22407          * @private
22408          * @deprecated
22409          */
22410         ElementWrapper: function(el) {
22411                 /**
22412                  * The element
22413                  * @property el
22414                  */
22415                 this.el = el || null;
22416                 /**
22417                  * The element id
22418                  * @property id
22419                  */
22420                 this.id = this.el && el.id;
22421                 /**
22422                  * A reference to the style property
22423                  * @property css
22424                  */
22425                 this.css = this.el && el.style;
22426             },
22427
22428         /**
22429          * Returns the X position of an html element
22430          * @method getPosX
22431          * @param el the element for which to get the position
22432          * @return {int} the X coordinate
22433          * @for DragDropMgr
22434          * @deprecated use Roo.lib.Dom.getX instead
22435          * @static
22436          */
22437         getPosX: function(el) {
22438             return Roo.lib.Dom.getX(el);
22439         },
22440
22441         /**
22442          * Returns the Y position of an html element
22443          * @method getPosY
22444          * @param el the element for which to get the position
22445          * @return {int} the Y coordinate
22446          * @deprecated use Roo.lib.Dom.getY instead
22447          * @static
22448          */
22449         getPosY: function(el) {
22450             return Roo.lib.Dom.getY(el);
22451         },
22452
22453         /**
22454          * Swap two nodes.  In IE, we use the native method, for others we
22455          * emulate the IE behavior
22456          * @method swapNode
22457          * @param n1 the first node to swap
22458          * @param n2 the other node to swap
22459          * @static
22460          */
22461         swapNode: function(n1, n2) {
22462             if (n1.swapNode) {
22463                 n1.swapNode(n2);
22464             } else {
22465                 var p = n2.parentNode;
22466                 var s = n2.nextSibling;
22467
22468                 if (s == n1) {
22469                     p.insertBefore(n1, n2);
22470                 } else if (n2 == n1.nextSibling) {
22471                     p.insertBefore(n2, n1);
22472                 } else {
22473                     n1.parentNode.replaceChild(n2, n1);
22474                     p.insertBefore(n1, s);
22475                 }
22476             }
22477         },
22478
22479         /**
22480          * Returns the current scroll position
22481          * @method getScroll
22482          * @private
22483          * @static
22484          */
22485         getScroll: function () {
22486             var t, l, dde=document.documentElement, db=document.body;
22487             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22488                 t = dde.scrollTop;
22489                 l = dde.scrollLeft;
22490             } else if (db) {
22491                 t = db.scrollTop;
22492                 l = db.scrollLeft;
22493             } else {
22494
22495             }
22496             return { top: t, left: l };
22497         },
22498
22499         /**
22500          * Returns the specified element style property
22501          * @method getStyle
22502          * @param {HTMLElement} el          the element
22503          * @param {string}      styleProp   the style property
22504          * @return {string} The value of the style property
22505          * @deprecated use Roo.lib.Dom.getStyle
22506          * @static
22507          */
22508         getStyle: function(el, styleProp) {
22509             return Roo.fly(el).getStyle(styleProp);
22510         },
22511
22512         /**
22513          * Gets the scrollTop
22514          * @method getScrollTop
22515          * @return {int} the document's scrollTop
22516          * @static
22517          */
22518         getScrollTop: function () { return this.getScroll().top; },
22519
22520         /**
22521          * Gets the scrollLeft
22522          * @method getScrollLeft
22523          * @return {int} the document's scrollTop
22524          * @static
22525          */
22526         getScrollLeft: function () { return this.getScroll().left; },
22527
22528         /**
22529          * Sets the x/y position of an element to the location of the
22530          * target element.
22531          * @method moveToEl
22532          * @param {HTMLElement} moveEl      The element to move
22533          * @param {HTMLElement} targetEl    The position reference element
22534          * @static
22535          */
22536         moveToEl: function (moveEl, targetEl) {
22537             var aCoord = Roo.lib.Dom.getXY(targetEl);
22538             Roo.lib.Dom.setXY(moveEl, aCoord);
22539         },
22540
22541         /**
22542          * Numeric array sort function
22543          * @method numericSort
22544          * @static
22545          */
22546         numericSort: function(a, b) { return (a - b); },
22547
22548         /**
22549          * Internal counter
22550          * @property _timeoutCount
22551          * @private
22552          * @static
22553          */
22554         _timeoutCount: 0,
22555
22556         /**
22557          * Trying to make the load order less important.  Without this we get
22558          * an error if this file is loaded before the Event Utility.
22559          * @method _addListeners
22560          * @private
22561          * @static
22562          */
22563         _addListeners: function() {
22564             var DDM = Roo.dd.DDM;
22565             if ( Roo.lib.Event && document ) {
22566                 DDM._onLoad();
22567             } else {
22568                 if (DDM._timeoutCount > 2000) {
22569                 } else {
22570                     setTimeout(DDM._addListeners, 10);
22571                     if (document && document.body) {
22572                         DDM._timeoutCount += 1;
22573                     }
22574                 }
22575             }
22576         },
22577
22578         /**
22579          * Recursively searches the immediate parent and all child nodes for
22580          * the handle element in order to determine wheter or not it was
22581          * clicked.
22582          * @method handleWasClicked
22583          * @param node the html element to inspect
22584          * @static
22585          */
22586         handleWasClicked: function(node, id) {
22587             if (this.isHandle(id, node.id)) {
22588                 return true;
22589             } else {
22590                 // check to see if this is a text node child of the one we want
22591                 var p = node.parentNode;
22592
22593                 while (p) {
22594                     if (this.isHandle(id, p.id)) {
22595                         return true;
22596                     } else {
22597                         p = p.parentNode;
22598                     }
22599                 }
22600             }
22601
22602             return false;
22603         }
22604
22605     };
22606
22607 }();
22608
22609 // shorter alias, save a few bytes
22610 Roo.dd.DDM = Roo.dd.DragDropMgr;
22611 Roo.dd.DDM._addListeners();
22612
22613 }/*
22614  * Based on:
22615  * Ext JS Library 1.1.1
22616  * Copyright(c) 2006-2007, Ext JS, LLC.
22617  *
22618  * Originally Released Under LGPL - original licence link has changed is not relivant.
22619  *
22620  * Fork - LGPL
22621  * <script type="text/javascript">
22622  */
22623
22624 /**
22625  * @class Roo.dd.DD
22626  * A DragDrop implementation where the linked element follows the
22627  * mouse cursor during a drag.
22628  * @extends Roo.dd.DragDrop
22629  * @constructor
22630  * @param {String} id the id of the linked element
22631  * @param {String} sGroup the group of related DragDrop items
22632  * @param {object} config an object containing configurable attributes
22633  *                Valid properties for DD:
22634  *                    scroll
22635  */
22636 Roo.dd.DD = function(id, sGroup, config) {
22637     if (id) {
22638         this.init(id, sGroup, config);
22639     }
22640 };
22641
22642 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22643
22644     /**
22645      * When set to true, the utility automatically tries to scroll the browser
22646      * window wehn a drag and drop element is dragged near the viewport boundary.
22647      * Defaults to true.
22648      * @property scroll
22649      * @type boolean
22650      */
22651     scroll: true,
22652
22653     /**
22654      * Sets the pointer offset to the distance between the linked element's top
22655      * left corner and the location the element was clicked
22656      * @method autoOffset
22657      * @param {int} iPageX the X coordinate of the click
22658      * @param {int} iPageY the Y coordinate of the click
22659      */
22660     autoOffset: function(iPageX, iPageY) {
22661         var x = iPageX - this.startPageX;
22662         var y = iPageY - this.startPageY;
22663         this.setDelta(x, y);
22664     },
22665
22666     /**
22667      * Sets the pointer offset.  You can call this directly to force the
22668      * offset to be in a particular location (e.g., pass in 0,0 to set it
22669      * to the center of the object)
22670      * @method setDelta
22671      * @param {int} iDeltaX the distance from the left
22672      * @param {int} iDeltaY the distance from the top
22673      */
22674     setDelta: function(iDeltaX, iDeltaY) {
22675         this.deltaX = iDeltaX;
22676         this.deltaY = iDeltaY;
22677     },
22678
22679     /**
22680      * Sets the drag element to the location of the mousedown or click event,
22681      * maintaining the cursor location relative to the location on the element
22682      * that was clicked.  Override this if you want to place the element in a
22683      * location other than where the cursor is.
22684      * @method setDragElPos
22685      * @param {int} iPageX the X coordinate of the mousedown or drag event
22686      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22687      */
22688     setDragElPos: function(iPageX, iPageY) {
22689         // the first time we do this, we are going to check to make sure
22690         // the element has css positioning
22691
22692         var el = this.getDragEl();
22693         this.alignElWithMouse(el, iPageX, iPageY);
22694     },
22695
22696     /**
22697      * Sets the element to the location of the mousedown or click event,
22698      * maintaining the cursor location relative to the location on the element
22699      * that was clicked.  Override this if you want to place the element in a
22700      * location other than where the cursor is.
22701      * @method alignElWithMouse
22702      * @param {HTMLElement} el the element to move
22703      * @param {int} iPageX the X coordinate of the mousedown or drag event
22704      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22705      */
22706     alignElWithMouse: function(el, iPageX, iPageY) {
22707         var oCoord = this.getTargetCoord(iPageX, iPageY);
22708         var fly = el.dom ? el : Roo.fly(el);
22709         if (!this.deltaSetXY) {
22710             var aCoord = [oCoord.x, oCoord.y];
22711             fly.setXY(aCoord);
22712             var newLeft = fly.getLeft(true);
22713             var newTop  = fly.getTop(true);
22714             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22715         } else {
22716             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22717         }
22718
22719         this.cachePosition(oCoord.x, oCoord.y);
22720         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22721         return oCoord;
22722     },
22723
22724     /**
22725      * Saves the most recent position so that we can reset the constraints and
22726      * tick marks on-demand.  We need to know this so that we can calculate the
22727      * number of pixels the element is offset from its original position.
22728      * @method cachePosition
22729      * @param iPageX the current x position (optional, this just makes it so we
22730      * don't have to look it up again)
22731      * @param iPageY the current y position (optional, this just makes it so we
22732      * don't have to look it up again)
22733      */
22734     cachePosition: function(iPageX, iPageY) {
22735         if (iPageX) {
22736             this.lastPageX = iPageX;
22737             this.lastPageY = iPageY;
22738         } else {
22739             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22740             this.lastPageX = aCoord[0];
22741             this.lastPageY = aCoord[1];
22742         }
22743     },
22744
22745     /**
22746      * Auto-scroll the window if the dragged object has been moved beyond the
22747      * visible window boundary.
22748      * @method autoScroll
22749      * @param {int} x the drag element's x position
22750      * @param {int} y the drag element's y position
22751      * @param {int} h the height of the drag element
22752      * @param {int} w the width of the drag element
22753      * @private
22754      */
22755     autoScroll: function(x, y, h, w) {
22756
22757         if (this.scroll) {
22758             // The client height
22759             var clientH = Roo.lib.Dom.getViewWidth();
22760
22761             // The client width
22762             var clientW = Roo.lib.Dom.getViewHeight();
22763
22764             // The amt scrolled down
22765             var st = this.DDM.getScrollTop();
22766
22767             // The amt scrolled right
22768             var sl = this.DDM.getScrollLeft();
22769
22770             // Location of the bottom of the element
22771             var bot = h + y;
22772
22773             // Location of the right of the element
22774             var right = w + x;
22775
22776             // The distance from the cursor to the bottom of the visible area,
22777             // adjusted so that we don't scroll if the cursor is beyond the
22778             // element drag constraints
22779             var toBot = (clientH + st - y - this.deltaY);
22780
22781             // The distance from the cursor to the right of the visible area
22782             var toRight = (clientW + sl - x - this.deltaX);
22783
22784
22785             // How close to the edge the cursor must be before we scroll
22786             // var thresh = (document.all) ? 100 : 40;
22787             var thresh = 40;
22788
22789             // How many pixels to scroll per autoscroll op.  This helps to reduce
22790             // clunky scrolling. IE is more sensitive about this ... it needs this
22791             // value to be higher.
22792             var scrAmt = (document.all) ? 80 : 30;
22793
22794             // Scroll down if we are near the bottom of the visible page and the
22795             // obj extends below the crease
22796             if ( bot > clientH && toBot < thresh ) {
22797                 window.scrollTo(sl, st + scrAmt);
22798             }
22799
22800             // Scroll up if the window is scrolled down and the top of the object
22801             // goes above the top border
22802             if ( y < st && st > 0 && y - st < thresh ) {
22803                 window.scrollTo(sl, st - scrAmt);
22804             }
22805
22806             // Scroll right if the obj is beyond the right border and the cursor is
22807             // near the border.
22808             if ( right > clientW && toRight < thresh ) {
22809                 window.scrollTo(sl + scrAmt, st);
22810             }
22811
22812             // Scroll left if the window has been scrolled to the right and the obj
22813             // extends past the left border
22814             if ( x < sl && sl > 0 && x - sl < thresh ) {
22815                 window.scrollTo(sl - scrAmt, st);
22816             }
22817         }
22818     },
22819
22820     /**
22821      * Finds the location the element should be placed if we want to move
22822      * it to where the mouse location less the click offset would place us.
22823      * @method getTargetCoord
22824      * @param {int} iPageX the X coordinate of the click
22825      * @param {int} iPageY the Y coordinate of the click
22826      * @return an object that contains the coordinates (Object.x and Object.y)
22827      * @private
22828      */
22829     getTargetCoord: function(iPageX, iPageY) {
22830
22831
22832         var x = iPageX - this.deltaX;
22833         var y = iPageY - this.deltaY;
22834
22835         if (this.constrainX) {
22836             if (x < this.minX) { x = this.minX; }
22837             if (x > this.maxX) { x = this.maxX; }
22838         }
22839
22840         if (this.constrainY) {
22841             if (y < this.minY) { y = this.minY; }
22842             if (y > this.maxY) { y = this.maxY; }
22843         }
22844
22845         x = this.getTick(x, this.xTicks);
22846         y = this.getTick(y, this.yTicks);
22847
22848
22849         return {x:x, y:y};
22850     },
22851
22852     /*
22853      * Sets up config options specific to this class. Overrides
22854      * Roo.dd.DragDrop, but all versions of this method through the
22855      * inheritance chain are called
22856      */
22857     applyConfig: function() {
22858         Roo.dd.DD.superclass.applyConfig.call(this);
22859         this.scroll = (this.config.scroll !== false);
22860     },
22861
22862     /*
22863      * Event that fires prior to the onMouseDown event.  Overrides
22864      * Roo.dd.DragDrop.
22865      */
22866     b4MouseDown: function(e) {
22867         // this.resetConstraints();
22868         this.autoOffset(e.getPageX(),
22869                             e.getPageY());
22870     },
22871
22872     /*
22873      * Event that fires prior to the onDrag event.  Overrides
22874      * Roo.dd.DragDrop.
22875      */
22876     b4Drag: function(e) {
22877         this.setDragElPos(e.getPageX(),
22878                             e.getPageY());
22879     },
22880
22881     toString: function() {
22882         return ("DD " + this.id);
22883     }
22884
22885     //////////////////////////////////////////////////////////////////////////
22886     // Debugging ygDragDrop events that can be overridden
22887     //////////////////////////////////////////////////////////////////////////
22888     /*
22889     startDrag: function(x, y) {
22890     },
22891
22892     onDrag: function(e) {
22893     },
22894
22895     onDragEnter: function(e, id) {
22896     },
22897
22898     onDragOver: function(e, id) {
22899     },
22900
22901     onDragOut: function(e, id) {
22902     },
22903
22904     onDragDrop: function(e, id) {
22905     },
22906
22907     endDrag: function(e) {
22908     }
22909
22910     */
22911
22912 });/*
22913  * Based on:
22914  * Ext JS Library 1.1.1
22915  * Copyright(c) 2006-2007, Ext JS, LLC.
22916  *
22917  * Originally Released Under LGPL - original licence link has changed is not relivant.
22918  *
22919  * Fork - LGPL
22920  * <script type="text/javascript">
22921  */
22922
22923 /**
22924  * @class Roo.dd.DDProxy
22925  * A DragDrop implementation that inserts an empty, bordered div into
22926  * the document that follows the cursor during drag operations.  At the time of
22927  * the click, the frame div is resized to the dimensions of the linked html
22928  * element, and moved to the exact location of the linked element.
22929  *
22930  * References to the "frame" element refer to the single proxy element that
22931  * was created to be dragged in place of all DDProxy elements on the
22932  * page.
22933  *
22934  * @extends Roo.dd.DD
22935  * @constructor
22936  * @param {String} id the id of the linked html element
22937  * @param {String} sGroup the group of related DragDrop objects
22938  * @param {object} config an object containing configurable attributes
22939  *                Valid properties for DDProxy in addition to those in DragDrop:
22940  *                   resizeFrame, centerFrame, dragElId
22941  */
22942 Roo.dd.DDProxy = function(id, sGroup, config) {
22943     if (id) {
22944         this.init(id, sGroup, config);
22945         this.initFrame();
22946     }
22947 };
22948
22949 /**
22950  * The default drag frame div id
22951  * @property Roo.dd.DDProxy.dragElId
22952  * @type String
22953  * @static
22954  */
22955 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22956
22957 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22958
22959     /**
22960      * By default we resize the drag frame to be the same size as the element
22961      * we want to drag (this is to get the frame effect).  We can turn it off
22962      * if we want a different behavior.
22963      * @property resizeFrame
22964      * @type boolean
22965      */
22966     resizeFrame: true,
22967
22968     /**
22969      * By default the frame is positioned exactly where the drag element is, so
22970      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22971      * you do not have constraints on the obj is to have the drag frame centered
22972      * around the cursor.  Set centerFrame to true for this effect.
22973      * @property centerFrame
22974      * @type boolean
22975      */
22976     centerFrame: false,
22977
22978     /**
22979      * Creates the proxy element if it does not yet exist
22980      * @method createFrame
22981      */
22982     createFrame: function() {
22983         var self = this;
22984         var body = document.body;
22985
22986         if (!body || !body.firstChild) {
22987             setTimeout( function() { self.createFrame(); }, 50 );
22988             return;
22989         }
22990
22991         var div = this.getDragEl();
22992
22993         if (!div) {
22994             div    = document.createElement("div");
22995             div.id = this.dragElId;
22996             var s  = div.style;
22997
22998             s.position   = "absolute";
22999             s.visibility = "hidden";
23000             s.cursor     = "move";
23001             s.border     = "2px solid #aaa";
23002             s.zIndex     = 999;
23003
23004             // appendChild can blow up IE if invoked prior to the window load event
23005             // while rendering a table.  It is possible there are other scenarios
23006             // that would cause this to happen as well.
23007             body.insertBefore(div, body.firstChild);
23008         }
23009     },
23010
23011     /**
23012      * Initialization for the drag frame element.  Must be called in the
23013      * constructor of all subclasses
23014      * @method initFrame
23015      */
23016     initFrame: function() {
23017         this.createFrame();
23018     },
23019
23020     applyConfig: function() {
23021         Roo.dd.DDProxy.superclass.applyConfig.call(this);
23022
23023         this.resizeFrame = (this.config.resizeFrame !== false);
23024         this.centerFrame = (this.config.centerFrame);
23025         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
23026     },
23027
23028     /**
23029      * Resizes the drag frame to the dimensions of the clicked object, positions
23030      * it over the object, and finally displays it
23031      * @method showFrame
23032      * @param {int} iPageX X click position
23033      * @param {int} iPageY Y click position
23034      * @private
23035      */
23036     showFrame: function(iPageX, iPageY) {
23037         var el = this.getEl();
23038         var dragEl = this.getDragEl();
23039         var s = dragEl.style;
23040
23041         this._resizeProxy();
23042
23043         if (this.centerFrame) {
23044             this.setDelta( Math.round(parseInt(s.width,  10)/2),
23045                            Math.round(parseInt(s.height, 10)/2) );
23046         }
23047
23048         this.setDragElPos(iPageX, iPageY);
23049
23050         Roo.fly(dragEl).show();
23051     },
23052
23053     /**
23054      * The proxy is automatically resized to the dimensions of the linked
23055      * element when a drag is initiated, unless resizeFrame is set to false
23056      * @method _resizeProxy
23057      * @private
23058      */
23059     _resizeProxy: function() {
23060         if (this.resizeFrame) {
23061             var el = this.getEl();
23062             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
23063         }
23064     },
23065
23066     // overrides Roo.dd.DragDrop
23067     b4MouseDown: function(e) {
23068         var x = e.getPageX();
23069         var y = e.getPageY();
23070         this.autoOffset(x, y);
23071         this.setDragElPos(x, y);
23072     },
23073
23074     // overrides Roo.dd.DragDrop
23075     b4StartDrag: function(x, y) {
23076         // show the drag frame
23077         this.showFrame(x, y);
23078     },
23079
23080     // overrides Roo.dd.DragDrop
23081     b4EndDrag: function(e) {
23082         Roo.fly(this.getDragEl()).hide();
23083     },
23084
23085     // overrides Roo.dd.DragDrop
23086     // By default we try to move the element to the last location of the frame.
23087     // This is so that the default behavior mirrors that of Roo.dd.DD.
23088     endDrag: function(e) {
23089
23090         var lel = this.getEl();
23091         var del = this.getDragEl();
23092
23093         // Show the drag frame briefly so we can get its position
23094         del.style.visibility = "";
23095
23096         this.beforeMove();
23097         // Hide the linked element before the move to get around a Safari
23098         // rendering bug.
23099         lel.style.visibility = "hidden";
23100         Roo.dd.DDM.moveToEl(lel, del);
23101         del.style.visibility = "hidden";
23102         lel.style.visibility = "";
23103
23104         this.afterDrag();
23105     },
23106
23107     beforeMove : function(){
23108
23109     },
23110
23111     afterDrag : function(){
23112
23113     },
23114
23115     toString: function() {
23116         return ("DDProxy " + this.id);
23117     }
23118
23119 });
23120 /*
23121  * Based on:
23122  * Ext JS Library 1.1.1
23123  * Copyright(c) 2006-2007, Ext JS, LLC.
23124  *
23125  * Originally Released Under LGPL - original licence link has changed is not relivant.
23126  *
23127  * Fork - LGPL
23128  * <script type="text/javascript">
23129  */
23130
23131  /**
23132  * @class Roo.dd.DDTarget
23133  * A DragDrop implementation that does not move, but can be a drop
23134  * target.  You would get the same result by simply omitting implementation
23135  * for the event callbacks, but this way we reduce the processing cost of the
23136  * event listener and the callbacks.
23137  * @extends Roo.dd.DragDrop
23138  * @constructor
23139  * @param {String} id the id of the element that is a drop target
23140  * @param {String} sGroup the group of related DragDrop objects
23141  * @param {object} config an object containing configurable attributes
23142  *                 Valid properties for DDTarget in addition to those in
23143  *                 DragDrop:
23144  *                    none
23145  */
23146 Roo.dd.DDTarget = function(id, sGroup, config) {
23147     if (id) {
23148         this.initTarget(id, sGroup, config);
23149     }
23150     if (config && (config.listeners || config.events)) { 
23151         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
23152             listeners : config.listeners || {}, 
23153             events : config.events || {} 
23154         });    
23155     }
23156 };
23157
23158 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
23159 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
23160     toString: function() {
23161         return ("DDTarget " + this.id);
23162     }
23163 });
23164 /*
23165  * Based on:
23166  * Ext JS Library 1.1.1
23167  * Copyright(c) 2006-2007, Ext JS, LLC.
23168  *
23169  * Originally Released Under LGPL - original licence link has changed is not relivant.
23170  *
23171  * Fork - LGPL
23172  * <script type="text/javascript">
23173  */
23174  
23175
23176 /**
23177  * @class Roo.dd.ScrollManager
23178  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
23179  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
23180  * @static
23181  */
23182 Roo.dd.ScrollManager = function(){
23183     var ddm = Roo.dd.DragDropMgr;
23184     var els = {};
23185     var dragEl = null;
23186     var proc = {};
23187     
23188     
23189     
23190     var onStop = function(e){
23191         dragEl = null;
23192         clearProc();
23193     };
23194     
23195     var triggerRefresh = function(){
23196         if(ddm.dragCurrent){
23197              ddm.refreshCache(ddm.dragCurrent.groups);
23198         }
23199     };
23200     
23201     var doScroll = function(){
23202         if(ddm.dragCurrent){
23203             var dds = Roo.dd.ScrollManager;
23204             if(!dds.animate){
23205                 if(proc.el.scroll(proc.dir, dds.increment)){
23206                     triggerRefresh();
23207                 }
23208             }else{
23209                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23210             }
23211         }
23212     };
23213     
23214     var clearProc = function(){
23215         if(proc.id){
23216             clearInterval(proc.id);
23217         }
23218         proc.id = 0;
23219         proc.el = null;
23220         proc.dir = "";
23221     };
23222     
23223     var startProc = function(el, dir){
23224          Roo.log('scroll startproc');
23225         clearProc();
23226         proc.el = el;
23227         proc.dir = dir;
23228         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23229     };
23230     
23231     var onFire = function(e, isDrop){
23232        
23233         if(isDrop || !ddm.dragCurrent){ return; }
23234         var dds = Roo.dd.ScrollManager;
23235         if(!dragEl || dragEl != ddm.dragCurrent){
23236             dragEl = ddm.dragCurrent;
23237             // refresh regions on drag start
23238             dds.refreshCache();
23239         }
23240         
23241         var xy = Roo.lib.Event.getXY(e);
23242         var pt = new Roo.lib.Point(xy[0], xy[1]);
23243         for(var id in els){
23244             var el = els[id], r = el._region;
23245             if(r && r.contains(pt) && el.isScrollable()){
23246                 if(r.bottom - pt.y <= dds.thresh){
23247                     if(proc.el != el){
23248                         startProc(el, "down");
23249                     }
23250                     return;
23251                 }else if(r.right - pt.x <= dds.thresh){
23252                     if(proc.el != el){
23253                         startProc(el, "left");
23254                     }
23255                     return;
23256                 }else if(pt.y - r.top <= dds.thresh){
23257                     if(proc.el != el){
23258                         startProc(el, "up");
23259                     }
23260                     return;
23261                 }else if(pt.x - r.left <= dds.thresh){
23262                     if(proc.el != el){
23263                         startProc(el, "right");
23264                     }
23265                     return;
23266                 }
23267             }
23268         }
23269         clearProc();
23270     };
23271     
23272     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23273     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23274     
23275     return {
23276         /**
23277          * Registers new overflow element(s) to auto scroll
23278          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23279          */
23280         register : function(el){
23281             if(el instanceof Array){
23282                 for(var i = 0, len = el.length; i < len; i++) {
23283                         this.register(el[i]);
23284                 }
23285             }else{
23286                 el = Roo.get(el);
23287                 els[el.id] = el;
23288             }
23289             Roo.dd.ScrollManager.els = els;
23290         },
23291         
23292         /**
23293          * Unregisters overflow element(s) so they are no longer scrolled
23294          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23295          */
23296         unregister : function(el){
23297             if(el instanceof Array){
23298                 for(var i = 0, len = el.length; i < len; i++) {
23299                         this.unregister(el[i]);
23300                 }
23301             }else{
23302                 el = Roo.get(el);
23303                 delete els[el.id];
23304             }
23305         },
23306         
23307         /**
23308          * The number of pixels from the edge of a container the pointer needs to be to 
23309          * trigger scrolling (defaults to 25)
23310          * @type Number
23311          */
23312         thresh : 25,
23313         
23314         /**
23315          * The number of pixels to scroll in each scroll increment (defaults to 50)
23316          * @type Number
23317          */
23318         increment : 100,
23319         
23320         /**
23321          * The frequency of scrolls in milliseconds (defaults to 500)
23322          * @type Number
23323          */
23324         frequency : 500,
23325         
23326         /**
23327          * True to animate the scroll (defaults to true)
23328          * @type Boolean
23329          */
23330         animate: true,
23331         
23332         /**
23333          * The animation duration in seconds - 
23334          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23335          * @type Number
23336          */
23337         animDuration: .4,
23338         
23339         /**
23340          * Manually trigger a cache refresh.
23341          */
23342         refreshCache : function(){
23343             for(var id in els){
23344                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23345                     els[id]._region = els[id].getRegion();
23346                 }
23347             }
23348         }
23349     };
23350 }();/*
23351  * Based on:
23352  * Ext JS Library 1.1.1
23353  * Copyright(c) 2006-2007, Ext JS, LLC.
23354  *
23355  * Originally Released Under LGPL - original licence link has changed is not relivant.
23356  *
23357  * Fork - LGPL
23358  * <script type="text/javascript">
23359  */
23360  
23361
23362 /**
23363  * @class Roo.dd.Registry
23364  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23365  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23366  * @static
23367  */
23368 Roo.dd.Registry = function(){
23369     var elements = {}; 
23370     var handles = {}; 
23371     var autoIdSeed = 0;
23372
23373     var getId = function(el, autogen){
23374         if(typeof el == "string"){
23375             return el;
23376         }
23377         var id = el.id;
23378         if(!id && autogen !== false){
23379             id = "roodd-" + (++autoIdSeed);
23380             el.id = id;
23381         }
23382         return id;
23383     };
23384     
23385     return {
23386     /**
23387      * Register a drag drop element
23388      * @param {String|HTMLElement} element The id or DOM node to register
23389      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23390      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23391      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23392      * populated in the data object (if applicable):
23393      * <pre>
23394 Value      Description<br />
23395 ---------  ------------------------------------------<br />
23396 handles    Array of DOM nodes that trigger dragging<br />
23397            for the element being registered<br />
23398 isHandle   True if the element passed in triggers<br />
23399            dragging itself, else false
23400 </pre>
23401      */
23402         register : function(el, data){
23403             data = data || {};
23404             if(typeof el == "string"){
23405                 el = document.getElementById(el);
23406             }
23407             data.ddel = el;
23408             elements[getId(el)] = data;
23409             if(data.isHandle !== false){
23410                 handles[data.ddel.id] = data;
23411             }
23412             if(data.handles){
23413                 var hs = data.handles;
23414                 for(var i = 0, len = hs.length; i < len; i++){
23415                         handles[getId(hs[i])] = data;
23416                 }
23417             }
23418         },
23419
23420     /**
23421      * Unregister a drag drop element
23422      * @param {String|HTMLElement}  element The id or DOM node to unregister
23423      */
23424         unregister : function(el){
23425             var id = getId(el, false);
23426             var data = elements[id];
23427             if(data){
23428                 delete elements[id];
23429                 if(data.handles){
23430                     var hs = data.handles;
23431                     for(var i = 0, len = hs.length; i < len; i++){
23432                         delete handles[getId(hs[i], false)];
23433                     }
23434                 }
23435             }
23436         },
23437
23438     /**
23439      * Returns the handle registered for a DOM Node by id
23440      * @param {String|HTMLElement} id The DOM node or id to look up
23441      * @return {Object} handle The custom handle data
23442      */
23443         getHandle : function(id){
23444             if(typeof id != "string"){ // must be element?
23445                 id = id.id;
23446             }
23447             return handles[id];
23448         },
23449
23450     /**
23451      * Returns the handle that is registered for the DOM node that is the target of the event
23452      * @param {Event} e The event
23453      * @return {Object} handle The custom handle data
23454      */
23455         getHandleFromEvent : function(e){
23456             var t = Roo.lib.Event.getTarget(e);
23457             return t ? handles[t.id] : null;
23458         },
23459
23460     /**
23461      * Returns a custom data object that is registered for a DOM node by id
23462      * @param {String|HTMLElement} id The DOM node or id to look up
23463      * @return {Object} data The custom data
23464      */
23465         getTarget : function(id){
23466             if(typeof id != "string"){ // must be element?
23467                 id = id.id;
23468             }
23469             return elements[id];
23470         },
23471
23472     /**
23473      * Returns a custom data object that is registered for the DOM node that is the target of the event
23474      * @param {Event} e The event
23475      * @return {Object} data The custom data
23476      */
23477         getTargetFromEvent : function(e){
23478             var t = Roo.lib.Event.getTarget(e);
23479             return t ? elements[t.id] || handles[t.id] : null;
23480         }
23481     };
23482 }();/*
23483  * Based on:
23484  * Ext JS Library 1.1.1
23485  * Copyright(c) 2006-2007, Ext JS, LLC.
23486  *
23487  * Originally Released Under LGPL - original licence link has changed is not relivant.
23488  *
23489  * Fork - LGPL
23490  * <script type="text/javascript">
23491  */
23492  
23493
23494 /**
23495  * @class Roo.dd.StatusProxy
23496  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23497  * default drag proxy used by all Roo.dd components.
23498  * @constructor
23499  * @param {Object} config
23500  */
23501 Roo.dd.StatusProxy = function(config){
23502     Roo.apply(this, config);
23503     this.id = this.id || Roo.id();
23504     this.el = new Roo.Layer({
23505         dh: {
23506             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23507                 {tag: "div", cls: "x-dd-drop-icon"},
23508                 {tag: "div", cls: "x-dd-drag-ghost"}
23509             ]
23510         }, 
23511         shadow: !config || config.shadow !== false
23512     });
23513     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23514     this.dropStatus = this.dropNotAllowed;
23515 };
23516
23517 Roo.dd.StatusProxy.prototype = {
23518     /**
23519      * @cfg {String} dropAllowed
23520      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23521      */
23522     dropAllowed : "x-dd-drop-ok",
23523     /**
23524      * @cfg {String} dropNotAllowed
23525      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23526      */
23527     dropNotAllowed : "x-dd-drop-nodrop",
23528
23529     /**
23530      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23531      * over the current target element.
23532      * @param {String} cssClass The css class for the new drop status indicator image
23533      */
23534     setStatus : function(cssClass){
23535         cssClass = cssClass || this.dropNotAllowed;
23536         if(this.dropStatus != cssClass){
23537             this.el.replaceClass(this.dropStatus, cssClass);
23538             this.dropStatus = cssClass;
23539         }
23540     },
23541
23542     /**
23543      * Resets the status indicator to the default dropNotAllowed value
23544      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23545      */
23546     reset : function(clearGhost){
23547         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23548         this.dropStatus = this.dropNotAllowed;
23549         if(clearGhost){
23550             this.ghost.update("");
23551         }
23552     },
23553
23554     /**
23555      * Updates the contents of the ghost element
23556      * @param {String} html The html that will replace the current innerHTML of the ghost element
23557      */
23558     update : function(html){
23559         if(typeof html == "string"){
23560             this.ghost.update(html);
23561         }else{
23562             this.ghost.update("");
23563             html.style.margin = "0";
23564             this.ghost.dom.appendChild(html);
23565         }
23566         // ensure float = none set?? cant remember why though.
23567         var el = this.ghost.dom.firstChild;
23568                 if(el){
23569                         Roo.fly(el).setStyle('float', 'none');
23570                 }
23571     },
23572     
23573     /**
23574      * Returns the underlying proxy {@link Roo.Layer}
23575      * @return {Roo.Layer} el
23576     */
23577     getEl : function(){
23578         return this.el;
23579     },
23580
23581     /**
23582      * Returns the ghost element
23583      * @return {Roo.Element} el
23584      */
23585     getGhost : function(){
23586         return this.ghost;
23587     },
23588
23589     /**
23590      * Hides the proxy
23591      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23592      */
23593     hide : function(clear){
23594         this.el.hide();
23595         if(clear){
23596             this.reset(true);
23597         }
23598     },
23599
23600     /**
23601      * Stops the repair animation if it's currently running
23602      */
23603     stop : function(){
23604         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23605             this.anim.stop();
23606         }
23607     },
23608
23609     /**
23610      * Displays this proxy
23611      */
23612     show : function(){
23613         this.el.show();
23614     },
23615
23616     /**
23617      * Force the Layer to sync its shadow and shim positions to the element
23618      */
23619     sync : function(){
23620         this.el.sync();
23621     },
23622
23623     /**
23624      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23625      * invalid drop operation by the item being dragged.
23626      * @param {Array} xy The XY position of the element ([x, y])
23627      * @param {Function} callback The function to call after the repair is complete
23628      * @param {Object} scope The scope in which to execute the callback
23629      */
23630     repair : function(xy, callback, scope){
23631         this.callback = callback;
23632         this.scope = scope;
23633         if(xy && this.animRepair !== false){
23634             this.el.addClass("x-dd-drag-repair");
23635             this.el.hideUnders(true);
23636             this.anim = this.el.shift({
23637                 duration: this.repairDuration || .5,
23638                 easing: 'easeOut',
23639                 xy: xy,
23640                 stopFx: true,
23641                 callback: this.afterRepair,
23642                 scope: this
23643             });
23644         }else{
23645             this.afterRepair();
23646         }
23647     },
23648
23649     // private
23650     afterRepair : function(){
23651         this.hide(true);
23652         if(typeof this.callback == "function"){
23653             this.callback.call(this.scope || this);
23654         }
23655         this.callback = null;
23656         this.scope = null;
23657     }
23658 };/*
23659  * Based on:
23660  * Ext JS Library 1.1.1
23661  * Copyright(c) 2006-2007, Ext JS, LLC.
23662  *
23663  * Originally Released Under LGPL - original licence link has changed is not relivant.
23664  *
23665  * Fork - LGPL
23666  * <script type="text/javascript">
23667  */
23668
23669 /**
23670  * @class Roo.dd.DragSource
23671  * @extends Roo.dd.DDProxy
23672  * A simple class that provides the basic implementation needed to make any element draggable.
23673  * @constructor
23674  * @param {String/HTMLElement/Element} el The container element
23675  * @param {Object} config
23676  */
23677 Roo.dd.DragSource = function(el, config){
23678     this.el = Roo.get(el);
23679     this.dragData = {};
23680     
23681     Roo.apply(this, config);
23682     
23683     if(!this.proxy){
23684         this.proxy = new Roo.dd.StatusProxy();
23685     }
23686
23687     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23688           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23689     
23690     this.dragging = false;
23691 };
23692
23693 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23694     /**
23695      * @cfg {String} dropAllowed
23696      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23697      */
23698     dropAllowed : "x-dd-drop-ok",
23699     /**
23700      * @cfg {String} dropNotAllowed
23701      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23702      */
23703     dropNotAllowed : "x-dd-drop-nodrop",
23704
23705     /**
23706      * Returns the data object associated with this drag source
23707      * @return {Object} data An object containing arbitrary data
23708      */
23709     getDragData : function(e){
23710         return this.dragData;
23711     },
23712
23713     // private
23714     onDragEnter : function(e, id){
23715         var target = Roo.dd.DragDropMgr.getDDById(id);
23716         this.cachedTarget = target;
23717         if(this.beforeDragEnter(target, e, id) !== false){
23718             if(target.isNotifyTarget){
23719                 var status = target.notifyEnter(this, e, this.dragData);
23720                 this.proxy.setStatus(status);
23721             }else{
23722                 this.proxy.setStatus(this.dropAllowed);
23723             }
23724             
23725             if(this.afterDragEnter){
23726                 /**
23727                  * An empty function by default, but provided so that you can perform a custom action
23728                  * when the dragged item enters the drop target by providing an implementation.
23729                  * @param {Roo.dd.DragDrop} target The drop target
23730                  * @param {Event} e The event object
23731                  * @param {String} id The id of the dragged element
23732                  * @method afterDragEnter
23733                  */
23734                 this.afterDragEnter(target, e, id);
23735             }
23736         }
23737     },
23738
23739     /**
23740      * An empty function by default, but provided so that you can perform a custom action
23741      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23742      * @param {Roo.dd.DragDrop} target The drop target
23743      * @param {Event} e The event object
23744      * @param {String} id The id of the dragged element
23745      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23746      */
23747     beforeDragEnter : function(target, e, id){
23748         return true;
23749     },
23750
23751     // private
23752     alignElWithMouse: function() {
23753         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23754         this.proxy.sync();
23755     },
23756
23757     // private
23758     onDragOver : function(e, id){
23759         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23760         if(this.beforeDragOver(target, e, id) !== false){
23761             if(target.isNotifyTarget){
23762                 var status = target.notifyOver(this, e, this.dragData);
23763                 this.proxy.setStatus(status);
23764             }
23765
23766             if(this.afterDragOver){
23767                 /**
23768                  * An empty function by default, but provided so that you can perform a custom action
23769                  * while the dragged item is over the drop target by providing an implementation.
23770                  * @param {Roo.dd.DragDrop} target The drop target
23771                  * @param {Event} e The event object
23772                  * @param {String} id The id of the dragged element
23773                  * @method afterDragOver
23774                  */
23775                 this.afterDragOver(target, e, id);
23776             }
23777         }
23778     },
23779
23780     /**
23781      * An empty function by default, but provided so that you can perform a custom action
23782      * while the dragged item is over the drop target and optionally cancel the onDragOver.
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     beforeDragOver : function(target, e, id){
23789         return true;
23790     },
23791
23792     // private
23793     onDragOut : function(e, id){
23794         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23795         if(this.beforeDragOut(target, e, id) !== false){
23796             if(target.isNotifyTarget){
23797                 target.notifyOut(this, e, this.dragData);
23798             }
23799             this.proxy.reset();
23800             if(this.afterDragOut){
23801                 /**
23802                  * An empty function by default, but provided so that you can perform a custom action
23803                  * after the dragged item is dragged out of the target without dropping.
23804                  * @param {Roo.dd.DragDrop} target The drop target
23805                  * @param {Event} e The event object
23806                  * @param {String} id The id of the dragged element
23807                  * @method afterDragOut
23808                  */
23809                 this.afterDragOut(target, e, id);
23810             }
23811         }
23812         this.cachedTarget = null;
23813     },
23814
23815     /**
23816      * An empty function by default, but provided so that you can perform a custom action before the dragged
23817      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23818      * @param {Roo.dd.DragDrop} target The drop target
23819      * @param {Event} e The event object
23820      * @param {String} id The id of the dragged element
23821      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23822      */
23823     beforeDragOut : function(target, e, id){
23824         return true;
23825     },
23826     
23827     // private
23828     onDragDrop : function(e, id){
23829         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23830         if(this.beforeDragDrop(target, e, id) !== false){
23831             if(target.isNotifyTarget){
23832                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23833                     this.onValidDrop(target, e, id);
23834                 }else{
23835                     this.onInvalidDrop(target, e, id);
23836                 }
23837             }else{
23838                 this.onValidDrop(target, e, id);
23839             }
23840             
23841             if(this.afterDragDrop){
23842                 /**
23843                  * An empty function by default, but provided so that you can perform a custom action
23844                  * after a valid drag drop has occurred by providing an implementation.
23845                  * @param {Roo.dd.DragDrop} target The drop target
23846                  * @param {Event} e The event object
23847                  * @param {String} id The id of the dropped element
23848                  * @method afterDragDrop
23849                  */
23850                 this.afterDragDrop(target, e, id);
23851             }
23852         }
23853         delete this.cachedTarget;
23854     },
23855
23856     /**
23857      * An empty function by default, but provided so that you can perform a custom action before the dragged
23858      * item is dropped onto the target and optionally cancel the onDragDrop.
23859      * @param {Roo.dd.DragDrop} target The drop target
23860      * @param {Event} e The event object
23861      * @param {String} id The id of the dragged element
23862      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23863      */
23864     beforeDragDrop : function(target, e, id){
23865         return true;
23866     },
23867
23868     // private
23869     onValidDrop : function(target, e, id){
23870         this.hideProxy();
23871         if(this.afterValidDrop){
23872             /**
23873              * An empty function by default, but provided so that you can perform a custom action
23874              * after a valid drop has occurred by providing an implementation.
23875              * @param {Object} target The target DD 
23876              * @param {Event} e The event object
23877              * @param {String} id The id of the dropped element
23878              * @method afterInvalidDrop
23879              */
23880             this.afterValidDrop(target, e, id);
23881         }
23882     },
23883
23884     // private
23885     getRepairXY : function(e, data){
23886         return this.el.getXY();  
23887     },
23888
23889     // private
23890     onInvalidDrop : function(target, e, id){
23891         this.beforeInvalidDrop(target, e, id);
23892         if(this.cachedTarget){
23893             if(this.cachedTarget.isNotifyTarget){
23894                 this.cachedTarget.notifyOut(this, e, this.dragData);
23895             }
23896             this.cacheTarget = null;
23897         }
23898         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23899
23900         if(this.afterInvalidDrop){
23901             /**
23902              * An empty function by default, but provided so that you can perform a custom action
23903              * after an invalid drop has occurred by providing an implementation.
23904              * @param {Event} e The event object
23905              * @param {String} id The id of the dropped element
23906              * @method afterInvalidDrop
23907              */
23908             this.afterInvalidDrop(e, id);
23909         }
23910     },
23911
23912     // private
23913     afterRepair : function(){
23914         if(Roo.enableFx){
23915             this.el.highlight(this.hlColor || "c3daf9");
23916         }
23917         this.dragging = false;
23918     },
23919
23920     /**
23921      * An empty function by default, but provided so that you can perform a custom action after an invalid
23922      * drop has occurred.
23923      * @param {Roo.dd.DragDrop} target The drop target
23924      * @param {Event} e The event object
23925      * @param {String} id The id of the dragged element
23926      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23927      */
23928     beforeInvalidDrop : function(target, e, id){
23929         return true;
23930     },
23931
23932     // private
23933     handleMouseDown : function(e){
23934         if(this.dragging) {
23935             return;
23936         }
23937         var data = this.getDragData(e);
23938         if(data && this.onBeforeDrag(data, e) !== false){
23939             this.dragData = data;
23940             this.proxy.stop();
23941             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23942         } 
23943     },
23944
23945     /**
23946      * An empty function by default, but provided so that you can perform a custom action before the initial
23947      * drag event begins and optionally cancel it.
23948      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23949      * @param {Event} e The event object
23950      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23951      */
23952     onBeforeDrag : function(data, e){
23953         return true;
23954     },
23955
23956     /**
23957      * An empty function by default, but provided so that you can perform a custom action once the initial
23958      * drag event has begun.  The drag cannot be canceled from this function.
23959      * @param {Number} x The x position of the click on the dragged object
23960      * @param {Number} y The y position of the click on the dragged object
23961      */
23962     onStartDrag : Roo.emptyFn,
23963
23964     // private - YUI override
23965     startDrag : function(x, y){
23966         this.proxy.reset();
23967         this.dragging = true;
23968         this.proxy.update("");
23969         this.onInitDrag(x, y);
23970         this.proxy.show();
23971     },
23972
23973     // private
23974     onInitDrag : function(x, y){
23975         var clone = this.el.dom.cloneNode(true);
23976         clone.id = Roo.id(); // prevent duplicate ids
23977         this.proxy.update(clone);
23978         this.onStartDrag(x, y);
23979         return true;
23980     },
23981
23982     /**
23983      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23984      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23985      */
23986     getProxy : function(){
23987         return this.proxy;  
23988     },
23989
23990     /**
23991      * Hides the drag source's {@link Roo.dd.StatusProxy}
23992      */
23993     hideProxy : function(){
23994         this.proxy.hide();  
23995         this.proxy.reset(true);
23996         this.dragging = false;
23997     },
23998
23999     // private
24000     triggerCacheRefresh : function(){
24001         Roo.dd.DDM.refreshCache(this.groups);
24002     },
24003
24004     // private - override to prevent hiding
24005     b4EndDrag: function(e) {
24006     },
24007
24008     // private - override to prevent moving
24009     endDrag : function(e){
24010         this.onEndDrag(this.dragData, e);
24011     },
24012
24013     // private
24014     onEndDrag : function(data, e){
24015     },
24016     
24017     // private - pin to cursor
24018     autoOffset : function(x, y) {
24019         this.setDelta(-12, -20);
24020     }    
24021 });/*
24022  * Based on:
24023  * Ext JS Library 1.1.1
24024  * Copyright(c) 2006-2007, Ext JS, LLC.
24025  *
24026  * Originally Released Under LGPL - original licence link has changed is not relivant.
24027  *
24028  * Fork - LGPL
24029  * <script type="text/javascript">
24030  */
24031
24032
24033 /**
24034  * @class Roo.dd.DropTarget
24035  * @extends Roo.dd.DDTarget
24036  * A simple class that provides the basic implementation needed to make any element a drop target that can have
24037  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
24038  * @constructor
24039  * @param {String/HTMLElement/Element} el The container element
24040  * @param {Object} config
24041  */
24042 Roo.dd.DropTarget = function(el, config){
24043     this.el = Roo.get(el);
24044     
24045     var listeners = false; ;
24046     if (config && config.listeners) {
24047         listeners= config.listeners;
24048         delete config.listeners;
24049     }
24050     Roo.apply(this, config);
24051     
24052     if(this.containerScroll){
24053         Roo.dd.ScrollManager.register(this.el);
24054     }
24055     this.addEvents( {
24056          /**
24057          * @scope Roo.dd.DropTarget
24058          */
24059          
24060          /**
24061          * @event enter
24062          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
24063          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
24064          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
24065          * 
24066          * IMPORTANT : it should set  this.valid to true|false
24067          * 
24068          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24069          * @param {Event} e The event
24070          * @param {Object} data An object containing arbitrary data supplied by the drag source
24071          */
24072         "enter" : true,
24073         
24074          /**
24075          * @event over
24076          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
24077          * This method will be called on every mouse movement while the drag source is over the drop target.
24078          * This default implementation simply returns the dropAllowed config value.
24079          * 
24080          * IMPORTANT : it should set  this.valid to true|false
24081          * 
24082          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24083          * @param {Event} e The event
24084          * @param {Object} data An object containing arbitrary data supplied by the drag source
24085          
24086          */
24087         "over" : true,
24088         /**
24089          * @event out
24090          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
24091          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
24092          * overClass (if any) from the drop element.
24093          * 
24094          * 
24095          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24096          * @param {Event} e The event
24097          * @param {Object} data An object containing arbitrary data supplied by the drag source
24098          */
24099          "out" : true,
24100          
24101         /**
24102          * @event drop
24103          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
24104          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
24105          * implementation that does something to process the drop event and returns true so that the drag source's
24106          * repair action does not run.
24107          * 
24108          * IMPORTANT : it should set this.success
24109          * 
24110          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24111          * @param {Event} e The event
24112          * @param {Object} data An object containing arbitrary data supplied by the drag source
24113         */
24114          "drop" : true
24115     });
24116             
24117      
24118     Roo.dd.DropTarget.superclass.constructor.call(  this, 
24119         this.el.dom, 
24120         this.ddGroup || this.group,
24121         {
24122             isTarget: true,
24123             listeners : listeners || {} 
24124            
24125         
24126         }
24127     );
24128
24129 };
24130
24131 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
24132     /**
24133      * @cfg {String} overClass
24134      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
24135      */
24136      /**
24137      * @cfg {String} ddGroup
24138      * The drag drop group to handle drop events for
24139      */
24140      
24141     /**
24142      * @cfg {String} dropAllowed
24143      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
24144      */
24145     dropAllowed : "x-dd-drop-ok",
24146     /**
24147      * @cfg {String} dropNotAllowed
24148      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
24149      */
24150     dropNotAllowed : "x-dd-drop-nodrop",
24151     /**
24152      * @cfg {boolean} success
24153      * set this after drop listener.. 
24154      */
24155     success : false,
24156     /**
24157      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
24158      * if the drop point is valid for over/enter..
24159      */
24160     valid : false,
24161     // private
24162     isTarget : true,
24163
24164     // private
24165     isNotifyTarget : true,
24166     
24167     /**
24168      * @hide
24169      */
24170     notifyEnter : function(dd, e, data)
24171     {
24172         this.valid = true;
24173         this.fireEvent('enter', dd, e, data);
24174         if(this.overClass){
24175             this.el.addClass(this.overClass);
24176         }
24177         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24178             this.valid ? this.dropAllowed : this.dropNotAllowed
24179         );
24180     },
24181
24182     /**
24183      * @hide
24184      */
24185     notifyOver : function(dd, e, data)
24186     {
24187         this.valid = true;
24188         this.fireEvent('over', dd, e, data);
24189         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
24190             this.valid ? this.dropAllowed : this.dropNotAllowed
24191         );
24192     },
24193
24194     /**
24195      * @hide
24196      */
24197     notifyOut : function(dd, e, data)
24198     {
24199         this.fireEvent('out', dd, e, data);
24200         if(this.overClass){
24201             this.el.removeClass(this.overClass);
24202         }
24203     },
24204
24205     /**
24206      * @hide
24207      */
24208     notifyDrop : function(dd, e, data)
24209     {
24210         this.success = false;
24211         this.fireEvent('drop', dd, e, data);
24212         return this.success;
24213     }
24214 });/*
24215  * Based on:
24216  * Ext JS Library 1.1.1
24217  * Copyright(c) 2006-2007, Ext JS, LLC.
24218  *
24219  * Originally Released Under LGPL - original licence link has changed is not relivant.
24220  *
24221  * Fork - LGPL
24222  * <script type="text/javascript">
24223  */
24224
24225
24226 /**
24227  * @class Roo.dd.DragZone
24228  * @extends Roo.dd.DragSource
24229  * This class provides a container DD instance that proxies for multiple child node sources.<br />
24230  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24231  * @constructor
24232  * @param {String/HTMLElement/Element} el The container element
24233  * @param {Object} config
24234  */
24235 Roo.dd.DragZone = function(el, config){
24236     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24237     if(this.containerScroll){
24238         Roo.dd.ScrollManager.register(this.el);
24239     }
24240 };
24241
24242 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24243     /**
24244      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24245      * for auto scrolling during drag operations.
24246      */
24247     /**
24248      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24249      * method after a failed drop (defaults to "c3daf9" - light blue)
24250      */
24251
24252     /**
24253      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24254      * for a valid target to drag based on the mouse down. Override this method
24255      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24256      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24257      * @param {EventObject} e The mouse down event
24258      * @return {Object} The dragData
24259      */
24260     getDragData : function(e){
24261         return Roo.dd.Registry.getHandleFromEvent(e);
24262     },
24263     
24264     /**
24265      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24266      * this.dragData.ddel
24267      * @param {Number} x The x position of the click on the dragged object
24268      * @param {Number} y The y position of the click on the dragged object
24269      * @return {Boolean} true to continue the drag, false to cancel
24270      */
24271     onInitDrag : function(x, y){
24272         this.proxy.update(this.dragData.ddel.cloneNode(true));
24273         this.onStartDrag(x, y);
24274         return true;
24275     },
24276     
24277     /**
24278      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24279      */
24280     afterRepair : function(){
24281         if(Roo.enableFx){
24282             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24283         }
24284         this.dragging = false;
24285     },
24286
24287     /**
24288      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24289      * the XY of this.dragData.ddel
24290      * @param {EventObject} e The mouse up event
24291      * @return {Array} The xy location (e.g. [100, 200])
24292      */
24293     getRepairXY : function(e){
24294         return Roo.Element.fly(this.dragData.ddel).getXY();  
24295     }
24296 });/*
24297  * Based on:
24298  * Ext JS Library 1.1.1
24299  * Copyright(c) 2006-2007, Ext JS, LLC.
24300  *
24301  * Originally Released Under LGPL - original licence link has changed is not relivant.
24302  *
24303  * Fork - LGPL
24304  * <script type="text/javascript">
24305  */
24306 /**
24307  * @class Roo.dd.DropZone
24308  * @extends Roo.dd.DropTarget
24309  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24310  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24311  * @constructor
24312  * @param {String/HTMLElement/Element} el The container element
24313  * @param {Object} config
24314  */
24315 Roo.dd.DropZone = function(el, config){
24316     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24317 };
24318
24319 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24320     /**
24321      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24322      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24323      * provide your own custom lookup.
24324      * @param {Event} e The event
24325      * @return {Object} data The custom data
24326      */
24327     getTargetFromEvent : function(e){
24328         return Roo.dd.Registry.getTargetFromEvent(e);
24329     },
24330
24331     /**
24332      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24333      * that it has registered.  This method has no default implementation and should be overridden to provide
24334      * node-specific processing if necessary.
24335      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24336      * {@link #getTargetFromEvent} for this node)
24337      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24338      * @param {Event} e The event
24339      * @param {Object} data An object containing arbitrary data supplied by the drag source
24340      */
24341     onNodeEnter : function(n, dd, e, data){
24342         
24343     },
24344
24345     /**
24346      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24347      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24348      * overridden to provide the proper feedback.
24349      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24350      * {@link #getTargetFromEvent} for this node)
24351      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24352      * @param {Event} e The event
24353      * @param {Object} data An object containing arbitrary data supplied by the drag source
24354      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24355      * underlying {@link Roo.dd.StatusProxy} can be updated
24356      */
24357     onNodeOver : function(n, dd, e, data){
24358         return this.dropAllowed;
24359     },
24360
24361     /**
24362      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24363      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24364      * node-specific processing if necessary.
24365      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24366      * {@link #getTargetFromEvent} for this node)
24367      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24368      * @param {Event} e The event
24369      * @param {Object} data An object containing arbitrary data supplied by the drag source
24370      */
24371     onNodeOut : function(n, dd, e, data){
24372         
24373     },
24374
24375     /**
24376      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24377      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24378      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24379      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24380      * {@link #getTargetFromEvent} for this node)
24381      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24382      * @param {Event} e The event
24383      * @param {Object} data An object containing arbitrary data supplied by the drag source
24384      * @return {Boolean} True if the drop was valid, else false
24385      */
24386     onNodeDrop : function(n, dd, e, data){
24387         return false;
24388     },
24389
24390     /**
24391      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24392      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24393      * it should be overridden to provide the proper feedback if necessary.
24394      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24395      * @param {Event} e The event
24396      * @param {Object} data An object containing arbitrary data supplied by the drag source
24397      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24398      * underlying {@link Roo.dd.StatusProxy} can be updated
24399      */
24400     onContainerOver : function(dd, e, data){
24401         return this.dropNotAllowed;
24402     },
24403
24404     /**
24405      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24406      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24407      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24408      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24409      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24410      * @param {Event} e The event
24411      * @param {Object} data An object containing arbitrary data supplied by the drag source
24412      * @return {Boolean} True if the drop was valid, else false
24413      */
24414     onContainerDrop : function(dd, e, data){
24415         return false;
24416     },
24417
24418     /**
24419      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24420      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24421      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24422      * you should override this method and provide a custom implementation.
24423      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24424      * @param {Event} e The event
24425      * @param {Object} data An object containing arbitrary data supplied by the drag source
24426      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24427      * underlying {@link Roo.dd.StatusProxy} can be updated
24428      */
24429     notifyEnter : function(dd, e, data){
24430         return this.dropNotAllowed;
24431     },
24432
24433     /**
24434      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24435      * This method will be called on every mouse movement while the drag source is over the drop zone.
24436      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24437      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24438      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24439      * registered node, it will call {@link #onContainerOver}.
24440      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24441      * @param {Event} e The event
24442      * @param {Object} data An object containing arbitrary data supplied by the drag source
24443      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24444      * underlying {@link Roo.dd.StatusProxy} can be updated
24445      */
24446     notifyOver : function(dd, e, data){
24447         var n = this.getTargetFromEvent(e);
24448         if(!n){ // not over valid drop target
24449             if(this.lastOverNode){
24450                 this.onNodeOut(this.lastOverNode, dd, e, data);
24451                 this.lastOverNode = null;
24452             }
24453             return this.onContainerOver(dd, e, data);
24454         }
24455         if(this.lastOverNode != n){
24456             if(this.lastOverNode){
24457                 this.onNodeOut(this.lastOverNode, dd, e, data);
24458             }
24459             this.onNodeEnter(n, dd, e, data);
24460             this.lastOverNode = n;
24461         }
24462         return this.onNodeOver(n, dd, e, data);
24463     },
24464
24465     /**
24466      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24467      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24468      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24469      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24470      * @param {Event} e The event
24471      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24472      */
24473     notifyOut : function(dd, e, data){
24474         if(this.lastOverNode){
24475             this.onNodeOut(this.lastOverNode, dd, e, data);
24476             this.lastOverNode = null;
24477         }
24478     },
24479
24480     /**
24481      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24482      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24483      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24484      * otherwise it will call {@link #onContainerDrop}.
24485      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24486      * @param {Event} e The event
24487      * @param {Object} data An object containing arbitrary data supplied by the drag source
24488      * @return {Boolean} True if the drop was valid, else false
24489      */
24490     notifyDrop : function(dd, e, data){
24491         if(this.lastOverNode){
24492             this.onNodeOut(this.lastOverNode, dd, e, data);
24493             this.lastOverNode = null;
24494         }
24495         var n = this.getTargetFromEvent(e);
24496         return n ?
24497             this.onNodeDrop(n, dd, e, data) :
24498             this.onContainerDrop(dd, e, data);
24499     },
24500
24501     // private
24502     triggerCacheRefresh : function(){
24503         Roo.dd.DDM.refreshCache(this.groups);
24504     }  
24505 });/*
24506  * Based on:
24507  * Ext JS Library 1.1.1
24508  * Copyright(c) 2006-2007, Ext JS, LLC.
24509  *
24510  * Originally Released Under LGPL - original licence link has changed is not relivant.
24511  *
24512  * Fork - LGPL
24513  * <script type="text/javascript">
24514  */
24515
24516
24517 /**
24518  * @class Roo.data.SortTypes
24519  * @static
24520  * Defines the default sorting (casting?) comparison functions used when sorting data.
24521  */
24522 Roo.data.SortTypes = {
24523     /**
24524      * Default sort that does nothing
24525      * @param {Mixed} s The value being converted
24526      * @return {Mixed} The comparison value
24527      */
24528     none : function(s){
24529         return s;
24530     },
24531     
24532     /**
24533      * The regular expression used to strip tags
24534      * @type {RegExp}
24535      * @property
24536      */
24537     stripTagsRE : /<\/?[^>]+>/gi,
24538     
24539     /**
24540      * Strips all HTML tags to sort on text only
24541      * @param {Mixed} s The value being converted
24542      * @return {String} The comparison value
24543      */
24544     asText : function(s){
24545         return String(s).replace(this.stripTagsRE, "");
24546     },
24547     
24548     /**
24549      * Strips all HTML tags to sort on text only - Case insensitive
24550      * @param {Mixed} s The value being converted
24551      * @return {String} The comparison value
24552      */
24553     asUCText : function(s){
24554         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24555     },
24556     
24557     /**
24558      * Case insensitive string
24559      * @param {Mixed} s The value being converted
24560      * @return {String} The comparison value
24561      */
24562     asUCString : function(s) {
24563         return String(s).toUpperCase();
24564     },
24565     
24566     /**
24567      * Date sorting
24568      * @param {Mixed} s The value being converted
24569      * @return {Number} The comparison value
24570      */
24571     asDate : function(s) {
24572         if(!s){
24573             return 0;
24574         }
24575         if(s instanceof Date){
24576             return s.getTime();
24577         }
24578         return Date.parse(String(s));
24579     },
24580     
24581     /**
24582      * Float sorting
24583      * @param {Mixed} s The value being converted
24584      * @return {Float} The comparison value
24585      */
24586     asFloat : function(s) {
24587         var val = parseFloat(String(s).replace(/,/g, ""));
24588         if(isNaN(val)) {
24589             val = 0;
24590         }
24591         return val;
24592     },
24593     
24594     /**
24595      * Integer sorting
24596      * @param {Mixed} s The value being converted
24597      * @return {Number} The comparison value
24598      */
24599     asInt : function(s) {
24600         var val = parseInt(String(s).replace(/,/g, ""));
24601         if(isNaN(val)) {
24602             val = 0;
24603         }
24604         return val;
24605     }
24606 };/*
24607  * Based on:
24608  * Ext JS Library 1.1.1
24609  * Copyright(c) 2006-2007, Ext JS, LLC.
24610  *
24611  * Originally Released Under LGPL - original licence link has changed is not relivant.
24612  *
24613  * Fork - LGPL
24614  * <script type="text/javascript">
24615  */
24616
24617 /**
24618 * @class Roo.data.Record
24619  * Instances of this class encapsulate both record <em>definition</em> information, and record
24620  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24621  * to access Records cached in an {@link Roo.data.Store} object.<br>
24622  * <p>
24623  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24624  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24625  * objects.<br>
24626  * <p>
24627  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24628  * @constructor
24629  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24630  * {@link #create}. The parameters are the same.
24631  * @param {Array} data An associative Array of data values keyed by the field name.
24632  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24633  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24634  * not specified an integer id is generated.
24635  */
24636 Roo.data.Record = function(data, id){
24637     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24638     this.data = data;
24639 };
24640
24641 /**
24642  * Generate a constructor for a specific record layout.
24643  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24644  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24645  * Each field definition object may contain the following properties: <ul>
24646  * <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,
24647  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24648  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24649  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24650  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24651  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24652  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24653  * this may be omitted.</p></li>
24654  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24655  * <ul><li>auto (Default, implies no conversion)</li>
24656  * <li>string</li>
24657  * <li>int</li>
24658  * <li>float</li>
24659  * <li>boolean</li>
24660  * <li>date</li></ul></p></li>
24661  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24662  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24663  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24664  * by the Reader into an object that will be stored in the Record. It is passed the
24665  * following parameters:<ul>
24666  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24667  * </ul></p></li>
24668  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24669  * </ul>
24670  * <br>usage:<br><pre><code>
24671 var TopicRecord = Roo.data.Record.create(
24672     {name: 'title', mapping: 'topic_title'},
24673     {name: 'author', mapping: 'username'},
24674     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24675     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24676     {name: 'lastPoster', mapping: 'user2'},
24677     {name: 'excerpt', mapping: 'post_text'}
24678 );
24679
24680 var myNewRecord = new TopicRecord({
24681     title: 'Do my job please',
24682     author: 'noobie',
24683     totalPosts: 1,
24684     lastPost: new Date(),
24685     lastPoster: 'Animal',
24686     excerpt: 'No way dude!'
24687 });
24688 myStore.add(myNewRecord);
24689 </code></pre>
24690  * @method create
24691  * @static
24692  */
24693 Roo.data.Record.create = function(o){
24694     var f = function(){
24695         f.superclass.constructor.apply(this, arguments);
24696     };
24697     Roo.extend(f, Roo.data.Record);
24698     var p = f.prototype;
24699     p.fields = new Roo.util.MixedCollection(false, function(field){
24700         return field.name;
24701     });
24702     for(var i = 0, len = o.length; i < len; i++){
24703         p.fields.add(new Roo.data.Field(o[i]));
24704     }
24705     f.getField = function(name){
24706         return p.fields.get(name);  
24707     };
24708     return f;
24709 };
24710
24711 Roo.data.Record.AUTO_ID = 1000;
24712 Roo.data.Record.EDIT = 'edit';
24713 Roo.data.Record.REJECT = 'reject';
24714 Roo.data.Record.COMMIT = 'commit';
24715
24716 Roo.data.Record.prototype = {
24717     /**
24718      * Readonly flag - true if this record has been modified.
24719      * @type Boolean
24720      */
24721     dirty : false,
24722     editing : false,
24723     error: null,
24724     modified: null,
24725
24726     // private
24727     join : function(store){
24728         this.store = store;
24729     },
24730
24731     /**
24732      * Set the named field to the specified value.
24733      * @param {String} name The name of the field to set.
24734      * @param {Object} value The value to set the field to.
24735      */
24736     set : function(name, value){
24737         if(this.data[name] == value){
24738             return;
24739         }
24740         this.dirty = true;
24741         if(!this.modified){
24742             this.modified = {};
24743         }
24744         if(typeof this.modified[name] == 'undefined'){
24745             this.modified[name] = this.data[name];
24746         }
24747         this.data[name] = value;
24748         if(!this.editing && this.store){
24749             this.store.afterEdit(this);
24750         }       
24751     },
24752
24753     /**
24754      * Get the value of the named field.
24755      * @param {String} name The name of the field to get the value of.
24756      * @return {Object} The value of the field.
24757      */
24758     get : function(name){
24759         return this.data[name]; 
24760     },
24761
24762     // private
24763     beginEdit : function(){
24764         this.editing = true;
24765         this.modified = {}; 
24766     },
24767
24768     // private
24769     cancelEdit : function(){
24770         this.editing = false;
24771         delete this.modified;
24772     },
24773
24774     // private
24775     endEdit : function(){
24776         this.editing = false;
24777         if(this.dirty && this.store){
24778             this.store.afterEdit(this);
24779         }
24780     },
24781
24782     /**
24783      * Usually called by the {@link Roo.data.Store} which owns the Record.
24784      * Rejects all changes made to the Record since either creation, or the last commit operation.
24785      * Modified fields are reverted to their original values.
24786      * <p>
24787      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24788      * of reject operations.
24789      */
24790     reject : function(){
24791         var m = this.modified;
24792         for(var n in m){
24793             if(typeof m[n] != "function"){
24794                 this.data[n] = m[n];
24795             }
24796         }
24797         this.dirty = false;
24798         delete this.modified;
24799         this.editing = false;
24800         if(this.store){
24801             this.store.afterReject(this);
24802         }
24803     },
24804
24805     /**
24806      * Usually called by the {@link Roo.data.Store} which owns the Record.
24807      * Commits all changes made to the Record since either creation, or the last commit operation.
24808      * <p>
24809      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24810      * of commit operations.
24811      */
24812     commit : function(){
24813         this.dirty = false;
24814         delete this.modified;
24815         this.editing = false;
24816         if(this.store){
24817             this.store.afterCommit(this);
24818         }
24819     },
24820
24821     // private
24822     hasError : function(){
24823         return this.error != null;
24824     },
24825
24826     // private
24827     clearError : function(){
24828         this.error = null;
24829     },
24830
24831     /**
24832      * Creates a copy of this record.
24833      * @param {String} id (optional) A new record id if you don't want to use this record's id
24834      * @return {Record}
24835      */
24836     copy : function(newId) {
24837         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24838     }
24839 };/*
24840  * Based on:
24841  * Ext JS Library 1.1.1
24842  * Copyright(c) 2006-2007, Ext JS, LLC.
24843  *
24844  * Originally Released Under LGPL - original licence link has changed is not relivant.
24845  *
24846  * Fork - LGPL
24847  * <script type="text/javascript">
24848  */
24849
24850
24851
24852 /**
24853  * @class Roo.data.Store
24854  * @extends Roo.util.Observable
24855  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24856  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24857  * <p>
24858  * 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
24859  * has no knowledge of the format of the data returned by the Proxy.<br>
24860  * <p>
24861  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24862  * instances from the data object. These records are cached and made available through accessor functions.
24863  * @constructor
24864  * Creates a new Store.
24865  * @param {Object} config A config object containing the objects needed for the Store to access data,
24866  * and read the data into Records.
24867  */
24868 Roo.data.Store = function(config){
24869     this.data = new Roo.util.MixedCollection(false);
24870     this.data.getKey = function(o){
24871         return o.id;
24872     };
24873     this.baseParams = {};
24874     // private
24875     this.paramNames = {
24876         "start" : "start",
24877         "limit" : "limit",
24878         "sort" : "sort",
24879         "dir" : "dir",
24880         "multisort" : "_multisort"
24881     };
24882
24883     if(config && config.data){
24884         this.inlineData = config.data;
24885         delete config.data;
24886     }
24887
24888     Roo.apply(this, config);
24889     
24890     if(this.reader){ // reader passed
24891         this.reader = Roo.factory(this.reader, Roo.data);
24892         this.reader.xmodule = this.xmodule || false;
24893         if(!this.recordType){
24894             this.recordType = this.reader.recordType;
24895         }
24896         if(this.reader.onMetaChange){
24897             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24898         }
24899     }
24900
24901     if(this.recordType){
24902         this.fields = this.recordType.prototype.fields;
24903     }
24904     this.modified = [];
24905
24906     this.addEvents({
24907         /**
24908          * @event datachanged
24909          * Fires when the data cache has changed, and a widget which is using this Store
24910          * as a Record cache should refresh its view.
24911          * @param {Store} this
24912          */
24913         datachanged : true,
24914         /**
24915          * @event metachange
24916          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24917          * @param {Store} this
24918          * @param {Object} meta The JSON metadata
24919          */
24920         metachange : true,
24921         /**
24922          * @event add
24923          * Fires when Records have been added to the Store
24924          * @param {Store} this
24925          * @param {Roo.data.Record[]} records The array of Records added
24926          * @param {Number} index The index at which the record(s) were added
24927          */
24928         add : true,
24929         /**
24930          * @event remove
24931          * Fires when a Record has been removed from the Store
24932          * @param {Store} this
24933          * @param {Roo.data.Record} record The Record that was removed
24934          * @param {Number} index The index at which the record was removed
24935          */
24936         remove : true,
24937         /**
24938          * @event update
24939          * Fires when a Record has been updated
24940          * @param {Store} this
24941          * @param {Roo.data.Record} record The Record that was updated
24942          * @param {String} operation The update operation being performed.  Value may be one of:
24943          * <pre><code>
24944  Roo.data.Record.EDIT
24945  Roo.data.Record.REJECT
24946  Roo.data.Record.COMMIT
24947          * </code></pre>
24948          */
24949         update : true,
24950         /**
24951          * @event clear
24952          * Fires when the data cache has been cleared.
24953          * @param {Store} this
24954          */
24955         clear : true,
24956         /**
24957          * @event beforeload
24958          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24959          * the load action will be canceled.
24960          * @param {Store} this
24961          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24962          */
24963         beforeload : true,
24964         /**
24965          * @event beforeloadadd
24966          * Fires after a new set of Records has been loaded.
24967          * @param {Store} this
24968          * @param {Roo.data.Record[]} records The Records that were loaded
24969          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24970          */
24971         beforeloadadd : true,
24972         /**
24973          * @event load
24974          * Fires after a new set of Records has been loaded, before they are added to the store.
24975          * @param {Store} this
24976          * @param {Roo.data.Record[]} records The Records that were loaded
24977          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24978          * @params {Object} return from reader
24979          */
24980         load : true,
24981         /**
24982          * @event loadexception
24983          * Fires if an exception occurs in the Proxy during loading.
24984          * Called with the signature of the Proxy's "loadexception" event.
24985          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24986          * 
24987          * @param {Proxy} 
24988          * @param {Object} ret return data from JsonData.reader() - success, totalRecords, records
24989          * @param {Object} opts - load Options
24990          * @param {Object} jsonData from your request (normally this contains the Exception)
24991          */
24992         loadexception : true
24993     });
24994     
24995     if(this.proxy){
24996         this.proxy = Roo.factory(this.proxy, Roo.data);
24997         this.proxy.xmodule = this.xmodule || false;
24998         this.relayEvents(this.proxy,  ["loadexception"]);
24999     }
25000     this.sortToggle = {};
25001     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
25002
25003     Roo.data.Store.superclass.constructor.call(this);
25004
25005     if(this.inlineData){
25006         this.loadData(this.inlineData);
25007         delete this.inlineData;
25008     }
25009 };
25010
25011 Roo.extend(Roo.data.Store, Roo.util.Observable, {
25012      /**
25013     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
25014     * without a remote query - used by combo/forms at present.
25015     */
25016     
25017     /**
25018     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
25019     */
25020     /**
25021     * @cfg {Array} data Inline data to be loaded when the store is initialized.
25022     */
25023     /**
25024     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
25025     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
25026     */
25027     /**
25028     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
25029     * on any HTTP request
25030     */
25031     /**
25032     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
25033     */
25034     /**
25035     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
25036     */
25037     multiSort: false,
25038     /**
25039     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
25040     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
25041     */
25042     remoteSort : false,
25043
25044     /**
25045     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
25046      * loaded or when a record is removed. (defaults to false).
25047     */
25048     pruneModifiedRecords : false,
25049
25050     // private
25051     lastOptions : null,
25052
25053     /**
25054      * Add Records to the Store and fires the add event.
25055      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25056      */
25057     add : function(records){
25058         records = [].concat(records);
25059         for(var i = 0, len = records.length; i < len; i++){
25060             records[i].join(this);
25061         }
25062         var index = this.data.length;
25063         this.data.addAll(records);
25064         this.fireEvent("add", this, records, index);
25065     },
25066
25067     /**
25068      * Remove a Record from the Store and fires the remove event.
25069      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
25070      */
25071     remove : function(record){
25072         var index = this.data.indexOf(record);
25073         this.data.removeAt(index);
25074  
25075         if(this.pruneModifiedRecords){
25076             this.modified.remove(record);
25077         }
25078         this.fireEvent("remove", this, record, index);
25079     },
25080
25081     /**
25082      * Remove all Records from the Store and fires the clear event.
25083      */
25084     removeAll : function(){
25085         this.data.clear();
25086         if(this.pruneModifiedRecords){
25087             this.modified = [];
25088         }
25089         this.fireEvent("clear", this);
25090     },
25091
25092     /**
25093      * Inserts Records to the Store at the given index and fires the add event.
25094      * @param {Number} index The start index at which to insert the passed Records.
25095      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
25096      */
25097     insert : function(index, records){
25098         records = [].concat(records);
25099         for(var i = 0, len = records.length; i < len; i++){
25100             this.data.insert(index, records[i]);
25101             records[i].join(this);
25102         }
25103         this.fireEvent("add", this, records, index);
25104     },
25105
25106     /**
25107      * Get the index within the cache of the passed Record.
25108      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
25109      * @return {Number} The index of the passed Record. Returns -1 if not found.
25110      */
25111     indexOf : function(record){
25112         return this.data.indexOf(record);
25113     },
25114
25115     /**
25116      * Get the index within the cache of the Record with the passed id.
25117      * @param {String} id The id of the Record to find.
25118      * @return {Number} The index of the Record. Returns -1 if not found.
25119      */
25120     indexOfId : function(id){
25121         return this.data.indexOfKey(id);
25122     },
25123
25124     /**
25125      * Get the Record with the specified id.
25126      * @param {String} id The id of the Record to find.
25127      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
25128      */
25129     getById : function(id){
25130         return this.data.key(id);
25131     },
25132
25133     /**
25134      * Get the Record at the specified index.
25135      * @param {Number} index The index of the Record to find.
25136      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
25137      */
25138     getAt : function(index){
25139         return this.data.itemAt(index);
25140     },
25141
25142     /**
25143      * Returns a range of Records between specified indices.
25144      * @param {Number} startIndex (optional) The starting index (defaults to 0)
25145      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
25146      * @return {Roo.data.Record[]} An array of Records
25147      */
25148     getRange : function(start, end){
25149         return this.data.getRange(start, end);
25150     },
25151
25152     // private
25153     storeOptions : function(o){
25154         o = Roo.apply({}, o);
25155         delete o.callback;
25156         delete o.scope;
25157         this.lastOptions = o;
25158     },
25159
25160     /**
25161      * Loads the Record cache from the configured Proxy using the configured Reader.
25162      * <p>
25163      * If using remote paging, then the first load call must specify the <em>start</em>
25164      * and <em>limit</em> properties in the options.params property to establish the initial
25165      * position within the dataset, and the number of Records to cache on each read from the Proxy.
25166      * <p>
25167      * <strong>It is important to note that for remote data sources, loading is asynchronous,
25168      * and this call will return before the new data has been loaded. Perform any post-processing
25169      * in a callback function, or in a "load" event handler.</strong>
25170      * <p>
25171      * @param {Object} options An object containing properties which control loading options:<ul>
25172      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
25173      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
25174      * <pre>
25175                 {
25176                     data : data,  // array of key=>value data like JsonReader
25177                     total : data.length,
25178                     success : true
25179                     
25180                 }
25181         </pre>
25182             }.</li>
25183      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
25184      * passed the following arguments:<ul>
25185      * <li>r : Roo.data.Record[]</li>
25186      * <li>options: Options object from the load call</li>
25187      * <li>success: Boolean success indicator</li></ul></li>
25188      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
25189      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
25190      * </ul>
25191      */
25192     load : function(options){
25193         options = options || {};
25194         if(this.fireEvent("beforeload", this, options) !== false){
25195             this.storeOptions(options);
25196             var p = Roo.apply(options.params || {}, this.baseParams);
25197             // if meta was not loaded from remote source.. try requesting it.
25198             if (!this.reader.metaFromRemote) {
25199                 p._requestMeta = 1;
25200             }
25201             if(this.sortInfo && this.remoteSort){
25202                 var pn = this.paramNames;
25203                 p[pn["sort"]] = this.sortInfo.field;
25204                 p[pn["dir"]] = this.sortInfo.direction;
25205             }
25206             if (this.multiSort) {
25207                 var pn = this.paramNames;
25208                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25209             }
25210             
25211             this.proxy.load(p, this.reader, this.loadRecords, this, options);
25212         }
25213     },
25214
25215     /**
25216      * Reloads the Record cache from the configured Proxy using the configured Reader and
25217      * the options from the last load operation performed.
25218      * @param {Object} options (optional) An object containing properties which may override the options
25219      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25220      * the most recently used options are reused).
25221      */
25222     reload : function(options){
25223         this.load(Roo.applyIf(options||{}, this.lastOptions));
25224     },
25225
25226     // private
25227     // Called as a callback by the Reader during a load operation.
25228     loadRecords : function(o, options, success){
25229          
25230         if(!o){
25231             if(success !== false){
25232                 this.fireEvent("load", this, [], options, o);
25233             }
25234             if(options.callback){
25235                 options.callback.call(options.scope || this, [], options, false);
25236             }
25237             return;
25238         }
25239         // if data returned failure - throw an exception.
25240         if (o.success === false) {
25241             // show a message if no listener is registered.
25242             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25243                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25244             }
25245             // loadmask wil be hooked into this..
25246             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25247             return;
25248         }
25249         var r = o.records, t = o.totalRecords || r.length;
25250         
25251         this.fireEvent("beforeloadadd", this, r, options, o);
25252         
25253         if(!options || options.add !== true){
25254             if(this.pruneModifiedRecords){
25255                 this.modified = [];
25256             }
25257             for(var i = 0, len = r.length; i < len; i++){
25258                 r[i].join(this);
25259             }
25260             if(this.snapshot){
25261                 this.data = this.snapshot;
25262                 delete this.snapshot;
25263             }
25264             this.data.clear();
25265             this.data.addAll(r);
25266             this.totalLength = t;
25267             this.applySort();
25268             this.fireEvent("datachanged", this);
25269         }else{
25270             this.totalLength = Math.max(t, this.data.length+r.length);
25271             this.add(r);
25272         }
25273         
25274         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25275                 
25276             var e = new Roo.data.Record({});
25277
25278             e.set(this.parent.displayField, this.parent.emptyTitle);
25279             e.set(this.parent.valueField, '');
25280
25281             this.insert(0, e);
25282         }
25283             
25284         this.fireEvent("load", this, r, options, o);
25285         if(options.callback){
25286             options.callback.call(options.scope || this, r, options, true);
25287         }
25288     },
25289
25290
25291     /**
25292      * Loads data from a passed data block. A Reader which understands the format of the data
25293      * must have been configured in the constructor.
25294      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25295      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25296      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25297      */
25298     loadData : function(o, append){
25299         var r = this.reader.readRecords(o);
25300         this.loadRecords(r, {add: append}, true);
25301     },
25302     
25303      /**
25304      * using 'cn' the nested child reader read the child array into it's child stores.
25305      * @param {Object} rec The record with a 'children array
25306      */
25307     loadDataFromChildren : function(rec)
25308     {
25309         this.loadData(this.reader.toLoadData(rec));
25310     },
25311     
25312
25313     /**
25314      * Gets the number of cached records.
25315      * <p>
25316      * <em>If using paging, this may not be the total size of the dataset. If the data object
25317      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25318      * the data set size</em>
25319      */
25320     getCount : function(){
25321         return this.data.length || 0;
25322     },
25323
25324     /**
25325      * Gets the total number of records in the dataset as returned by the server.
25326      * <p>
25327      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25328      * the dataset size</em>
25329      */
25330     getTotalCount : function(){
25331         return this.totalLength || 0;
25332     },
25333
25334     /**
25335      * Returns the sort state of the Store as an object with two properties:
25336      * <pre><code>
25337  field {String} The name of the field by which the Records are sorted
25338  direction {String} The sort order, "ASC" or "DESC"
25339      * </code></pre>
25340      */
25341     getSortState : function(){
25342         return this.sortInfo;
25343     },
25344
25345     // private
25346     applySort : function(){
25347         if(this.sortInfo && !this.remoteSort){
25348             var s = this.sortInfo, f = s.field;
25349             var st = this.fields.get(f).sortType;
25350             var fn = function(r1, r2){
25351                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25352                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25353             };
25354             this.data.sort(s.direction, fn);
25355             if(this.snapshot && this.snapshot != this.data){
25356                 this.snapshot.sort(s.direction, fn);
25357             }
25358         }
25359     },
25360
25361     /**
25362      * Sets the default sort column and order to be used by the next load operation.
25363      * @param {String} fieldName The name of the field to sort by.
25364      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25365      */
25366     setDefaultSort : function(field, dir){
25367         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25368     },
25369
25370     /**
25371      * Sort the Records.
25372      * If remote sorting is used, the sort is performed on the server, and the cache is
25373      * reloaded. If local sorting is used, the cache is sorted internally.
25374      * @param {String} fieldName The name of the field to sort by.
25375      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25376      */
25377     sort : function(fieldName, dir){
25378         var f = this.fields.get(fieldName);
25379         if(!dir){
25380             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25381             
25382             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25383                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25384             }else{
25385                 dir = f.sortDir;
25386             }
25387         }
25388         this.sortToggle[f.name] = dir;
25389         this.sortInfo = {field: f.name, direction: dir};
25390         if(!this.remoteSort){
25391             this.applySort();
25392             this.fireEvent("datachanged", this);
25393         }else{
25394             this.load(this.lastOptions);
25395         }
25396     },
25397
25398     /**
25399      * Calls the specified function for each of the Records in the cache.
25400      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25401      * Returning <em>false</em> aborts and exits the iteration.
25402      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25403      */
25404     each : function(fn, scope){
25405         this.data.each(fn, scope);
25406     },
25407
25408     /**
25409      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25410      * (e.g., during paging).
25411      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25412      */
25413     getModifiedRecords : function(){
25414         return this.modified;
25415     },
25416
25417     // private
25418     createFilterFn : function(property, value, anyMatch){
25419         if(!value.exec){ // not a regex
25420             value = String(value);
25421             if(value.length == 0){
25422                 return false;
25423             }
25424             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25425         }
25426         return function(r){
25427             return value.test(r.data[property]);
25428         };
25429     },
25430
25431     /**
25432      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25433      * @param {String} property A field on your records
25434      * @param {Number} start The record index to start at (defaults to 0)
25435      * @param {Number} end The last record index to include (defaults to length - 1)
25436      * @return {Number} The sum
25437      */
25438     sum : function(property, start, end){
25439         var rs = this.data.items, v = 0;
25440         start = start || 0;
25441         end = (end || end === 0) ? end : rs.length-1;
25442
25443         for(var i = start; i <= end; i++){
25444             v += (rs[i].data[property] || 0);
25445         }
25446         return v;
25447     },
25448
25449     /**
25450      * Filter the records by a specified property.
25451      * @param {String} field A field on your records
25452      * @param {String/RegExp} value Either a string that the field
25453      * should start with or a RegExp to test against the field
25454      * @param {Boolean} anyMatch True to match any part not just the beginning
25455      */
25456     filter : function(property, value, anyMatch){
25457         var fn = this.createFilterFn(property, value, anyMatch);
25458         return fn ? this.filterBy(fn) : this.clearFilter();
25459     },
25460
25461     /**
25462      * Filter by a function. The specified function will be called with each
25463      * record in this data source. If the function returns true the record is included,
25464      * otherwise it is filtered.
25465      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25466      * @param {Object} scope (optional) The scope of the function (defaults to this)
25467      */
25468     filterBy : function(fn, scope){
25469         this.snapshot = this.snapshot || this.data;
25470         this.data = this.queryBy(fn, scope||this);
25471         this.fireEvent("datachanged", this);
25472     },
25473
25474     /**
25475      * Query the records by a specified property.
25476      * @param {String} field A field on your records
25477      * @param {String/RegExp} value Either a string that the field
25478      * should start with or a RegExp to test against the field
25479      * @param {Boolean} anyMatch True to match any part not just the beginning
25480      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25481      */
25482     query : function(property, value, anyMatch){
25483         var fn = this.createFilterFn(property, value, anyMatch);
25484         return fn ? this.queryBy(fn) : this.data.clone();
25485     },
25486
25487     /**
25488      * Query by a function. The specified function will be called with each
25489      * record in this data source. If the function returns true the record is included
25490      * in the results.
25491      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25492      * @param {Object} scope (optional) The scope of the function (defaults to this)
25493       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25494      **/
25495     queryBy : function(fn, scope){
25496         var data = this.snapshot || this.data;
25497         return data.filterBy(fn, scope||this);
25498     },
25499
25500     /**
25501      * Collects unique values for a particular dataIndex from this store.
25502      * @param {String} dataIndex The property to collect
25503      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25504      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25505      * @return {Array} An array of the unique values
25506      **/
25507     collect : function(dataIndex, allowNull, bypassFilter){
25508         var d = (bypassFilter === true && this.snapshot) ?
25509                 this.snapshot.items : this.data.items;
25510         var v, sv, r = [], l = {};
25511         for(var i = 0, len = d.length; i < len; i++){
25512             v = d[i].data[dataIndex];
25513             sv = String(v);
25514             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25515                 l[sv] = true;
25516                 r[r.length] = v;
25517             }
25518         }
25519         return r;
25520     },
25521
25522     /**
25523      * Revert to a view of the Record cache with no filtering applied.
25524      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25525      */
25526     clearFilter : function(suppressEvent){
25527         if(this.snapshot && this.snapshot != this.data){
25528             this.data = this.snapshot;
25529             delete this.snapshot;
25530             if(suppressEvent !== true){
25531                 this.fireEvent("datachanged", this);
25532             }
25533         }
25534     },
25535
25536     // private
25537     afterEdit : function(record){
25538         if(this.modified.indexOf(record) == -1){
25539             this.modified.push(record);
25540         }
25541         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25542     },
25543     
25544     // private
25545     afterReject : function(record){
25546         this.modified.remove(record);
25547         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25548     },
25549
25550     // private
25551     afterCommit : function(record){
25552         this.modified.remove(record);
25553         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25554     },
25555
25556     /**
25557      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25558      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25559      */
25560     commitChanges : function(){
25561         var m = this.modified.slice(0);
25562         this.modified = [];
25563         for(var i = 0, len = m.length; i < len; i++){
25564             m[i].commit();
25565         }
25566     },
25567
25568     /**
25569      * Cancel outstanding changes on all changed records.
25570      */
25571     rejectChanges : function(){
25572         var m = this.modified.slice(0);
25573         this.modified = [];
25574         for(var i = 0, len = m.length; i < len; i++){
25575             m[i].reject();
25576         }
25577     },
25578
25579     onMetaChange : function(meta, rtype, o){
25580         this.recordType = rtype;
25581         this.fields = rtype.prototype.fields;
25582         delete this.snapshot;
25583         this.sortInfo = meta.sortInfo || this.sortInfo;
25584         this.modified = [];
25585         this.fireEvent('metachange', this, this.reader.meta);
25586     },
25587     
25588     moveIndex : function(data, type)
25589     {
25590         var index = this.indexOf(data);
25591         
25592         var newIndex = index + type;
25593         
25594         this.remove(data);
25595         
25596         this.insert(newIndex, data);
25597         
25598     }
25599 });/*
25600  * Based on:
25601  * Ext JS Library 1.1.1
25602  * Copyright(c) 2006-2007, Ext JS, LLC.
25603  *
25604  * Originally Released Under LGPL - original licence link has changed is not relivant.
25605  *
25606  * Fork - LGPL
25607  * <script type="text/javascript">
25608  */
25609
25610 /**
25611  * @class Roo.data.SimpleStore
25612  * @extends Roo.data.Store
25613  * Small helper class to make creating Stores from Array data easier.
25614  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25615  * @cfg {Array} fields An array of field definition objects, or field name strings.
25616  * @cfg {Object} an existing reader (eg. copied from another store)
25617  * @cfg {Array} data The multi-dimensional array of data
25618  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25619  * @cfg {Roo.data.Reader} reader  [not-required] 
25620  * @constructor
25621  * @param {Object} config
25622  */
25623 Roo.data.SimpleStore = function(config)
25624 {
25625     Roo.data.SimpleStore.superclass.constructor.call(this, {
25626         isLocal : true,
25627         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25628                 id: config.id
25629             },
25630             Roo.data.Record.create(config.fields)
25631         ),
25632         proxy : new Roo.data.MemoryProxy(config.data)
25633     });
25634     this.load();
25635 };
25636 Roo.extend(Roo.data.SimpleStore, 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 /**
25649  * @extends Roo.data.Store
25650  * @class Roo.data.JsonStore
25651  * Small helper class to make creating Stores for JSON data easier. <br/>
25652 <pre><code>
25653 var store = new Roo.data.JsonStore({
25654     url: 'get-images.php',
25655     root: 'images',
25656     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25657 });
25658 </code></pre>
25659  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25660  * JsonReader and HttpProxy (unless inline data is provided).</b>
25661  * @cfg {Array} fields An array of field definition objects, or field name strings.
25662  * @constructor
25663  * @param {Object} config
25664  */
25665 Roo.data.JsonStore = function(c){
25666     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25667         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25668         reader: new Roo.data.JsonReader(c, c.fields)
25669     }));
25670 };
25671 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25672  * Based on:
25673  * Ext JS Library 1.1.1
25674  * Copyright(c) 2006-2007, Ext JS, LLC.
25675  *
25676  * Originally Released Under LGPL - original licence link has changed is not relivant.
25677  *
25678  * Fork - LGPL
25679  * <script type="text/javascript">
25680  */
25681
25682  
25683 Roo.data.Field = function(config){
25684     if(typeof config == "string"){
25685         config = {name: config};
25686     }
25687     Roo.apply(this, config);
25688     
25689     if(!this.type){
25690         this.type = "auto";
25691     }
25692     
25693     var st = Roo.data.SortTypes;
25694     // named sortTypes are supported, here we look them up
25695     if(typeof this.sortType == "string"){
25696         this.sortType = st[this.sortType];
25697     }
25698     
25699     // set default sortType for strings and dates
25700     if(!this.sortType){
25701         switch(this.type){
25702             case "string":
25703                 this.sortType = st.asUCString;
25704                 break;
25705             case "date":
25706                 this.sortType = st.asDate;
25707                 break;
25708             default:
25709                 this.sortType = st.none;
25710         }
25711     }
25712
25713     // define once
25714     var stripRe = /[\$,%]/g;
25715
25716     // prebuilt conversion function for this field, instead of
25717     // switching every time we're reading a value
25718     if(!this.convert){
25719         var cv, dateFormat = this.dateFormat;
25720         switch(this.type){
25721             case "":
25722             case "auto":
25723             case undefined:
25724                 cv = function(v){ return v; };
25725                 break;
25726             case "string":
25727                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25728                 break;
25729             case "int":
25730                 cv = function(v){
25731                     return v !== undefined && v !== null && v !== '' ?
25732                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25733                     };
25734                 break;
25735             case "float":
25736                 cv = function(v){
25737                     return v !== undefined && v !== null && v !== '' ?
25738                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25739                     };
25740                 break;
25741             case "bool":
25742             case "boolean":
25743                 cv = function(v){ return v === true || v === "true" || v == 1; };
25744                 break;
25745             case "date":
25746                 cv = function(v){
25747                     if(!v){
25748                         return '';
25749                     }
25750                     if(v instanceof Date){
25751                         return v;
25752                     }
25753                     if(dateFormat){
25754                         if(dateFormat == "timestamp"){
25755                             return new Date(v*1000);
25756                         }
25757                         return Date.parseDate(v, dateFormat);
25758                     }
25759                     var parsed = Date.parse(v);
25760                     return parsed ? new Date(parsed) : null;
25761                 };
25762              break;
25763             
25764         }
25765         this.convert = cv;
25766     }
25767 };
25768
25769 Roo.data.Field.prototype = {
25770     dateFormat: null,
25771     defaultValue: "",
25772     mapping: null,
25773     sortType : null,
25774     sortDir : "ASC"
25775 };/*
25776  * Based on:
25777  * Ext JS Library 1.1.1
25778  * Copyright(c) 2006-2007, Ext JS, LLC.
25779  *
25780  * Originally Released Under LGPL - original licence link has changed is not relivant.
25781  *
25782  * Fork - LGPL
25783  * <script type="text/javascript">
25784  */
25785  
25786 // Base class for reading structured data from a data source.  This class is intended to be
25787 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25788
25789 /**
25790  * @class Roo.data.DataReader
25791  * @abstract
25792  * Base class for reading structured data from a data source.  This class is intended to be
25793  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25794  */
25795
25796 Roo.data.DataReader = function(meta, recordType){
25797     
25798     this.meta = meta;
25799     
25800     this.recordType = recordType instanceof Array ? 
25801         Roo.data.Record.create(recordType) : recordType;
25802 };
25803
25804 Roo.data.DataReader.prototype = {
25805     
25806     
25807     readerType : 'Data',
25808      /**
25809      * Create an empty record
25810      * @param {Object} data (optional) - overlay some values
25811      * @return {Roo.data.Record} record created.
25812      */
25813     newRow :  function(d) {
25814         var da =  {};
25815         this.recordType.prototype.fields.each(function(c) {
25816             switch( c.type) {
25817                 case 'int' : da[c.name] = 0; break;
25818                 case 'date' : da[c.name] = new Date(); break;
25819                 case 'float' : da[c.name] = 0.0; break;
25820                 case 'boolean' : da[c.name] = false; break;
25821                 default : da[c.name] = ""; break;
25822             }
25823             
25824         });
25825         return new this.recordType(Roo.apply(da, d));
25826     }
25827     
25828     
25829 };/*
25830  * Based on:
25831  * Ext JS Library 1.1.1
25832  * Copyright(c) 2006-2007, Ext JS, LLC.
25833  *
25834  * Originally Released Under LGPL - original licence link has changed is not relivant.
25835  *
25836  * Fork - LGPL
25837  * <script type="text/javascript">
25838  */
25839
25840 /**
25841  * @class Roo.data.DataProxy
25842  * @extends Roo.util.Observable
25843  * @abstract
25844  * This class is an abstract base class for implementations which provide retrieval of
25845  * unformatted data objects.<br>
25846  * <p>
25847  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25848  * (of the appropriate type which knows how to parse the data object) to provide a block of
25849  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25850  * <p>
25851  * Custom implementations must implement the load method as described in
25852  * {@link Roo.data.HttpProxy#load}.
25853  */
25854 Roo.data.DataProxy = function(){
25855     this.addEvents({
25856         /**
25857          * @event beforeload
25858          * Fires before a network request is made to retrieve a data object.
25859          * @param {Object} This DataProxy object.
25860          * @param {Object} params The params parameter to the load function.
25861          */
25862         beforeload : true,
25863         /**
25864          * @event load
25865          * Fires before the load method's callback is called.
25866          * @param {Object} This DataProxy object.
25867          * @param {Object} o The data object.
25868          * @param {Object} arg The callback argument object passed to the load function.
25869          */
25870         load : true,
25871         /**
25872          * @event loadexception
25873          * Fires if an Exception occurs during data retrieval.
25874          * @param {Object} This DataProxy object.
25875          * @param {Object} o The data object.
25876          * @param {Object} arg The callback argument object passed to the load function.
25877          * @param {Object} e The Exception.
25878          */
25879         loadexception : true
25880     });
25881     Roo.data.DataProxy.superclass.constructor.call(this);
25882 };
25883
25884 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25885
25886     /**
25887      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25888      */
25889 /*
25890  * Based on:
25891  * Ext JS Library 1.1.1
25892  * Copyright(c) 2006-2007, Ext JS, LLC.
25893  *
25894  * Originally Released Under LGPL - original licence link has changed is not relivant.
25895  *
25896  * Fork - LGPL
25897  * <script type="text/javascript">
25898  */
25899 /**
25900  * @class Roo.data.MemoryProxy
25901  * @extends Roo.data.DataProxy
25902  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25903  * to the Reader when its load method is called.
25904  * @constructor
25905  * @param {Object} config  A config object containing the objects needed for the Store to access data,
25906  */
25907 Roo.data.MemoryProxy = function(config){
25908     var data = config;
25909     if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
25910         data = config.data;
25911     }
25912     Roo.data.MemoryProxy.superclass.constructor.call(this);
25913     this.data = data;
25914 };
25915
25916 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25917     
25918     /**
25919      *  @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25920      */
25921     /**
25922      * Load data from the requested source (in this case an in-memory
25923      * data object passed to the constructor), read the data object into
25924      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25925      * process that block using the passed callback.
25926      * @param {Object} params This parameter is not used by the MemoryProxy class.
25927      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25928      * object into a block of Roo.data.Records.
25929      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25930      * The function must be passed <ul>
25931      * <li>The Record block object</li>
25932      * <li>The "arg" argument from the load function</li>
25933      * <li>A boolean success indicator</li>
25934      * </ul>
25935      * @param {Object} scope The scope in which to call the callback
25936      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25937      */
25938     load : function(params, reader, callback, scope, arg){
25939         params = params || {};
25940         var result;
25941         try {
25942             result = reader.readRecords(params.data ? params.data :this.data);
25943         }catch(e){
25944             this.fireEvent("loadexception", this, arg, null, e);
25945             callback.call(scope, null, arg, false);
25946             return;
25947         }
25948         callback.call(scope, result, arg, true);
25949     },
25950     
25951     // private
25952     update : function(params, records){
25953         
25954     }
25955 });/*
25956  * Based on:
25957  * Ext JS Library 1.1.1
25958  * Copyright(c) 2006-2007, Ext JS, LLC.
25959  *
25960  * Originally Released Under LGPL - original licence link has changed is not relivant.
25961  *
25962  * Fork - LGPL
25963  * <script type="text/javascript">
25964  */
25965 /**
25966  * @class Roo.data.HttpProxy
25967  * @extends Roo.data.DataProxy
25968  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25969  * configured to reference a certain URL.<br><br>
25970  * <p>
25971  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25972  * from which the running page was served.<br><br>
25973  * <p>
25974  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25975  * <p>
25976  * Be aware that to enable the browser to parse an XML document, the server must set
25977  * the Content-Type header in the HTTP response to "text/xml".
25978  * @constructor
25979  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25980  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25981  * will be used to make the request.
25982  */
25983 Roo.data.HttpProxy = function(conn){
25984     Roo.data.HttpProxy.superclass.constructor.call(this);
25985     // is conn a conn config or a real conn?
25986     this.conn = conn;
25987     this.useAjax = !conn || !conn.events;
25988   
25989 };
25990
25991 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25992     // thse are take from connection...
25993     
25994     /**
25995      * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
25996      */
25997     /**
25998      * @cfg {Object} extraParams  An object containing properties which are used as
25999      * extra parameters to each request made by this object. (defaults to undefined)
26000      */
26001     /**
26002      * @cfg {Object} defaultHeaders   An object containing request headers which are added
26003      *  to each request made by this object. (defaults to undefined)
26004      */
26005     /**
26006      * @cfg {String} method (GET|POST)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
26007      */
26008     /**
26009      * @cfg {Number} timeout The timeout in milliseconds to be used for requests. (defaults to 30000)
26010      */
26011      /**
26012      * @cfg {Boolean} autoAbort Whether this request should abort any pending requests. (defaults to false)
26013      * @type Boolean
26014      */
26015   
26016
26017     /**
26018      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
26019      * @type Boolean
26020      */
26021     /**
26022      * Return the {@link Roo.data.Connection} object being used by this Proxy.
26023      * @return {Connection} The Connection object. This object may be used to subscribe to events on
26024      * a finer-grained basis than the DataProxy events.
26025      */
26026     getConnection : function(){
26027         return this.useAjax ? Roo.Ajax : this.conn;
26028     },
26029
26030     /**
26031      * Load data from the configured {@link Roo.data.Connection}, read the data object into
26032      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
26033      * process that block using the passed callback.
26034      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26035      * for the request to the remote server.
26036      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26037      * object into a block of Roo.data.Records.
26038      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26039      * The function must be passed <ul>
26040      * <li>The Record block object</li>
26041      * <li>The "arg" argument from the load function</li>
26042      * <li>A boolean success indicator</li>
26043      * </ul>
26044      * @param {Object} scope The scope in which to call the callback
26045      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26046      */
26047     load : function(params, reader, callback, scope, arg){
26048         if(this.fireEvent("beforeload", this, params) !== false){
26049             var  o = {
26050                 params : params || {},
26051                 request: {
26052                     callback : callback,
26053                     scope : scope,
26054                     arg : arg
26055                 },
26056                 reader: reader,
26057                 callback : this.loadResponse,
26058                 scope: this
26059             };
26060             if(this.useAjax){
26061                 Roo.applyIf(o, this.conn);
26062                 if(this.activeRequest){
26063                     Roo.Ajax.abort(this.activeRequest);
26064                 }
26065                 this.activeRequest = Roo.Ajax.request(o);
26066             }else{
26067                 this.conn.request(o);
26068             }
26069         }else{
26070             callback.call(scope||this, null, arg, false);
26071         }
26072     },
26073
26074     // private
26075     loadResponse : function(o, success, response){
26076         delete this.activeRequest;
26077         if(!success){
26078             this.fireEvent("loadexception", this, o, response);
26079             o.request.callback.call(o.request.scope, null, o.request.arg, false);
26080             return;
26081         }
26082         var result;
26083         try {
26084             result = o.reader.read(response);
26085         }catch(e){
26086             o.success = false;
26087             o.raw = { errorMsg : response.responseText };
26088             this.fireEvent("loadexception", this, o, response, e);
26089             o.request.callback.call(o.request.scope, o, o.request.arg, false);
26090             return;
26091         }
26092         
26093         this.fireEvent("load", this, o, o.request.arg);
26094         o.request.callback.call(o.request.scope, result, o.request.arg, true);
26095     },
26096
26097     // private
26098     update : function(dataSet){
26099
26100     },
26101
26102     // private
26103     updateResponse : function(dataSet){
26104
26105     }
26106 });/*
26107  * Based on:
26108  * Ext JS Library 1.1.1
26109  * Copyright(c) 2006-2007, Ext JS, LLC.
26110  *
26111  * Originally Released Under LGPL - original licence link has changed is not relivant.
26112  *
26113  * Fork - LGPL
26114  * <script type="text/javascript">
26115  */
26116
26117 /**
26118  * @class Roo.data.ScriptTagProxy
26119  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
26120  * other than the originating domain of the running page.<br><br>
26121  * <p>
26122  * <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
26123  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
26124  * <p>
26125  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
26126  * source code that is used as the source inside a &lt;script> tag.<br><br>
26127  * <p>
26128  * In order for the browser to process the returned data, the server must wrap the data object
26129  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
26130  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
26131  * depending on whether the callback name was passed:
26132  * <p>
26133  * <pre><code>
26134 boolean scriptTag = false;
26135 String cb = request.getParameter("callback");
26136 if (cb != null) {
26137     scriptTag = true;
26138     response.setContentType("text/javascript");
26139 } else {
26140     response.setContentType("application/x-json");
26141 }
26142 Writer out = response.getWriter();
26143 if (scriptTag) {
26144     out.write(cb + "(");
26145 }
26146 out.print(dataBlock.toJsonString());
26147 if (scriptTag) {
26148     out.write(");");
26149 }
26150 </pre></code>
26151  *
26152  * @constructor
26153  * @param {Object} config A configuration object.
26154  */
26155 Roo.data.ScriptTagProxy = function(config){
26156     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
26157     Roo.apply(this, config);
26158     this.head = document.getElementsByTagName("head")[0];
26159 };
26160
26161 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
26162
26163 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
26164     /**
26165      * @cfg {String} url The URL from which to request the data object.
26166      */
26167     /**
26168      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
26169      */
26170     timeout : 30000,
26171     /**
26172      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
26173      * the server the name of the callback function set up by the load call to process the returned data object.
26174      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
26175      * javascript output which calls this named function passing the data object as its only parameter.
26176      */
26177     callbackParam : "callback",
26178     /**
26179      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
26180      * name to the request.
26181      */
26182     nocache : true,
26183
26184     /**
26185      * Load data from the configured URL, read the data object into
26186      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
26187      * process that block using the passed callback.
26188      * @param {Object} params An object containing properties which are to be used as HTTP parameters
26189      * for the request to the remote server.
26190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
26191      * object into a block of Roo.data.Records.
26192      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
26193      * The function must be passed <ul>
26194      * <li>The Record block object</li>
26195      * <li>The "arg" argument from the load function</li>
26196      * <li>A boolean success indicator</li>
26197      * </ul>
26198      * @param {Object} scope The scope in which to call the callback
26199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
26200      */
26201     load : function(params, reader, callback, scope, arg){
26202         if(this.fireEvent("beforeload", this, params) !== false){
26203
26204             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26205
26206             var url = this.url;
26207             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26208             if(this.nocache){
26209                 url += "&_dc=" + (new Date().getTime());
26210             }
26211             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26212             var trans = {
26213                 id : transId,
26214                 cb : "stcCallback"+transId,
26215                 scriptId : "stcScript"+transId,
26216                 params : params,
26217                 arg : arg,
26218                 url : url,
26219                 callback : callback,
26220                 scope : scope,
26221                 reader : reader
26222             };
26223             var conn = this;
26224
26225             window[trans.cb] = function(o){
26226                 conn.handleResponse(o, trans);
26227             };
26228
26229             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26230
26231             if(this.autoAbort !== false){
26232                 this.abort();
26233             }
26234
26235             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26236
26237             var script = document.createElement("script");
26238             script.setAttribute("src", url);
26239             script.setAttribute("type", "text/javascript");
26240             script.setAttribute("id", trans.scriptId);
26241             this.head.appendChild(script);
26242
26243             this.trans = trans;
26244         }else{
26245             callback.call(scope||this, null, arg, false);
26246         }
26247     },
26248
26249     // private
26250     isLoading : function(){
26251         return this.trans ? true : false;
26252     },
26253
26254     /**
26255      * Abort the current server request.
26256      */
26257     abort : function(){
26258         if(this.isLoading()){
26259             this.destroyTrans(this.trans);
26260         }
26261     },
26262
26263     // private
26264     destroyTrans : function(trans, isLoaded){
26265         this.head.removeChild(document.getElementById(trans.scriptId));
26266         clearTimeout(trans.timeoutId);
26267         if(isLoaded){
26268             window[trans.cb] = undefined;
26269             try{
26270                 delete window[trans.cb];
26271             }catch(e){}
26272         }else{
26273             // if hasn't been loaded, wait for load to remove it to prevent script error
26274             window[trans.cb] = function(){
26275                 window[trans.cb] = undefined;
26276                 try{
26277                     delete window[trans.cb];
26278                 }catch(e){}
26279             };
26280         }
26281     },
26282
26283     // private
26284     handleResponse : function(o, trans){
26285         this.trans = false;
26286         this.destroyTrans(trans, true);
26287         var result;
26288         try {
26289             result = trans.reader.readRecords(o);
26290         }catch(e){
26291             this.fireEvent("loadexception", this, o, trans.arg, e);
26292             trans.callback.call(trans.scope||window, null, trans.arg, false);
26293             return;
26294         }
26295         this.fireEvent("load", this, o, trans.arg);
26296         trans.callback.call(trans.scope||window, result, trans.arg, true);
26297     },
26298
26299     // private
26300     handleFailure : function(trans){
26301         this.trans = false;
26302         this.destroyTrans(trans, false);
26303         this.fireEvent("loadexception", this, null, trans.arg);
26304         trans.callback.call(trans.scope||window, null, trans.arg, false);
26305     }
26306 });/*
26307  * Based on:
26308  * Ext JS Library 1.1.1
26309  * Copyright(c) 2006-2007, Ext JS, LLC.
26310  *
26311  * Originally Released Under LGPL - original licence link has changed is not relivant.
26312  *
26313  * Fork - LGPL
26314  * <script type="text/javascript">
26315  */
26316
26317 /**
26318  * @class Roo.data.JsonReader
26319  * @extends Roo.data.DataReader
26320  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26321  * based on mappings in a provided Roo.data.Record constructor.
26322  * 
26323  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26324  * in the reply previously. 
26325  * 
26326  * <p>
26327  * Example code:
26328  * <pre><code>
26329 var RecordDef = Roo.data.Record.create([
26330     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26331     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26332 ]);
26333 var myReader = new Roo.data.JsonReader({
26334     totalProperty: "results",    // The property which contains the total dataset size (optional)
26335     root: "rows",                // The property which contains an Array of row objects
26336     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26337 }, RecordDef);
26338 </code></pre>
26339  * <p>
26340  * This would consume a JSON file like this:
26341  * <pre><code>
26342 { 'results': 2, 'rows': [
26343     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26344     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26345 }
26346 </code></pre>
26347  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26348  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26349  * paged from the remote server.
26350  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26351  * @cfg {String} root name of the property which contains the Array of row objects.
26352  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26353  * @cfg {Array} fields Array of field definition objects
26354  * @constructor
26355  * Create a new JsonReader
26356  * @param {Object} meta Metadata configuration options
26357  * @param {Object} recordType Either an Array of field definition objects,
26358  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26359  */
26360 Roo.data.JsonReader = function(meta, recordType){
26361     
26362     meta = meta || {};
26363     // set some defaults:
26364     Roo.applyIf(meta, {
26365         totalProperty: 'total',
26366         successProperty : 'success',
26367         root : 'data',
26368         id : 'id'
26369     });
26370     
26371     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26372 };
26373 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26374     
26375     readerType : 'Json',
26376     
26377     /**
26378      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26379      * Used by Store query builder to append _requestMeta to params.
26380      * 
26381      */
26382     metaFromRemote : false,
26383     /**
26384      * This method is only used by a DataProxy which has retrieved data from a remote server.
26385      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26386      * @return {Object} data A data block which is used by an Roo.data.Store object as
26387      * a cache of Roo.data.Records.
26388      */
26389     read : function(response){
26390         var json = response.responseText;
26391        
26392         var o = /* eval:var:o */ eval("("+json+")");
26393         if(!o) {
26394             throw {message: "JsonReader.read: Json object not found"};
26395         }
26396         
26397         if(o.metaData){
26398             
26399             delete this.ef;
26400             this.metaFromRemote = true;
26401             this.meta = o.metaData;
26402             this.recordType = Roo.data.Record.create(o.metaData.fields);
26403             this.onMetaChange(this.meta, this.recordType, o);
26404         }
26405         return this.readRecords(o);
26406     },
26407
26408     // private function a store will implement
26409     onMetaChange : function(meta, recordType, o){
26410
26411     },
26412
26413     /**
26414          * @ignore
26415          */
26416     simpleAccess: function(obj, subsc) {
26417         return obj[subsc];
26418     },
26419
26420         /**
26421          * @ignore
26422          */
26423     getJsonAccessor: function(){
26424         var re = /[\[\.]/;
26425         return function(expr) {
26426             try {
26427                 return(re.test(expr))
26428                     ? new Function("obj", "return obj." + expr)
26429                     : function(obj){
26430                         return obj[expr];
26431                     };
26432             } catch(e){}
26433             return Roo.emptyFn;
26434         };
26435     }(),
26436
26437     /**
26438      * Create a data block containing Roo.data.Records from an XML document.
26439      * @param {Object} o An object which contains an Array of row objects in the property specified
26440      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26441      * which contains the total size of the dataset.
26442      * @return {Object} data A data block which is used by an Roo.data.Store object as
26443      * a cache of Roo.data.Records.
26444      */
26445     readRecords : function(o){
26446         /**
26447          * After any data loads, the raw JSON data is available for further custom processing.
26448          * @type Object
26449          */
26450         this.o = o;
26451         var s = this.meta, Record = this.recordType,
26452             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26453
26454 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26455         if (!this.ef) {
26456             if(s.totalProperty) {
26457                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26458                 }
26459                 if(s.successProperty) {
26460                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26461                 }
26462                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26463                 if (s.id) {
26464                         var g = this.getJsonAccessor(s.id);
26465                         this.getId = function(rec) {
26466                                 var r = g(rec);  
26467                                 return (r === undefined || r === "") ? null : r;
26468                         };
26469                 } else {
26470                         this.getId = function(){return null;};
26471                 }
26472             this.ef = [];
26473             for(var jj = 0; jj < fl; jj++){
26474                 f = fi[jj];
26475                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26476                 this.ef[jj] = this.getJsonAccessor(map);
26477             }
26478         }
26479
26480         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26481         if(s.totalProperty){
26482             var vt = parseInt(this.getTotal(o), 10);
26483             if(!isNaN(vt)){
26484                 totalRecords = vt;
26485             }
26486         }
26487         if(s.successProperty){
26488             var vs = this.getSuccess(o);
26489             if(vs === false || vs === 'false'){
26490                 success = false;
26491             }
26492         }
26493         var records = [];
26494         for(var i = 0; i < c; i++){
26495             var n = root[i];
26496             var values = {};
26497             var id = this.getId(n);
26498             for(var j = 0; j < fl; j++){
26499                 f = fi[j];
26500                                 var v = this.ef[j](n);
26501                                 if (!f.convert) {
26502                                         Roo.log('missing convert for ' + f.name);
26503                                         Roo.log(f);
26504                                         continue;
26505                                 }
26506                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26507             }
26508                         if (!Record) {
26509                                 return {
26510                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26511                                         success : false,
26512                                         records : [],
26513                                         totalRecords : 0
26514                                 };
26515                         }
26516             var record = new Record(values, id);
26517             record.json = n;
26518             records[i] = record;
26519         }
26520         return {
26521             raw : o,
26522             success : success,
26523             records : records,
26524             totalRecords : totalRecords
26525         };
26526     },
26527     // used when loading children.. @see loadDataFromChildren
26528     toLoadData: function(rec)
26529     {
26530         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26531         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26532         return { data : data, total : data.length };
26533         
26534     }
26535 });/*
26536  * Based on:
26537  * Ext JS Library 1.1.1
26538  * Copyright(c) 2006-2007, Ext JS, LLC.
26539  *
26540  * Originally Released Under LGPL - original licence link has changed is not relivant.
26541  *
26542  * Fork - LGPL
26543  * <script type="text/javascript">
26544  */
26545
26546 /**
26547  * @class Roo.data.XmlReader
26548  * @extends Roo.data.DataReader
26549  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26550  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26551  * <p>
26552  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26553  * header in the HTTP response must be set to "text/xml".</em>
26554  * <p>
26555  * Example code:
26556  * <pre><code>
26557 var RecordDef = Roo.data.Record.create([
26558    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26559    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26560 ]);
26561 var myReader = new Roo.data.XmlReader({
26562    totalRecords: "results", // The element which contains the total dataset size (optional)
26563    record: "row",           // The repeated element which contains row information
26564    id: "id"                 // The element within the row that provides an ID for the record (optional)
26565 }, RecordDef);
26566 </code></pre>
26567  * <p>
26568  * This would consume an XML file like this:
26569  * <pre><code>
26570 &lt;?xml?>
26571 &lt;dataset>
26572  &lt;results>2&lt;/results>
26573  &lt;row>
26574    &lt;id>1&lt;/id>
26575    &lt;name>Bill&lt;/name>
26576    &lt;occupation>Gardener&lt;/occupation>
26577  &lt;/row>
26578  &lt;row>
26579    &lt;id>2&lt;/id>
26580    &lt;name>Ben&lt;/name>
26581    &lt;occupation>Horticulturalist&lt;/occupation>
26582  &lt;/row>
26583 &lt;/dataset>
26584 </code></pre>
26585  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26586  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26587  * paged from the remote server.
26588  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26589  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26590  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26591  * a record identifier value.
26592  * @constructor
26593  * Create a new XmlReader
26594  * @param {Object} meta Metadata configuration options
26595  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26596  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26597  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26598  */
26599 Roo.data.XmlReader = function(meta, recordType){
26600     meta = meta || {};
26601     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26602 };
26603 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26604     
26605     readerType : 'Xml',
26606     
26607     /**
26608      * This method is only used by a DataProxy which has retrieved data from a remote server.
26609          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26610          * to contain a method called 'responseXML' that returns an XML document object.
26611      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26612      * a cache of Roo.data.Records.
26613      */
26614     read : function(response){
26615         var doc = response.responseXML;
26616         if(!doc) {
26617             throw {message: "XmlReader.read: XML Document not available"};
26618         }
26619         return this.readRecords(doc);
26620     },
26621
26622     /**
26623      * Create a data block containing Roo.data.Records from an XML document.
26624          * @param {Object} doc A parsed XML document.
26625      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26626      * a cache of Roo.data.Records.
26627      */
26628     readRecords : function(doc){
26629         /**
26630          * After any data loads/reads, the raw XML Document is available for further custom processing.
26631          * @type XMLDocument
26632          */
26633         this.xmlData = doc;
26634         var root = doc.documentElement || doc;
26635         var q = Roo.DomQuery;
26636         var recordType = this.recordType, fields = recordType.prototype.fields;
26637         var sid = this.meta.id;
26638         var totalRecords = 0, success = true;
26639         if(this.meta.totalRecords){
26640             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26641         }
26642         
26643         if(this.meta.success){
26644             var sv = q.selectValue(this.meta.success, root, true);
26645             success = sv !== false && sv !== 'false';
26646         }
26647         var records = [];
26648         var ns = q.select(this.meta.record, root);
26649         for(var i = 0, len = ns.length; i < len; i++) {
26650                 var n = ns[i];
26651                 var values = {};
26652                 var id = sid ? q.selectValue(sid, n) : undefined;
26653                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26654                     var f = fields.items[j];
26655                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26656                     v = f.convert(v);
26657                     values[f.name] = v;
26658                 }
26659                 var record = new recordType(values, id);
26660                 record.node = n;
26661                 records[records.length] = record;
26662             }
26663
26664             return {
26665                 success : success,
26666                 records : records,
26667                 totalRecords : totalRecords || records.length
26668             };
26669     }
26670 });/*
26671  * Based on:
26672  * Ext JS Library 1.1.1
26673  * Copyright(c) 2006-2007, Ext JS, LLC.
26674  *
26675  * Originally Released Under LGPL - original licence link has changed is not relivant.
26676  *
26677  * Fork - LGPL
26678  * <script type="text/javascript">
26679  */
26680
26681 /**
26682  * @class Roo.data.ArrayReader
26683  * @extends Roo.data.DataReader
26684  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26685  * Each element of that Array represents a row of data fields. The
26686  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26687  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26688  * <p>
26689  * Example code:.
26690  * <pre><code>
26691 var RecordDef = Roo.data.Record.create([
26692     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26693     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26694 ]);
26695 var myReader = new Roo.data.ArrayReader({
26696     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26697 }, RecordDef);
26698 </code></pre>
26699  * <p>
26700  * This would consume an Array like this:
26701  * <pre><code>
26702 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26703   </code></pre>
26704  
26705  * @constructor
26706  * Create a new JsonReader
26707  * @param {Object} meta Metadata configuration options.
26708  * @param {Object|Array} recordType Either an Array of field definition objects
26709  * 
26710  * @cfg {Array} fields Array of field definition objects
26711  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26712  * as specified to {@link Roo.data.Record#create},
26713  * or an {@link Roo.data.Record} object
26714  *
26715  * 
26716  * created using {@link Roo.data.Record#create}.
26717  */
26718 Roo.data.ArrayReader = function(meta, recordType)
26719 {    
26720     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26721 };
26722
26723 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26724     
26725       /**
26726      * Create a data block containing Roo.data.Records from an XML document.
26727      * @param {Object} o An Array of row objects which represents the dataset.
26728      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26729      * a cache of Roo.data.Records.
26730      */
26731     readRecords : function(o)
26732     {
26733         var sid = this.meta ? this.meta.id : null;
26734         var recordType = this.recordType, fields = recordType.prototype.fields;
26735         var records = [];
26736         var root = o;
26737         for(var i = 0; i < root.length; i++){
26738             var n = root[i];
26739             var values = {};
26740             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26741             for(var j = 0, jlen = fields.length; j < jlen; j++){
26742                 var f = fields.items[j];
26743                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26744                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26745                 v = f.convert(v);
26746                 values[f.name] = v;
26747             }
26748             var record = new recordType(values, id);
26749             record.json = n;
26750             records[records.length] = record;
26751         }
26752         return {
26753             records : records,
26754             totalRecords : records.length
26755         };
26756     },
26757     // used when loading children.. @see loadDataFromChildren
26758     toLoadData: function(rec)
26759     {
26760         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26761         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26762         
26763     }
26764     
26765     
26766 });/*
26767  * Based on:
26768  * Ext JS Library 1.1.1
26769  * Copyright(c) 2006-2007, Ext JS, LLC.
26770  *
26771  * Originally Released Under LGPL - original licence link has changed is not relivant.
26772  *
26773  * Fork - LGPL
26774  * <script type="text/javascript">
26775  */
26776
26777
26778 /**
26779  * @class Roo.data.Tree
26780  * @extends Roo.util.Observable
26781  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26782  * in the tree have most standard DOM functionality.
26783  * @constructor
26784  * @param {Node} root (optional) The root node
26785  */
26786 Roo.data.Tree = function(root){
26787    this.nodeHash = {};
26788    /**
26789     * The root node for this tree
26790     * @type Node
26791     */
26792    this.root = null;
26793    if(root){
26794        this.setRootNode(root);
26795    }
26796    this.addEvents({
26797        /**
26798         * @event append
26799         * Fires when a new child node is appended to a node in this tree.
26800         * @param {Tree} tree The owner tree
26801         * @param {Node} parent The parent node
26802         * @param {Node} node The newly appended node
26803         * @param {Number} index The index of the newly appended node
26804         */
26805        "append" : true,
26806        /**
26807         * @event remove
26808         * Fires when a child node is removed from a node in this tree.
26809         * @param {Tree} tree The owner tree
26810         * @param {Node} parent The parent node
26811         * @param {Node} node The child node removed
26812         */
26813        "remove" : true,
26814        /**
26815         * @event move
26816         * Fires when a node is moved to a new location in the tree
26817         * @param {Tree} tree The owner tree
26818         * @param {Node} node The node moved
26819         * @param {Node} oldParent The old parent of this node
26820         * @param {Node} newParent The new parent of this node
26821         * @param {Number} index The index it was moved to
26822         */
26823        "move" : true,
26824        /**
26825         * @event insert
26826         * Fires when a new child node is inserted in a node in this tree.
26827         * @param {Tree} tree The owner tree
26828         * @param {Node} parent The parent node
26829         * @param {Node} node The child node inserted
26830         * @param {Node} refNode The child node the node was inserted before
26831         */
26832        "insert" : true,
26833        /**
26834         * @event beforeappend
26835         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26836         * @param {Tree} tree The owner tree
26837         * @param {Node} parent The parent node
26838         * @param {Node} node The child node to be appended
26839         */
26840        "beforeappend" : true,
26841        /**
26842         * @event beforeremove
26843         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26844         * @param {Tree} tree The owner tree
26845         * @param {Node} parent The parent node
26846         * @param {Node} node The child node to be removed
26847         */
26848        "beforeremove" : true,
26849        /**
26850         * @event beforemove
26851         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26852         * @param {Tree} tree The owner tree
26853         * @param {Node} node The node being moved
26854         * @param {Node} oldParent The parent of the node
26855         * @param {Node} newParent The new parent the node is moving to
26856         * @param {Number} index The index it is being moved to
26857         */
26858        "beforemove" : true,
26859        /**
26860         * @event beforeinsert
26861         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26862         * @param {Tree} tree The owner tree
26863         * @param {Node} parent The parent node
26864         * @param {Node} node The child node to be inserted
26865         * @param {Node} refNode The child node the node is being inserted before
26866         */
26867        "beforeinsert" : true
26868    });
26869
26870     Roo.data.Tree.superclass.constructor.call(this);
26871 };
26872
26873 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26874     pathSeparator: "/",
26875
26876     proxyNodeEvent : function(){
26877         return this.fireEvent.apply(this, arguments);
26878     },
26879
26880     /**
26881      * Returns the root node for this tree.
26882      * @return {Node}
26883      */
26884     getRootNode : function(){
26885         return this.root;
26886     },
26887
26888     /**
26889      * Sets the root node for this tree.
26890      * @param {Node} node
26891      * @return {Node}
26892      */
26893     setRootNode : function(node){
26894         this.root = node;
26895         node.ownerTree = this;
26896         node.isRoot = true;
26897         this.registerNode(node);
26898         return node;
26899     },
26900
26901     /**
26902      * Gets a node in this tree by its id.
26903      * @param {String} id
26904      * @return {Node}
26905      */
26906     getNodeById : function(id){
26907         return this.nodeHash[id];
26908     },
26909
26910     registerNode : function(node){
26911         this.nodeHash[node.id] = node;
26912     },
26913
26914     unregisterNode : function(node){
26915         delete this.nodeHash[node.id];
26916     },
26917
26918     toString : function(){
26919         return "[Tree"+(this.id?" "+this.id:"")+"]";
26920     }
26921 });
26922
26923 /**
26924  * @class Roo.data.Node
26925  * @extends Roo.util.Observable
26926  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26927  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26928  * @constructor
26929  * @param {Object} attributes The attributes/config for the node
26930  */
26931 Roo.data.Node = function(attributes){
26932     /**
26933      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26934      * @type {Object}
26935      */
26936     this.attributes = attributes || {};
26937     this.leaf = this.attributes.leaf;
26938     /**
26939      * The node id. @type String
26940      */
26941     this.id = this.attributes.id;
26942     if(!this.id){
26943         this.id = Roo.id(null, "ynode-");
26944         this.attributes.id = this.id;
26945     }
26946      
26947     
26948     /**
26949      * All child nodes of this node. @type Array
26950      */
26951     this.childNodes = [];
26952     if(!this.childNodes.indexOf){ // indexOf is a must
26953         this.childNodes.indexOf = function(o){
26954             for(var i = 0, len = this.length; i < len; i++){
26955                 if(this[i] == o) {
26956                     return i;
26957                 }
26958             }
26959             return -1;
26960         };
26961     }
26962     /**
26963      * The parent node for this node. @type Node
26964      */
26965     this.parentNode = null;
26966     /**
26967      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26968      */
26969     this.firstChild = null;
26970     /**
26971      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26972      */
26973     this.lastChild = null;
26974     /**
26975      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26976      */
26977     this.previousSibling = null;
26978     /**
26979      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26980      */
26981     this.nextSibling = null;
26982
26983     this.addEvents({
26984        /**
26985         * @event append
26986         * Fires when a new child node is appended
26987         * @param {Tree} tree The owner tree
26988         * @param {Node} this This node
26989         * @param {Node} node The newly appended node
26990         * @param {Number} index The index of the newly appended node
26991         */
26992        "append" : true,
26993        /**
26994         * @event remove
26995         * Fires when a child node is removed
26996         * @param {Tree} tree The owner tree
26997         * @param {Node} this This node
26998         * @param {Node} node The removed node
26999         */
27000        "remove" : true,
27001        /**
27002         * @event move
27003         * Fires when this node is moved to a new location in the tree
27004         * @param {Tree} tree The owner tree
27005         * @param {Node} this This node
27006         * @param {Node} oldParent The old parent of this node
27007         * @param {Node} newParent The new parent of this node
27008         * @param {Number} index The index it was moved to
27009         */
27010        "move" : true,
27011        /**
27012         * @event insert
27013         * Fires when a new child node is inserted.
27014         * @param {Tree} tree The owner tree
27015         * @param {Node} this This node
27016         * @param {Node} node The child node inserted
27017         * @param {Node} refNode The child node the node was inserted before
27018         */
27019        "insert" : true,
27020        /**
27021         * @event beforeappend
27022         * Fires before a new child is appended, return false to cancel the append.
27023         * @param {Tree} tree The owner tree
27024         * @param {Node} this This node
27025         * @param {Node} node The child node to be appended
27026         */
27027        "beforeappend" : true,
27028        /**
27029         * @event beforeremove
27030         * Fires before a child is removed, return false to cancel the remove.
27031         * @param {Tree} tree The owner tree
27032         * @param {Node} this This node
27033         * @param {Node} node The child node to be removed
27034         */
27035        "beforeremove" : true,
27036        /**
27037         * @event beforemove
27038         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
27039         * @param {Tree} tree The owner tree
27040         * @param {Node} this This node
27041         * @param {Node} oldParent The parent of this node
27042         * @param {Node} newParent The new parent this node is moving to
27043         * @param {Number} index The index it is being moved to
27044         */
27045        "beforemove" : true,
27046        /**
27047         * @event beforeinsert
27048         * Fires before a new child is inserted, return false to cancel the insert.
27049         * @param {Tree} tree The owner tree
27050         * @param {Node} this This node
27051         * @param {Node} node The child node to be inserted
27052         * @param {Node} refNode The child node the node is being inserted before
27053         */
27054        "beforeinsert" : true
27055    });
27056     this.listeners = this.attributes.listeners;
27057     Roo.data.Node.superclass.constructor.call(this);
27058 };
27059
27060 Roo.extend(Roo.data.Node, Roo.util.Observable, {
27061     fireEvent : function(evtName){
27062         // first do standard event for this node
27063         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
27064             return false;
27065         }
27066         // then bubble it up to the tree if the event wasn't cancelled
27067         var ot = this.getOwnerTree();
27068         if(ot){
27069             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
27070                 return false;
27071             }
27072         }
27073         return true;
27074     },
27075
27076     /**
27077      * Returns true if this node is a leaf
27078      * @return {Boolean}
27079      */
27080     isLeaf : function(){
27081         return this.leaf === true;
27082     },
27083
27084     // private
27085     setFirstChild : function(node){
27086         this.firstChild = node;
27087     },
27088
27089     //private
27090     setLastChild : function(node){
27091         this.lastChild = node;
27092     },
27093
27094
27095     /**
27096      * Returns true if this node is the last child of its parent
27097      * @return {Boolean}
27098      */
27099     isLast : function(){
27100        return (!this.parentNode ? true : this.parentNode.lastChild == this);
27101     },
27102
27103     /**
27104      * Returns true if this node is the first child of its parent
27105      * @return {Boolean}
27106      */
27107     isFirst : function(){
27108        return (!this.parentNode ? true : this.parentNode.firstChild == this);
27109     },
27110
27111     hasChildNodes : function(){
27112         return !this.isLeaf() && this.childNodes.length > 0;
27113     },
27114
27115     /**
27116      * Insert node(s) as the last child node of this node.
27117      * @param {Node/Array} node The node or Array of nodes to append
27118      * @return {Node} The appended node if single append, or null if an array was passed
27119      */
27120     appendChild : function(node){
27121         var multi = false;
27122         if(node instanceof Array){
27123             multi = node;
27124         }else if(arguments.length > 1){
27125             multi = arguments;
27126         }
27127         
27128         // if passed an array or multiple args do them one by one
27129         if(multi){
27130             for(var i = 0, len = multi.length; i < len; i++) {
27131                 this.appendChild(multi[i]);
27132             }
27133         }else{
27134             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
27135                 return false;
27136             }
27137             var index = this.childNodes.length;
27138             var oldParent = node.parentNode;
27139             // it's a move, make sure we move it cleanly
27140             if(oldParent){
27141                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
27142                     return false;
27143                 }
27144                 oldParent.removeChild(node);
27145             }
27146             
27147             index = this.childNodes.length;
27148             if(index == 0){
27149                 this.setFirstChild(node);
27150             }
27151             this.childNodes.push(node);
27152             node.parentNode = this;
27153             var ps = this.childNodes[index-1];
27154             if(ps){
27155                 node.previousSibling = ps;
27156                 ps.nextSibling = node;
27157             }else{
27158                 node.previousSibling = null;
27159             }
27160             node.nextSibling = null;
27161             this.setLastChild(node);
27162             node.setOwnerTree(this.getOwnerTree());
27163             this.fireEvent("append", this.ownerTree, this, node, index);
27164             if(this.ownerTree) {
27165                 this.ownerTree.fireEvent("appendnode", this, node, index);
27166             }
27167             if(oldParent){
27168                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
27169             }
27170             return node;
27171         }
27172     },
27173
27174     /**
27175      * Removes a child node from this node.
27176      * @param {Node} node The node to remove
27177      * @return {Node} The removed node
27178      */
27179     removeChild : function(node){
27180         var index = this.childNodes.indexOf(node);
27181         if(index == -1){
27182             return false;
27183         }
27184         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
27185             return false;
27186         }
27187
27188         // remove it from childNodes collection
27189         this.childNodes.splice(index, 1);
27190
27191         // update siblings
27192         if(node.previousSibling){
27193             node.previousSibling.nextSibling = node.nextSibling;
27194         }
27195         if(node.nextSibling){
27196             node.nextSibling.previousSibling = node.previousSibling;
27197         }
27198
27199         // update child refs
27200         if(this.firstChild == node){
27201             this.setFirstChild(node.nextSibling);
27202         }
27203         if(this.lastChild == node){
27204             this.setLastChild(node.previousSibling);
27205         }
27206
27207         node.setOwnerTree(null);
27208         // clear any references from the node
27209         node.parentNode = null;
27210         node.previousSibling = null;
27211         node.nextSibling = null;
27212         this.fireEvent("remove", this.ownerTree, this, node);
27213         return node;
27214     },
27215
27216     /**
27217      * Inserts the first node before the second node in this nodes childNodes collection.
27218      * @param {Node} node The node to insert
27219      * @param {Node} refNode The node to insert before (if null the node is appended)
27220      * @return {Node} The inserted node
27221      */
27222     insertBefore : function(node, refNode){
27223         if(!refNode){ // like standard Dom, refNode can be null for append
27224             return this.appendChild(node);
27225         }
27226         // nothing to do
27227         if(node == refNode){
27228             return false;
27229         }
27230
27231         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27232             return false;
27233         }
27234         var index = this.childNodes.indexOf(refNode);
27235         var oldParent = node.parentNode;
27236         var refIndex = index;
27237
27238         // when moving internally, indexes will change after remove
27239         if(oldParent == this && this.childNodes.indexOf(node) < index){
27240             refIndex--;
27241         }
27242
27243         // it's a move, make sure we move it cleanly
27244         if(oldParent){
27245             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27246                 return false;
27247             }
27248             oldParent.removeChild(node);
27249         }
27250         if(refIndex == 0){
27251             this.setFirstChild(node);
27252         }
27253         this.childNodes.splice(refIndex, 0, node);
27254         node.parentNode = this;
27255         var ps = this.childNodes[refIndex-1];
27256         if(ps){
27257             node.previousSibling = ps;
27258             ps.nextSibling = node;
27259         }else{
27260             node.previousSibling = null;
27261         }
27262         node.nextSibling = refNode;
27263         refNode.previousSibling = node;
27264         node.setOwnerTree(this.getOwnerTree());
27265         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27266         if(oldParent){
27267             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27268         }
27269         return node;
27270     },
27271
27272     /**
27273      * Returns the child node at the specified index.
27274      * @param {Number} index
27275      * @return {Node}
27276      */
27277     item : function(index){
27278         return this.childNodes[index];
27279     },
27280
27281     /**
27282      * Replaces one child node in this node with another.
27283      * @param {Node} newChild The replacement node
27284      * @param {Node} oldChild The node to replace
27285      * @return {Node} The replaced node
27286      */
27287     replaceChild : function(newChild, oldChild){
27288         this.insertBefore(newChild, oldChild);
27289         this.removeChild(oldChild);
27290         return oldChild;
27291     },
27292
27293     /**
27294      * Returns the index of a child node
27295      * @param {Node} node
27296      * @return {Number} The index of the node or -1 if it was not found
27297      */
27298     indexOf : function(child){
27299         return this.childNodes.indexOf(child);
27300     },
27301
27302     /**
27303      * Returns the tree this node is in.
27304      * @return {Tree}
27305      */
27306     getOwnerTree : function(){
27307         // if it doesn't have one, look for one
27308         if(!this.ownerTree){
27309             var p = this;
27310             while(p){
27311                 if(p.ownerTree){
27312                     this.ownerTree = p.ownerTree;
27313                     break;
27314                 }
27315                 p = p.parentNode;
27316             }
27317         }
27318         return this.ownerTree;
27319     },
27320
27321     /**
27322      * Returns depth of this node (the root node has a depth of 0)
27323      * @return {Number}
27324      */
27325     getDepth : function(){
27326         var depth = 0;
27327         var p = this;
27328         while(p.parentNode){
27329             ++depth;
27330             p = p.parentNode;
27331         }
27332         return depth;
27333     },
27334
27335     // private
27336     setOwnerTree : function(tree){
27337         // if it's move, we need to update everyone
27338         if(tree != this.ownerTree){
27339             if(this.ownerTree){
27340                 this.ownerTree.unregisterNode(this);
27341             }
27342             this.ownerTree = tree;
27343             var cs = this.childNodes;
27344             for(var i = 0, len = cs.length; i < len; i++) {
27345                 cs[i].setOwnerTree(tree);
27346             }
27347             if(tree){
27348                 tree.registerNode(this);
27349             }
27350         }
27351     },
27352
27353     /**
27354      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27355      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27356      * @return {String} The path
27357      */
27358     getPath : function(attr){
27359         attr = attr || "id";
27360         var p = this.parentNode;
27361         var b = [this.attributes[attr]];
27362         while(p){
27363             b.unshift(p.attributes[attr]);
27364             p = p.parentNode;
27365         }
27366         var sep = this.getOwnerTree().pathSeparator;
27367         return sep + b.join(sep);
27368     },
27369
27370     /**
27371      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27372      * function call will be the scope provided or the current node. The arguments to the function
27373      * will be the args provided or the current node. If the function returns false at any point,
27374      * the bubble is stopped.
27375      * @param {Function} fn The function to call
27376      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27377      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27378      */
27379     bubble : function(fn, scope, args){
27380         var p = this;
27381         while(p){
27382             if(fn.call(scope || p, args || p) === false){
27383                 break;
27384             }
27385             p = p.parentNode;
27386         }
27387     },
27388
27389     /**
27390      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27391      * function call will be the scope provided or the current node. The arguments to the function
27392      * will be the args provided or the current node. If the function returns false at any point,
27393      * the cascade is stopped on that branch.
27394      * @param {Function} fn The function to call
27395      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27396      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27397      */
27398     cascade : function(fn, scope, args){
27399         if(fn.call(scope || this, args || this) !== false){
27400             var cs = this.childNodes;
27401             for(var i = 0, len = cs.length; i < len; i++) {
27402                 cs[i].cascade(fn, scope, args);
27403             }
27404         }
27405     },
27406
27407     /**
27408      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27409      * function call will be the scope provided or the current node. The arguments to the function
27410      * will be the args provided or the current node. If the function returns false at any point,
27411      * the iteration stops.
27412      * @param {Function} fn The function to call
27413      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27414      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27415      */
27416     eachChild : function(fn, scope, args){
27417         var cs = this.childNodes;
27418         for(var i = 0, len = cs.length; i < len; i++) {
27419                 if(fn.call(scope || this, args || cs[i]) === false){
27420                     break;
27421                 }
27422         }
27423     },
27424
27425     /**
27426      * Finds the first child that has the attribute with the specified value.
27427      * @param {String} attribute The attribute name
27428      * @param {Mixed} value The value to search for
27429      * @return {Node} The found child or null if none was found
27430      */
27431     findChild : function(attribute, value){
27432         var cs = this.childNodes;
27433         for(var i = 0, len = cs.length; i < len; i++) {
27434                 if(cs[i].attributes[attribute] == value){
27435                     return cs[i];
27436                 }
27437         }
27438         return null;
27439     },
27440
27441     /**
27442      * Finds the first child by a custom function. The child matches if the function passed
27443      * returns true.
27444      * @param {Function} fn
27445      * @param {Object} scope (optional)
27446      * @return {Node} The found child or null if none was found
27447      */
27448     findChildBy : function(fn, scope){
27449         var cs = this.childNodes;
27450         for(var i = 0, len = cs.length; i < len; i++) {
27451                 if(fn.call(scope||cs[i], cs[i]) === true){
27452                     return cs[i];
27453                 }
27454         }
27455         return null;
27456     },
27457
27458     /**
27459      * Sorts this nodes children using the supplied sort function
27460      * @param {Function} fn
27461      * @param {Object} scope (optional)
27462      */
27463     sort : function(fn, scope){
27464         var cs = this.childNodes;
27465         var len = cs.length;
27466         if(len > 0){
27467             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27468             cs.sort(sortFn);
27469             for(var i = 0; i < len; i++){
27470                 var n = cs[i];
27471                 n.previousSibling = cs[i-1];
27472                 n.nextSibling = cs[i+1];
27473                 if(i == 0){
27474                     this.setFirstChild(n);
27475                 }
27476                 if(i == len-1){
27477                     this.setLastChild(n);
27478                 }
27479             }
27480         }
27481     },
27482
27483     /**
27484      * Returns true if this node is an ancestor (at any point) of the passed node.
27485      * @param {Node} node
27486      * @return {Boolean}
27487      */
27488     contains : function(node){
27489         return node.isAncestor(this);
27490     },
27491
27492     /**
27493      * Returns true if the passed node is an ancestor (at any point) of this node.
27494      * @param {Node} node
27495      * @return {Boolean}
27496      */
27497     isAncestor : function(node){
27498         var p = this.parentNode;
27499         while(p){
27500             if(p == node){
27501                 return true;
27502             }
27503             p = p.parentNode;
27504         }
27505         return false;
27506     },
27507
27508     toString : function(){
27509         return "[Node"+(this.id?" "+this.id:"")+"]";
27510     }
27511 });/*
27512  * Based on:
27513  * Ext JS Library 1.1.1
27514  * Copyright(c) 2006-2007, Ext JS, LLC.
27515  *
27516  * Originally Released Under LGPL - original licence link has changed is not relivant.
27517  *
27518  * Fork - LGPL
27519  * <script type="text/javascript">
27520  */
27521
27522
27523 /**
27524  * @class Roo.Shadow
27525  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27526  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27527  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27528  * @constructor
27529  * Create a new Shadow
27530  * @param {Object} config The config object
27531  */
27532 Roo.Shadow = function(config){
27533     Roo.apply(this, config);
27534     if(typeof this.mode != "string"){
27535         this.mode = this.defaultMode;
27536     }
27537     var o = this.offset, a = {h: 0};
27538     var rad = Math.floor(this.offset/2);
27539     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27540         case "drop":
27541             a.w = 0;
27542             a.l = a.t = o;
27543             a.t -= 1;
27544             if(Roo.isIE){
27545                 a.l -= this.offset + rad;
27546                 a.t -= this.offset + rad;
27547                 a.w -= rad;
27548                 a.h -= rad;
27549                 a.t += 1;
27550             }
27551         break;
27552         case "sides":
27553             a.w = (o*2);
27554             a.l = -o;
27555             a.t = o-1;
27556             if(Roo.isIE){
27557                 a.l -= (this.offset - rad);
27558                 a.t -= this.offset + rad;
27559                 a.l += 1;
27560                 a.w -= (this.offset - rad)*2;
27561                 a.w -= rad + 1;
27562                 a.h -= 1;
27563             }
27564         break;
27565         case "frame":
27566             a.w = a.h = (o*2);
27567             a.l = a.t = -o;
27568             a.t += 1;
27569             a.h -= 2;
27570             if(Roo.isIE){
27571                 a.l -= (this.offset - rad);
27572                 a.t -= (this.offset - rad);
27573                 a.l += 1;
27574                 a.w -= (this.offset + rad + 1);
27575                 a.h -= (this.offset + rad);
27576                 a.h += 1;
27577             }
27578         break;
27579     };
27580
27581     this.adjusts = a;
27582 };
27583
27584 Roo.Shadow.prototype = {
27585     /**
27586      * @cfg {String} mode
27587      * The shadow display mode.  Supports the following options:<br />
27588      * sides: Shadow displays on both sides and bottom only<br />
27589      * frame: Shadow displays equally on all four sides<br />
27590      * drop: Traditional bottom-right drop shadow (default)
27591      */
27592     mode: false,
27593     /**
27594      * @cfg {String} offset
27595      * The number of pixels to offset the shadow from the element (defaults to 4)
27596      */
27597     offset: 4,
27598
27599     // private
27600     defaultMode: "drop",
27601
27602     /**
27603      * Displays the shadow under the target element
27604      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27605      */
27606     show : function(target){
27607         target = Roo.get(target);
27608         if(!this.el){
27609             this.el = Roo.Shadow.Pool.pull();
27610             if(this.el.dom.nextSibling != target.dom){
27611                 this.el.insertBefore(target);
27612             }
27613         }
27614         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27615         if(Roo.isIE){
27616             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27617         }
27618         this.realign(
27619             target.getLeft(true),
27620             target.getTop(true),
27621             target.getWidth(),
27622             target.getHeight()
27623         );
27624         this.el.dom.style.display = "block";
27625     },
27626
27627     /**
27628      * Returns true if the shadow is visible, else false
27629      */
27630     isVisible : function(){
27631         return this.el ? true : false;  
27632     },
27633
27634     /**
27635      * Direct alignment when values are already available. Show must be called at least once before
27636      * calling this method to ensure it is initialized.
27637      * @param {Number} left The target element left position
27638      * @param {Number} top The target element top position
27639      * @param {Number} width The target element width
27640      * @param {Number} height The target element height
27641      */
27642     realign : function(l, t, w, h){
27643         if(!this.el){
27644             return;
27645         }
27646         var a = this.adjusts, d = this.el.dom, s = d.style;
27647         var iea = 0;
27648         s.left = (l+a.l)+"px";
27649         s.top = (t+a.t)+"px";
27650         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27651  
27652         if(s.width != sws || s.height != shs){
27653             s.width = sws;
27654             s.height = shs;
27655             if(!Roo.isIE){
27656                 var cn = d.childNodes;
27657                 var sww = Math.max(0, (sw-12))+"px";
27658                 cn[0].childNodes[1].style.width = sww;
27659                 cn[1].childNodes[1].style.width = sww;
27660                 cn[2].childNodes[1].style.width = sww;
27661                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27662             }
27663         }
27664     },
27665
27666     /**
27667      * Hides this shadow
27668      */
27669     hide : function(){
27670         if(this.el){
27671             this.el.dom.style.display = "none";
27672             Roo.Shadow.Pool.push(this.el);
27673             delete this.el;
27674         }
27675     },
27676
27677     /**
27678      * Adjust the z-index of this shadow
27679      * @param {Number} zindex The new z-index
27680      */
27681     setZIndex : function(z){
27682         this.zIndex = z;
27683         if(this.el){
27684             this.el.setStyle("z-index", z);
27685         }
27686     }
27687 };
27688
27689 // Private utility class that manages the internal Shadow cache
27690 Roo.Shadow.Pool = function(){
27691     var p = [];
27692     var markup = Roo.isIE ?
27693                  '<div class="x-ie-shadow"></div>' :
27694                  '<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>';
27695     return {
27696         pull : function(){
27697             var sh = p.shift();
27698             if(!sh){
27699                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27700                 sh.autoBoxAdjust = false;
27701             }
27702             return sh;
27703         },
27704
27705         push : function(sh){
27706             p.push(sh);
27707         }
27708     };
27709 }();/*
27710  * Based on:
27711  * Ext JS Library 1.1.1
27712  * Copyright(c) 2006-2007, Ext JS, LLC.
27713  *
27714  * Originally Released Under LGPL - original licence link has changed is not relivant.
27715  *
27716  * Fork - LGPL
27717  * <script type="text/javascript">
27718  */
27719
27720
27721 /**
27722  * @class Roo.SplitBar
27723  * @extends Roo.util.Observable
27724  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27725  * <br><br>
27726  * Usage:
27727  * <pre><code>
27728 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27729                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27730 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27731 split.minSize = 100;
27732 split.maxSize = 600;
27733 split.animate = true;
27734 split.on('moved', splitterMoved);
27735 </code></pre>
27736  * @constructor
27737  * Create a new SplitBar
27738  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27739  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27740  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27741  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27742                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27743                         position of the SplitBar).
27744  */
27745 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27746     
27747     /** @private */
27748     this.el = Roo.get(dragElement, true);
27749     this.el.dom.unselectable = "on";
27750     /** @private */
27751     this.resizingEl = Roo.get(resizingElement, true);
27752
27753     /**
27754      * @private
27755      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27756      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27757      * @type Number
27758      */
27759     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27760     
27761     /**
27762      * The minimum size of the resizing element. (Defaults to 0)
27763      * @type Number
27764      */
27765     this.minSize = 0;
27766     
27767     /**
27768      * The maximum size of the resizing element. (Defaults to 2000)
27769      * @type Number
27770      */
27771     this.maxSize = 2000;
27772     
27773     /**
27774      * Whether to animate the transition to the new size
27775      * @type Boolean
27776      */
27777     this.animate = false;
27778     
27779     /**
27780      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27781      * @type Boolean
27782      */
27783     this.useShim = false;
27784     
27785     /** @private */
27786     this.shim = null;
27787     
27788     if(!existingProxy){
27789         /** @private */
27790         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27791     }else{
27792         this.proxy = Roo.get(existingProxy).dom;
27793     }
27794     /** @private */
27795     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27796     
27797     /** @private */
27798     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27799     
27800     /** @private */
27801     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27802     
27803     /** @private */
27804     this.dragSpecs = {};
27805     
27806     /**
27807      * @private The adapter to use to positon and resize elements
27808      */
27809     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27810     this.adapter.init(this);
27811     
27812     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27813         /** @private */
27814         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27815         this.el.addClass("x-splitbar-h");
27816     }else{
27817         /** @private */
27818         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27819         this.el.addClass("x-splitbar-v");
27820     }
27821     
27822     this.addEvents({
27823         /**
27824          * @event resize
27825          * Fires when the splitter is moved (alias for {@link #event-moved})
27826          * @param {Roo.SplitBar} this
27827          * @param {Number} newSize the new width or height
27828          */
27829         "resize" : true,
27830         /**
27831          * @event moved
27832          * Fires when the splitter is moved
27833          * @param {Roo.SplitBar} this
27834          * @param {Number} newSize the new width or height
27835          */
27836         "moved" : true,
27837         /**
27838          * @event beforeresize
27839          * Fires before the splitter is dragged
27840          * @param {Roo.SplitBar} this
27841          */
27842         "beforeresize" : true,
27843
27844         "beforeapply" : true
27845     });
27846
27847     Roo.util.Observable.call(this);
27848 };
27849
27850 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27851     onStartProxyDrag : function(x, y){
27852         this.fireEvent("beforeresize", this);
27853         if(!this.overlay){
27854             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27855             o.unselectable();
27856             o.enableDisplayMode("block");
27857             // all splitbars share the same overlay
27858             Roo.SplitBar.prototype.overlay = o;
27859         }
27860         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27861         this.overlay.show();
27862         Roo.get(this.proxy).setDisplayed("block");
27863         var size = this.adapter.getElementSize(this);
27864         this.activeMinSize = this.getMinimumSize();;
27865         this.activeMaxSize = this.getMaximumSize();;
27866         var c1 = size - this.activeMinSize;
27867         var c2 = Math.max(this.activeMaxSize - size, 0);
27868         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27869             this.dd.resetConstraints();
27870             this.dd.setXConstraint(
27871                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27872                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27873             );
27874             this.dd.setYConstraint(0, 0);
27875         }else{
27876             this.dd.resetConstraints();
27877             this.dd.setXConstraint(0, 0);
27878             this.dd.setYConstraint(
27879                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27880                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27881             );
27882          }
27883         this.dragSpecs.startSize = size;
27884         this.dragSpecs.startPoint = [x, y];
27885         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27886     },
27887     
27888     /** 
27889      * @private Called after the drag operation by the DDProxy
27890      */
27891     onEndProxyDrag : function(e){
27892         Roo.get(this.proxy).setDisplayed(false);
27893         var endPoint = Roo.lib.Event.getXY(e);
27894         if(this.overlay){
27895             this.overlay.hide();
27896         }
27897         var newSize;
27898         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27899             newSize = this.dragSpecs.startSize + 
27900                 (this.placement == Roo.SplitBar.LEFT ?
27901                     endPoint[0] - this.dragSpecs.startPoint[0] :
27902                     this.dragSpecs.startPoint[0] - endPoint[0]
27903                 );
27904         }else{
27905             newSize = this.dragSpecs.startSize + 
27906                 (this.placement == Roo.SplitBar.TOP ?
27907                     endPoint[1] - this.dragSpecs.startPoint[1] :
27908                     this.dragSpecs.startPoint[1] - endPoint[1]
27909                 );
27910         }
27911         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27912         if(newSize != this.dragSpecs.startSize){
27913             if(this.fireEvent('beforeapply', this, newSize) !== false){
27914                 this.adapter.setElementSize(this, newSize);
27915                 this.fireEvent("moved", this, newSize);
27916                 this.fireEvent("resize", this, newSize);
27917             }
27918         }
27919     },
27920     
27921     /**
27922      * Get the adapter this SplitBar uses
27923      * @return The adapter object
27924      */
27925     getAdapter : function(){
27926         return this.adapter;
27927     },
27928     
27929     /**
27930      * Set the adapter this SplitBar uses
27931      * @param {Object} adapter A SplitBar adapter object
27932      */
27933     setAdapter : function(adapter){
27934         this.adapter = adapter;
27935         this.adapter.init(this);
27936     },
27937     
27938     /**
27939      * Gets the minimum size for the resizing element
27940      * @return {Number} The minimum size
27941      */
27942     getMinimumSize : function(){
27943         return this.minSize;
27944     },
27945     
27946     /**
27947      * Sets the minimum size for the resizing element
27948      * @param {Number} minSize The minimum size
27949      */
27950     setMinimumSize : function(minSize){
27951         this.minSize = minSize;
27952     },
27953     
27954     /**
27955      * Gets the maximum size for the resizing element
27956      * @return {Number} The maximum size
27957      */
27958     getMaximumSize : function(){
27959         return this.maxSize;
27960     },
27961     
27962     /**
27963      * Sets the maximum size for the resizing element
27964      * @param {Number} maxSize The maximum size
27965      */
27966     setMaximumSize : function(maxSize){
27967         this.maxSize = maxSize;
27968     },
27969     
27970     /**
27971      * Sets the initialize size for the resizing element
27972      * @param {Number} size The initial size
27973      */
27974     setCurrentSize : function(size){
27975         var oldAnimate = this.animate;
27976         this.animate = false;
27977         this.adapter.setElementSize(this, size);
27978         this.animate = oldAnimate;
27979     },
27980     
27981     /**
27982      * Destroy this splitbar. 
27983      * @param {Boolean} removeEl True to remove the element
27984      */
27985     destroy : function(removeEl){
27986         if(this.shim){
27987             this.shim.remove();
27988         }
27989         this.dd.unreg();
27990         this.proxy.parentNode.removeChild(this.proxy);
27991         if(removeEl){
27992             this.el.remove();
27993         }
27994     }
27995 });
27996
27997 /**
27998  * @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.
27999  */
28000 Roo.SplitBar.createProxy = function(dir){
28001     var proxy = new Roo.Element(document.createElement("div"));
28002     proxy.unselectable();
28003     var cls = 'x-splitbar-proxy';
28004     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
28005     document.body.appendChild(proxy.dom);
28006     return proxy.dom;
28007 };
28008
28009 /** 
28010  * @class Roo.SplitBar.BasicLayoutAdapter
28011  * Default Adapter. It assumes the splitter and resizing element are not positioned
28012  * elements and only gets/sets the width of the element. Generally used for table based layouts.
28013  */
28014 Roo.SplitBar.BasicLayoutAdapter = function(){
28015 };
28016
28017 Roo.SplitBar.BasicLayoutAdapter.prototype = {
28018     // do nothing for now
28019     init : function(s){
28020     
28021     },
28022     /**
28023      * Called before drag operations to get the current size of the resizing element. 
28024      * @param {Roo.SplitBar} s The SplitBar using this adapter
28025      */
28026      getElementSize : function(s){
28027         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28028             return s.resizingEl.getWidth();
28029         }else{
28030             return s.resizingEl.getHeight();
28031         }
28032     },
28033     
28034     /**
28035      * Called after drag operations to set the size of the resizing element.
28036      * @param {Roo.SplitBar} s The SplitBar using this adapter
28037      * @param {Number} newSize The new size to set
28038      * @param {Function} onComplete A function to be invoked when resizing is complete
28039      */
28040     setElementSize : function(s, newSize, onComplete){
28041         if(s.orientation == Roo.SplitBar.HORIZONTAL){
28042             if(!s.animate){
28043                 s.resizingEl.setWidth(newSize);
28044                 if(onComplete){
28045                     onComplete(s, newSize);
28046                 }
28047             }else{
28048                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
28049             }
28050         }else{
28051             
28052             if(!s.animate){
28053                 s.resizingEl.setHeight(newSize);
28054                 if(onComplete){
28055                     onComplete(s, newSize);
28056                 }
28057             }else{
28058                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
28059             }
28060         }
28061     }
28062 };
28063
28064 /** 
28065  *@class Roo.SplitBar.AbsoluteLayoutAdapter
28066  * @extends Roo.SplitBar.BasicLayoutAdapter
28067  * Adapter that  moves the splitter element to align with the resized sizing element. 
28068  * Used with an absolute positioned SplitBar.
28069  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
28070  * document.body, make sure you assign an id to the body element.
28071  */
28072 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
28073     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
28074     this.container = Roo.get(container);
28075 };
28076
28077 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
28078     init : function(s){
28079         this.basic.init(s);
28080     },
28081     
28082     getElementSize : function(s){
28083         return this.basic.getElementSize(s);
28084     },
28085     
28086     setElementSize : function(s, newSize, onComplete){
28087         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
28088     },
28089     
28090     moveSplitter : function(s){
28091         var yes = Roo.SplitBar;
28092         switch(s.placement){
28093             case yes.LEFT:
28094                 s.el.setX(s.resizingEl.getRight());
28095                 break;
28096             case yes.RIGHT:
28097                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
28098                 break;
28099             case yes.TOP:
28100                 s.el.setY(s.resizingEl.getBottom());
28101                 break;
28102             case yes.BOTTOM:
28103                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
28104                 break;
28105         }
28106     }
28107 };
28108
28109 /**
28110  * Orientation constant - Create a vertical SplitBar
28111  * @static
28112  * @type Number
28113  */
28114 Roo.SplitBar.VERTICAL = 1;
28115
28116 /**
28117  * Orientation constant - Create a horizontal SplitBar
28118  * @static
28119  * @type Number
28120  */
28121 Roo.SplitBar.HORIZONTAL = 2;
28122
28123 /**
28124  * Placement constant - The resizing element is to the left of the splitter element
28125  * @static
28126  * @type Number
28127  */
28128 Roo.SplitBar.LEFT = 1;
28129
28130 /**
28131  * Placement constant - The resizing element is to the right of the splitter element
28132  * @static
28133  * @type Number
28134  */
28135 Roo.SplitBar.RIGHT = 2;
28136
28137 /**
28138  * Placement constant - The resizing element is positioned above the splitter element
28139  * @static
28140  * @type Number
28141  */
28142 Roo.SplitBar.TOP = 3;
28143
28144 /**
28145  * Placement constant - The resizing element is positioned under splitter element
28146  * @static
28147  * @type Number
28148  */
28149 Roo.SplitBar.BOTTOM = 4;
28150 /*
28151  * Based on:
28152  * Ext JS Library 1.1.1
28153  * Copyright(c) 2006-2007, Ext JS, LLC.
28154  *
28155  * Originally Released Under LGPL - original licence link has changed is not relivant.
28156  *
28157  * Fork - LGPL
28158  * <script type="text/javascript">
28159  */
28160
28161 /**
28162  * @class Roo.View
28163  * @extends Roo.util.Observable
28164  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
28165  * This class also supports single and multi selection modes. <br>
28166  * Create a data model bound view:
28167  <pre><code>
28168  var store = new Roo.data.Store(...);
28169
28170  var view = new Roo.View({
28171     el : "my-element",
28172     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
28173  
28174     singleSelect: true,
28175     selectedClass: "ydataview-selected",
28176     store: store
28177  });
28178
28179  // listen for node click?
28180  view.on("click", function(vw, index, node, e){
28181  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28182  });
28183
28184  // load XML data
28185  dataModel.load("foobar.xml");
28186  </code></pre>
28187  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
28188  * <br><br>
28189  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
28190  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
28191  * 
28192  * Note: old style constructor is still suported (container, template, config)
28193  * 
28194  * @constructor
28195  * Create a new View
28196  * @param {Object} config The config object
28197  * 
28198  */
28199 Roo.View = function(config, depreciated_tpl, depreciated_config){
28200     
28201     this.parent = false;
28202     
28203     if (typeof(depreciated_tpl) == 'undefined') {
28204         // new way.. - universal constructor.
28205         Roo.apply(this, config);
28206         this.el  = Roo.get(this.el);
28207     } else {
28208         // old format..
28209         this.el  = Roo.get(config);
28210         this.tpl = depreciated_tpl;
28211         Roo.apply(this, depreciated_config);
28212     }
28213     this.wrapEl  = this.el.wrap().wrap();
28214     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28215     
28216     
28217     if(typeof(this.tpl) == "string"){
28218         this.tpl = new Roo.Template(this.tpl);
28219     } else {
28220         // support xtype ctors..
28221         this.tpl = new Roo.factory(this.tpl, Roo);
28222     }
28223     
28224     
28225     this.tpl.compile();
28226     
28227     /** @private */
28228     this.addEvents({
28229         /**
28230          * @event beforeclick
28231          * Fires before a click is processed. Returns false to cancel the default action.
28232          * @param {Roo.View} this
28233          * @param {Number} index The index of the target node
28234          * @param {HTMLElement} node The target node
28235          * @param {Roo.EventObject} e The raw event object
28236          */
28237             "beforeclick" : true,
28238         /**
28239          * @event click
28240          * Fires when a template node is clicked.
28241          * @param {Roo.View} this
28242          * @param {Number} index The index of the target node
28243          * @param {HTMLElement} node The target node
28244          * @param {Roo.EventObject} e The raw event object
28245          */
28246             "click" : true,
28247         /**
28248          * @event dblclick
28249          * Fires when a template node is double clicked.
28250          * @param {Roo.View} this
28251          * @param {Number} index The index of the target node
28252          * @param {HTMLElement} node The target node
28253          * @param {Roo.EventObject} e The raw event object
28254          */
28255             "dblclick" : true,
28256         /**
28257          * @event contextmenu
28258          * Fires when a template node is right clicked.
28259          * @param {Roo.View} this
28260          * @param {Number} index The index of the target node
28261          * @param {HTMLElement} node The target node
28262          * @param {Roo.EventObject} e The raw event object
28263          */
28264             "contextmenu" : true,
28265         /**
28266          * @event selectionchange
28267          * Fires when the selected nodes change.
28268          * @param {Roo.View} this
28269          * @param {Array} selections Array of the selected nodes
28270          */
28271             "selectionchange" : true,
28272     
28273         /**
28274          * @event beforeselect
28275          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28276          * @param {Roo.View} this
28277          * @param {HTMLElement} node The node to be selected
28278          * @param {Array} selections Array of currently selected nodes
28279          */
28280             "beforeselect" : true,
28281         /**
28282          * @event preparedata
28283          * Fires on every row to render, to allow you to change the data.
28284          * @param {Roo.View} this
28285          * @param {Object} data to be rendered (change this)
28286          */
28287           "preparedata" : true
28288           
28289           
28290         });
28291
28292
28293
28294     this.el.on({
28295         "click": this.onClick,
28296         "dblclick": this.onDblClick,
28297         "contextmenu": this.onContextMenu,
28298         scope:this
28299     });
28300
28301     this.selections = [];
28302     this.nodes = [];
28303     this.cmp = new Roo.CompositeElementLite([]);
28304     if(this.store){
28305         this.store = Roo.factory(this.store, Roo.data);
28306         this.setStore(this.store, true);
28307     }
28308     
28309     if ( this.footer && this.footer.xtype) {
28310            
28311          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28312         
28313         this.footer.dataSource = this.store;
28314         this.footer.container = fctr;
28315         this.footer = Roo.factory(this.footer, Roo);
28316         fctr.insertFirst(this.el);
28317         
28318         // this is a bit insane - as the paging toolbar seems to detach the el..
28319 //        dom.parentNode.parentNode.parentNode
28320          // they get detached?
28321     }
28322     
28323     
28324     Roo.View.superclass.constructor.call(this);
28325     
28326     
28327 };
28328
28329 Roo.extend(Roo.View, Roo.util.Observable, {
28330     
28331      /**
28332      * @cfg {Roo.data.Store} store Data store to load data from.
28333      */
28334     store : false,
28335     
28336     /**
28337      * @cfg {String|Roo.Element} el The container element.
28338      */
28339     el : '',
28340     
28341     /**
28342      * @cfg {String|Roo.Template} tpl The template used by this View 
28343      */
28344     tpl : false,
28345     /**
28346      * @cfg {String} dataName the named area of the template to use as the data area
28347      *                          Works with domtemplates roo-name="name"
28348      */
28349     dataName: false,
28350     /**
28351      * @cfg {String} selectedClass The css class to add to selected nodes
28352      */
28353     selectedClass : "x-view-selected",
28354      /**
28355      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28356      */
28357     emptyText : "",
28358     
28359     /**
28360      * @cfg {String} text to display on mask (default Loading)
28361      */
28362     mask : false,
28363     /**
28364      * @cfg {Boolean} multiSelect Allow multiple selection
28365      */
28366     multiSelect : false,
28367     /**
28368      * @cfg {Boolean} singleSelect Allow single selection
28369      */
28370     singleSelect:  false,
28371     
28372     /**
28373      * @cfg {Boolean} toggleSelect - selecting 
28374      */
28375     toggleSelect : false,
28376     
28377     /**
28378      * @cfg {Boolean} tickable - selecting 
28379      */
28380     tickable : false,
28381     
28382     /**
28383      * Returns the element this view is bound to.
28384      * @return {Roo.Element}
28385      */
28386     getEl : function(){
28387         return this.wrapEl;
28388     },
28389     
28390     
28391
28392     /**
28393      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28394      */
28395     refresh : function(){
28396         //Roo.log('refresh');
28397         var t = this.tpl;
28398         
28399         // if we are using something like 'domtemplate', then
28400         // the what gets used is:
28401         // t.applySubtemplate(NAME, data, wrapping data..)
28402         // the outer template then get' applied with
28403         //     the store 'extra data'
28404         // and the body get's added to the
28405         //      roo-name="data" node?
28406         //      <span class='roo-tpl-{name}'></span> ?????
28407         
28408         
28409         
28410         this.clearSelections();
28411         this.el.update("");
28412         var html = [];
28413         var records = this.store.getRange();
28414         if(records.length < 1) {
28415             
28416             // is this valid??  = should it render a template??
28417             
28418             this.el.update(this.emptyText);
28419             return;
28420         }
28421         var el = this.el;
28422         if (this.dataName) {
28423             this.el.update(t.apply(this.store.meta)); //????
28424             el = this.el.child('.roo-tpl-' + this.dataName);
28425         }
28426         
28427         for(var i = 0, len = records.length; i < len; i++){
28428             var data = this.prepareData(records[i].data, i, records[i]);
28429             this.fireEvent("preparedata", this, data, i, records[i]);
28430             
28431             var d = Roo.apply({}, data);
28432             
28433             if(this.tickable){
28434                 Roo.apply(d, {'roo-id' : Roo.id()});
28435                 
28436                 var _this = this;
28437             
28438                 Roo.each(this.parent.item, function(item){
28439                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28440                         return;
28441                     }
28442                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28443                 });
28444             }
28445             
28446             html[html.length] = Roo.util.Format.trim(
28447                 this.dataName ?
28448                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28449                     t.apply(d)
28450             );
28451         }
28452         
28453         
28454         
28455         el.update(html.join(""));
28456         this.nodes = el.dom.childNodes;
28457         this.updateIndexes(0);
28458     },
28459     
28460
28461     /**
28462      * Function to override to reformat the data that is sent to
28463      * the template for each node.
28464      * DEPRICATED - use the preparedata event handler.
28465      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28466      * a JSON object for an UpdateManager bound view).
28467      */
28468     prepareData : function(data, index, record)
28469     {
28470         this.fireEvent("preparedata", this, data, index, record);
28471         return data;
28472     },
28473
28474     onUpdate : function(ds, record){
28475         // Roo.log('on update');   
28476         this.clearSelections();
28477         var index = this.store.indexOf(record);
28478         var n = this.nodes[index];
28479         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28480         n.parentNode.removeChild(n);
28481         this.updateIndexes(index, index);
28482     },
28483
28484     
28485     
28486 // --------- FIXME     
28487     onAdd : function(ds, records, index)
28488     {
28489         //Roo.log(['on Add', ds, records, index] );        
28490         this.clearSelections();
28491         if(this.nodes.length == 0){
28492             this.refresh();
28493             return;
28494         }
28495         var n = this.nodes[index];
28496         for(var i = 0, len = records.length; i < len; i++){
28497             var d = this.prepareData(records[i].data, i, records[i]);
28498             if(n){
28499                 this.tpl.insertBefore(n, d);
28500             }else{
28501                 
28502                 this.tpl.append(this.el, d);
28503             }
28504         }
28505         this.updateIndexes(index);
28506     },
28507
28508     onRemove : function(ds, record, index){
28509        // Roo.log('onRemove');
28510         this.clearSelections();
28511         var el = this.dataName  ?
28512             this.el.child('.roo-tpl-' + this.dataName) :
28513             this.el; 
28514         
28515         el.dom.removeChild(this.nodes[index]);
28516         this.updateIndexes(index);
28517     },
28518
28519     /**
28520      * Refresh an individual node.
28521      * @param {Number} index
28522      */
28523     refreshNode : function(index){
28524         this.onUpdate(this.store, this.store.getAt(index));
28525     },
28526
28527     updateIndexes : function(startIndex, endIndex){
28528         var ns = this.nodes;
28529         startIndex = startIndex || 0;
28530         endIndex = endIndex || ns.length - 1;
28531         for(var i = startIndex; i <= endIndex; i++){
28532             ns[i].nodeIndex = i;
28533         }
28534     },
28535
28536     /**
28537      * Changes the data store this view uses and refresh the view.
28538      * @param {Store} store
28539      */
28540     setStore : function(store, initial){
28541         if(!initial && this.store){
28542             this.store.un("datachanged", this.refresh);
28543             this.store.un("add", this.onAdd);
28544             this.store.un("remove", this.onRemove);
28545             this.store.un("update", this.onUpdate);
28546             this.store.un("clear", this.refresh);
28547             this.store.un("beforeload", this.onBeforeLoad);
28548             this.store.un("load", this.onLoad);
28549             this.store.un("loadexception", this.onLoad);
28550         }
28551         if(store){
28552           
28553             store.on("datachanged", this.refresh, this);
28554             store.on("add", this.onAdd, this);
28555             store.on("remove", this.onRemove, this);
28556             store.on("update", this.onUpdate, this);
28557             store.on("clear", this.refresh, this);
28558             store.on("beforeload", this.onBeforeLoad, this);
28559             store.on("load", this.onLoad, this);
28560             store.on("loadexception", this.onLoad, this);
28561         }
28562         
28563         if(store){
28564             this.refresh();
28565         }
28566     },
28567     /**
28568      * onbeforeLoad - masks the loading area.
28569      *
28570      */
28571     onBeforeLoad : function(store,opts)
28572     {
28573          //Roo.log('onBeforeLoad');   
28574         if (!opts.add) {
28575             this.el.update("");
28576         }
28577         this.el.mask(this.mask ? this.mask : "Loading" ); 
28578     },
28579     onLoad : function ()
28580     {
28581         this.el.unmask();
28582     },
28583     
28584
28585     /**
28586      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28587      * @param {HTMLElement} node
28588      * @return {HTMLElement} The template node
28589      */
28590     findItemFromChild : function(node){
28591         var el = this.dataName  ?
28592             this.el.child('.roo-tpl-' + this.dataName,true) :
28593             this.el.dom; 
28594         
28595         if(!node || node.parentNode == el){
28596                     return node;
28597             }
28598             var p = node.parentNode;
28599             while(p && p != el){
28600             if(p.parentNode == el){
28601                 return p;
28602             }
28603             p = p.parentNode;
28604         }
28605             return null;
28606     },
28607
28608     /** @ignore */
28609     onClick : function(e){
28610         var item = this.findItemFromChild(e.getTarget());
28611         if(item){
28612             var index = this.indexOf(item);
28613             if(this.onItemClick(item, index, e) !== false){
28614                 this.fireEvent("click", this, index, item, e);
28615             }
28616         }else{
28617             this.clearSelections();
28618         }
28619     },
28620
28621     /** @ignore */
28622     onContextMenu : function(e){
28623         var item = this.findItemFromChild(e.getTarget());
28624         if(item){
28625             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28626         }
28627     },
28628
28629     /** @ignore */
28630     onDblClick : function(e){
28631         var item = this.findItemFromChild(e.getTarget());
28632         if(item){
28633             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28634         }
28635     },
28636
28637     onItemClick : function(item, index, e)
28638     {
28639         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28640             return false;
28641         }
28642         if (this.toggleSelect) {
28643             var m = this.isSelected(item) ? 'unselect' : 'select';
28644             //Roo.log(m);
28645             var _t = this;
28646             _t[m](item, true, false);
28647             return true;
28648         }
28649         if(this.multiSelect || this.singleSelect){
28650             if(this.multiSelect && e.shiftKey && this.lastSelection){
28651                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28652             }else{
28653                 this.select(item, this.multiSelect && e.ctrlKey);
28654                 this.lastSelection = item;
28655             }
28656             
28657             if(!this.tickable){
28658                 e.preventDefault();
28659             }
28660             
28661         }
28662         return true;
28663     },
28664
28665     /**
28666      * Get the number of selected nodes.
28667      * @return {Number}
28668      */
28669     getSelectionCount : function(){
28670         return this.selections.length;
28671     },
28672
28673     /**
28674      * Get the currently selected nodes.
28675      * @return {Array} An array of HTMLElements
28676      */
28677     getSelectedNodes : function(){
28678         return this.selections;
28679     },
28680
28681     /**
28682      * Get the indexes of the selected nodes.
28683      * @return {Array}
28684      */
28685     getSelectedIndexes : function(){
28686         var indexes = [], s = this.selections;
28687         for(var i = 0, len = s.length; i < len; i++){
28688             indexes.push(s[i].nodeIndex);
28689         }
28690         return indexes;
28691     },
28692
28693     /**
28694      * Clear all selections
28695      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28696      */
28697     clearSelections : function(suppressEvent){
28698         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28699             this.cmp.elements = this.selections;
28700             this.cmp.removeClass(this.selectedClass);
28701             this.selections = [];
28702             if(!suppressEvent){
28703                 this.fireEvent("selectionchange", this, this.selections);
28704             }
28705         }
28706     },
28707
28708     /**
28709      * Returns true if the passed node is selected
28710      * @param {HTMLElement/Number} node The node or node index
28711      * @return {Boolean}
28712      */
28713     isSelected : function(node){
28714         var s = this.selections;
28715         if(s.length < 1){
28716             return false;
28717         }
28718         node = this.getNode(node);
28719         return s.indexOf(node) !== -1;
28720     },
28721
28722     /**
28723      * Selects nodes.
28724      * @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
28725      * @param {Boolean} keepExisting (optional) true to keep existing selections
28726      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28727      */
28728     select : function(nodeInfo, keepExisting, suppressEvent){
28729         if(nodeInfo instanceof Array){
28730             if(!keepExisting){
28731                 this.clearSelections(true);
28732             }
28733             for(var i = 0, len = nodeInfo.length; i < len; i++){
28734                 this.select(nodeInfo[i], true, true);
28735             }
28736             return;
28737         } 
28738         var node = this.getNode(nodeInfo);
28739         if(!node || this.isSelected(node)){
28740             return; // already selected.
28741         }
28742         if(!keepExisting){
28743             this.clearSelections(true);
28744         }
28745         
28746         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28747             Roo.fly(node).addClass(this.selectedClass);
28748             this.selections.push(node);
28749             if(!suppressEvent){
28750                 this.fireEvent("selectionchange", this, this.selections);
28751             }
28752         }
28753         
28754         
28755     },
28756       /**
28757      * Unselects nodes.
28758      * @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
28759      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28760      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28761      */
28762     unselect : function(nodeInfo, keepExisting, suppressEvent)
28763     {
28764         if(nodeInfo instanceof Array){
28765             Roo.each(this.selections, function(s) {
28766                 this.unselect(s, nodeInfo);
28767             }, this);
28768             return;
28769         }
28770         var node = this.getNode(nodeInfo);
28771         if(!node || !this.isSelected(node)){
28772             //Roo.log("not selected");
28773             return; // not selected.
28774         }
28775         // fireevent???
28776         var ns = [];
28777         Roo.each(this.selections, function(s) {
28778             if (s == node ) {
28779                 Roo.fly(node).removeClass(this.selectedClass);
28780
28781                 return;
28782             }
28783             ns.push(s);
28784         },this);
28785         
28786         this.selections= ns;
28787         this.fireEvent("selectionchange", this, this.selections);
28788     },
28789
28790     /**
28791      * Gets a template node.
28792      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28793      * @return {HTMLElement} The node or null if it wasn't found
28794      */
28795     getNode : function(nodeInfo){
28796         if(typeof nodeInfo == "string"){
28797             return document.getElementById(nodeInfo);
28798         }else if(typeof nodeInfo == "number"){
28799             return this.nodes[nodeInfo];
28800         }
28801         return nodeInfo;
28802     },
28803
28804     /**
28805      * Gets a range template nodes.
28806      * @param {Number} startIndex
28807      * @param {Number} endIndex
28808      * @return {Array} An array of nodes
28809      */
28810     getNodes : function(start, end){
28811         var ns = this.nodes;
28812         start = start || 0;
28813         end = typeof end == "undefined" ? ns.length - 1 : end;
28814         var nodes = [];
28815         if(start <= end){
28816             for(var i = start; i <= end; i++){
28817                 nodes.push(ns[i]);
28818             }
28819         } else{
28820             for(var i = start; i >= end; i--){
28821                 nodes.push(ns[i]);
28822             }
28823         }
28824         return nodes;
28825     },
28826
28827     /**
28828      * Finds the index of the passed node
28829      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28830      * @return {Number} The index of the node or -1
28831      */
28832     indexOf : function(node){
28833         node = this.getNode(node);
28834         if(typeof node.nodeIndex == "number"){
28835             return node.nodeIndex;
28836         }
28837         var ns = this.nodes;
28838         for(var i = 0, len = ns.length; i < len; i++){
28839             if(ns[i] == node){
28840                 return i;
28841             }
28842         }
28843         return -1;
28844     }
28845 });
28846 /*
28847  * Based on:
28848  * Ext JS Library 1.1.1
28849  * Copyright(c) 2006-2007, Ext JS, LLC.
28850  *
28851  * Originally Released Under LGPL - original licence link has changed is not relivant.
28852  *
28853  * Fork - LGPL
28854  * <script type="text/javascript">
28855  */
28856
28857 /**
28858  * @class Roo.JsonView
28859  * @extends Roo.View
28860  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28861 <pre><code>
28862 var view = new Roo.JsonView({
28863     container: "my-element",
28864     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28865     multiSelect: true, 
28866     jsonRoot: "data" 
28867 });
28868
28869 // listen for node click?
28870 view.on("click", function(vw, index, node, e){
28871     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28872 });
28873
28874 // direct load of JSON data
28875 view.load("foobar.php");
28876
28877 // Example from my blog list
28878 var tpl = new Roo.Template(
28879     '&lt;div class="entry"&gt;' +
28880     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28881     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28882     "&lt;/div&gt;&lt;hr /&gt;"
28883 );
28884
28885 var moreView = new Roo.JsonView({
28886     container :  "entry-list", 
28887     template : tpl,
28888     jsonRoot: "posts"
28889 });
28890 moreView.on("beforerender", this.sortEntries, this);
28891 moreView.load({
28892     url: "/blog/get-posts.php",
28893     params: "allposts=true",
28894     text: "Loading Blog Entries..."
28895 });
28896 </code></pre>
28897
28898 * Note: old code is supported with arguments : (container, template, config)
28899
28900
28901  * @constructor
28902  * Create a new JsonView
28903  * 
28904  * @param {Object} config The config object
28905  * 
28906  */
28907 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28908     
28909     
28910     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28911
28912     var um = this.el.getUpdateManager();
28913     um.setRenderer(this);
28914     um.on("update", this.onLoad, this);
28915     um.on("failure", this.onLoadException, this);
28916
28917     /**
28918      * @event beforerender
28919      * Fires before rendering of the downloaded JSON data.
28920      * @param {Roo.JsonView} this
28921      * @param {Object} data The JSON data loaded
28922      */
28923     /**
28924      * @event load
28925      * Fires when data is loaded.
28926      * @param {Roo.JsonView} this
28927      * @param {Object} data The JSON data loaded
28928      * @param {Object} response The raw Connect response object
28929      */
28930     /**
28931      * @event loadexception
28932      * Fires when loading fails.
28933      * @param {Roo.JsonView} this
28934      * @param {Object} response The raw Connect response object
28935      */
28936     this.addEvents({
28937         'beforerender' : true,
28938         'load' : true,
28939         'loadexception' : true
28940     });
28941 };
28942 Roo.extend(Roo.JsonView, Roo.View, {
28943     /**
28944      * @type {String} The root property in the loaded JSON object that contains the data
28945      */
28946     jsonRoot : "",
28947
28948     /**
28949      * Refreshes the view.
28950      */
28951     refresh : function(){
28952         this.clearSelections();
28953         this.el.update("");
28954         var html = [];
28955         var o = this.jsonData;
28956         if(o && o.length > 0){
28957             for(var i = 0, len = o.length; i < len; i++){
28958                 var data = this.prepareData(o[i], i, o);
28959                 html[html.length] = this.tpl.apply(data);
28960             }
28961         }else{
28962             html.push(this.emptyText);
28963         }
28964         this.el.update(html.join(""));
28965         this.nodes = this.el.dom.childNodes;
28966         this.updateIndexes(0);
28967     },
28968
28969     /**
28970      * 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.
28971      * @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:
28972      <pre><code>
28973      view.load({
28974          url: "your-url.php",
28975          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28976          callback: yourFunction,
28977          scope: yourObject, //(optional scope)
28978          discardUrl: false,
28979          nocache: false,
28980          text: "Loading...",
28981          timeout: 30,
28982          scripts: false
28983      });
28984      </code></pre>
28985      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28986      * 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.
28987      * @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}
28988      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28989      * @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.
28990      */
28991     load : function(){
28992         var um = this.el.getUpdateManager();
28993         um.update.apply(um, arguments);
28994     },
28995
28996     // note - render is a standard framework call...
28997     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28998     render : function(el, response){
28999         
29000         this.clearSelections();
29001         this.el.update("");
29002         var o;
29003         try{
29004             if (response != '') {
29005                 o = Roo.util.JSON.decode(response.responseText);
29006                 if(this.jsonRoot){
29007                     
29008                     o = o[this.jsonRoot];
29009                 }
29010             }
29011         } catch(e){
29012         }
29013         /**
29014          * The current JSON data or null
29015          */
29016         this.jsonData = o;
29017         this.beforeRender();
29018         this.refresh();
29019     },
29020
29021 /**
29022  * Get the number of records in the current JSON dataset
29023  * @return {Number}
29024  */
29025     getCount : function(){
29026         return this.jsonData ? this.jsonData.length : 0;
29027     },
29028
29029 /**
29030  * Returns the JSON object for the specified node(s)
29031  * @param {HTMLElement/Array} node The node or an array of nodes
29032  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
29033  * you get the JSON object for the node
29034  */
29035     getNodeData : function(node){
29036         if(node instanceof Array){
29037             var data = [];
29038             for(var i = 0, len = node.length; i < len; i++){
29039                 data.push(this.getNodeData(node[i]));
29040             }
29041             return data;
29042         }
29043         return this.jsonData[this.indexOf(node)] || null;
29044     },
29045
29046     beforeRender : function(){
29047         this.snapshot = this.jsonData;
29048         if(this.sortInfo){
29049             this.sort.apply(this, this.sortInfo);
29050         }
29051         this.fireEvent("beforerender", this, this.jsonData);
29052     },
29053
29054     onLoad : function(el, o){
29055         this.fireEvent("load", this, this.jsonData, o);
29056     },
29057
29058     onLoadException : function(el, o){
29059         this.fireEvent("loadexception", this, o);
29060     },
29061
29062 /**
29063  * Filter the data by a specific property.
29064  * @param {String} property A property on your JSON objects
29065  * @param {String/RegExp} value Either string that the property values
29066  * should start with, or a RegExp to test against the property
29067  */
29068     filter : function(property, value){
29069         if(this.jsonData){
29070             var data = [];
29071             var ss = this.snapshot;
29072             if(typeof value == "string"){
29073                 var vlen = value.length;
29074                 if(vlen == 0){
29075                     this.clearFilter();
29076                     return;
29077                 }
29078                 value = value.toLowerCase();
29079                 for(var i = 0, len = ss.length; i < len; i++){
29080                     var o = ss[i];
29081                     if(o[property].substr(0, vlen).toLowerCase() == value){
29082                         data.push(o);
29083                     }
29084                 }
29085             } else if(value.exec){ // regex?
29086                 for(var i = 0, len = ss.length; i < len; i++){
29087                     var o = ss[i];
29088                     if(value.test(o[property])){
29089                         data.push(o);
29090                     }
29091                 }
29092             } else{
29093                 return;
29094             }
29095             this.jsonData = data;
29096             this.refresh();
29097         }
29098     },
29099
29100 /**
29101  * Filter by a function. The passed function will be called with each
29102  * object in the current dataset. If the function returns true the value is kept,
29103  * otherwise it is filtered.
29104  * @param {Function} fn
29105  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
29106  */
29107     filterBy : function(fn, scope){
29108         if(this.jsonData){
29109             var data = [];
29110             var ss = this.snapshot;
29111             for(var i = 0, len = ss.length; i < len; i++){
29112                 var o = ss[i];
29113                 if(fn.call(scope || this, o)){
29114                     data.push(o);
29115                 }
29116             }
29117             this.jsonData = data;
29118             this.refresh();
29119         }
29120     },
29121
29122 /**
29123  * Clears the current filter.
29124  */
29125     clearFilter : function(){
29126         if(this.snapshot && this.jsonData != this.snapshot){
29127             this.jsonData = this.snapshot;
29128             this.refresh();
29129         }
29130     },
29131
29132
29133 /**
29134  * Sorts the data for this view and refreshes it.
29135  * @param {String} property A property on your JSON objects to sort on
29136  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
29137  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
29138  */
29139     sort : function(property, dir, sortType){
29140         this.sortInfo = Array.prototype.slice.call(arguments, 0);
29141         if(this.jsonData){
29142             var p = property;
29143             var dsc = dir && dir.toLowerCase() == "desc";
29144             var f = function(o1, o2){
29145                 var v1 = sortType ? sortType(o1[p]) : o1[p];
29146                 var v2 = sortType ? sortType(o2[p]) : o2[p];
29147                 ;
29148                 if(v1 < v2){
29149                     return dsc ? +1 : -1;
29150                 } else if(v1 > v2){
29151                     return dsc ? -1 : +1;
29152                 } else{
29153                     return 0;
29154                 }
29155             };
29156             this.jsonData.sort(f);
29157             this.refresh();
29158             if(this.jsonData != this.snapshot){
29159                 this.snapshot.sort(f);
29160             }
29161         }
29162     }
29163 });/*
29164  * Based on:
29165  * Ext JS Library 1.1.1
29166  * Copyright(c) 2006-2007, Ext JS, LLC.
29167  *
29168  * Originally Released Under LGPL - original licence link has changed is not relivant.
29169  *
29170  * Fork - LGPL
29171  * <script type="text/javascript">
29172  */
29173  
29174
29175 /**
29176  * @class Roo.ColorPalette
29177  * @extends Roo.Component
29178  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
29179  * Here's an example of typical usage:
29180  * <pre><code>
29181 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
29182 cp.render('my-div');
29183
29184 cp.on('select', function(palette, selColor){
29185     // do something with selColor
29186 });
29187 </code></pre>
29188  * @constructor
29189  * Create a new ColorPalette
29190  * @param {Object} config The config object
29191  */
29192 Roo.ColorPalette = function(config){
29193     Roo.ColorPalette.superclass.constructor.call(this, config);
29194     this.addEvents({
29195         /**
29196              * @event select
29197              * Fires when a color is selected
29198              * @param {ColorPalette} this
29199              * @param {String} color The 6-digit color hex code (without the # symbol)
29200              */
29201         select: true
29202     });
29203
29204     if(this.handler){
29205         this.on("select", this.handler, this.scope, true);
29206     }
29207 };
29208 Roo.extend(Roo.ColorPalette, Roo.Component, {
29209     /**
29210      * @cfg {String} itemCls
29211      * The CSS class to apply to the containing element (defaults to "x-color-palette")
29212      */
29213     itemCls : "x-color-palette",
29214     /**
29215      * @cfg {String} value
29216      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
29217      * the hex codes are case-sensitive.
29218      */
29219     value : null,
29220     clickEvent:'click',
29221     // private
29222     ctype: "Roo.ColorPalette",
29223
29224     /**
29225      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29226      */
29227     allowReselect : false,
29228
29229     /**
29230      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
29231      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
29232      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29233      * of colors with the width setting until the box is symmetrical.</p>
29234      * <p>You can override individual colors if needed:</p>
29235      * <pre><code>
29236 var cp = new Roo.ColorPalette();
29237 cp.colors[0] = "FF0000";  // change the first box to red
29238 </code></pre>
29239
29240 Or you can provide a custom array of your own for complete control:
29241 <pre><code>
29242 var cp = new Roo.ColorPalette();
29243 cp.colors = ["000000", "993300", "333300"];
29244 </code></pre>
29245      * @type Array
29246      */
29247     colors : [
29248         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29249         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29250         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29251         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29252         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29253     ],
29254
29255     // private
29256     onRender : function(container, position){
29257         var t = new Roo.MasterTemplate(
29258             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29259         );
29260         var c = this.colors;
29261         for(var i = 0, len = c.length; i < len; i++){
29262             t.add([c[i]]);
29263         }
29264         var el = document.createElement("div");
29265         el.className = this.itemCls;
29266         t.overwrite(el);
29267         container.dom.insertBefore(el, position);
29268         this.el = Roo.get(el);
29269         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29270         if(this.clickEvent != 'click'){
29271             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29272         }
29273     },
29274
29275     // private
29276     afterRender : function(){
29277         Roo.ColorPalette.superclass.afterRender.call(this);
29278         if(this.value){
29279             var s = this.value;
29280             this.value = null;
29281             this.select(s);
29282         }
29283     },
29284
29285     // private
29286     handleClick : function(e, t){
29287         e.preventDefault();
29288         if(!this.disabled){
29289             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29290             this.select(c.toUpperCase());
29291         }
29292     },
29293
29294     /**
29295      * Selects the specified color in the palette (fires the select event)
29296      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29297      */
29298     select : function(color){
29299         color = color.replace("#", "");
29300         if(color != this.value || this.allowReselect){
29301             var el = this.el;
29302             if(this.value){
29303                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29304             }
29305             el.child("a.color-"+color).addClass("x-color-palette-sel");
29306             this.value = color;
29307             this.fireEvent("select", this, color);
29308         }
29309     }
29310 });/*
29311  * Based on:
29312  * Ext JS Library 1.1.1
29313  * Copyright(c) 2006-2007, Ext JS, LLC.
29314  *
29315  * Originally Released Under LGPL - original licence link has changed is not relivant.
29316  *
29317  * Fork - LGPL
29318  * <script type="text/javascript">
29319  */
29320  
29321 /**
29322  * @class Roo.DatePicker
29323  * @extends Roo.Component
29324  * Simple date picker class.
29325  * @constructor
29326  * Create a new DatePicker
29327  * @param {Object} config The config object
29328  */
29329 Roo.DatePicker = function(config){
29330     Roo.DatePicker.superclass.constructor.call(this, config);
29331
29332     this.value = config && config.value ?
29333                  config.value.clearTime() : new Date().clearTime();
29334
29335     this.addEvents({
29336         /**
29337              * @event select
29338              * Fires when a date is selected
29339              * @param {DatePicker} this
29340              * @param {Date} date The selected date
29341              */
29342         'select': true,
29343         /**
29344              * @event monthchange
29345              * Fires when the displayed month changes 
29346              * @param {DatePicker} this
29347              * @param {Date} date The selected month
29348              */
29349         'monthchange': true
29350     });
29351
29352     if(this.handler){
29353         this.on("select", this.handler,  this.scope || this);
29354     }
29355     // build the disabledDatesRE
29356     if(!this.disabledDatesRE && this.disabledDates){
29357         var dd = this.disabledDates;
29358         var re = "(?:";
29359         for(var i = 0; i < dd.length; i++){
29360             re += dd[i];
29361             if(i != dd.length-1) {
29362                 re += "|";
29363             }
29364         }
29365         this.disabledDatesRE = new RegExp(re + ")");
29366     }
29367 };
29368
29369 Roo.extend(Roo.DatePicker, Roo.Component, {
29370     /**
29371      * @cfg {String} todayText
29372      * The text to display on the button that selects the current date (defaults to "Today")
29373      */
29374     todayText : "Today",
29375     /**
29376      * @cfg {String} okText
29377      * The text to display on the ok button
29378      */
29379     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29380     /**
29381      * @cfg {String} cancelText
29382      * The text to display on the cancel button
29383      */
29384     cancelText : "Cancel",
29385     /**
29386      * @cfg {String} todayTip
29387      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29388      */
29389     todayTip : "{0} (Spacebar)",
29390     /**
29391      * @cfg {Date} minDate
29392      * Minimum allowable date (JavaScript date object, defaults to null)
29393      */
29394     minDate : null,
29395     /**
29396      * @cfg {Date} maxDate
29397      * Maximum allowable date (JavaScript date object, defaults to null)
29398      */
29399     maxDate : null,
29400     /**
29401      * @cfg {String} minText
29402      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29403      */
29404     minText : "This date is before the minimum date",
29405     /**
29406      * @cfg {String} maxText
29407      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29408      */
29409     maxText : "This date is after the maximum date",
29410     /**
29411      * @cfg {String} format
29412      * The default date format string which can be overriden for localization support.  The format must be
29413      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29414      */
29415     format : "m/d/y",
29416     /**
29417      * @cfg {Array} disabledDays
29418      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29419      */
29420     disabledDays : null,
29421     /**
29422      * @cfg {String} disabledDaysText
29423      * The tooltip to display when the date falls on a disabled day (defaults to "")
29424      */
29425     disabledDaysText : "",
29426     /**
29427      * @cfg {RegExp} disabledDatesRE
29428      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29429      */
29430     disabledDatesRE : null,
29431     /**
29432      * @cfg {String} disabledDatesText
29433      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29434      */
29435     disabledDatesText : "",
29436     /**
29437      * @cfg {Boolean} constrainToViewport
29438      * True to constrain the date picker to the viewport (defaults to true)
29439      */
29440     constrainToViewport : true,
29441     /**
29442      * @cfg {Array} monthNames
29443      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29444      */
29445     monthNames : Date.monthNames,
29446     /**
29447      * @cfg {Array} dayNames
29448      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29449      */
29450     dayNames : Date.dayNames,
29451     /**
29452      * @cfg {String} nextText
29453      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29454      */
29455     nextText: 'Next Month (Control+Right)',
29456     /**
29457      * @cfg {String} prevText
29458      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29459      */
29460     prevText: 'Previous Month (Control+Left)',
29461     /**
29462      * @cfg {String} monthYearText
29463      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29464      */
29465     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29466     /**
29467      * @cfg {Number} startDay
29468      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29469      */
29470     startDay : 0,
29471     /**
29472      * @cfg {Bool} showClear
29473      * Show a clear button (usefull for date form elements that can be blank.)
29474      */
29475     
29476     showClear: false,
29477     
29478     /**
29479      * Sets the value of the date field
29480      * @param {Date} value The date to set
29481      */
29482     setValue : function(value){
29483         var old = this.value;
29484         
29485         if (typeof(value) == 'string') {
29486          
29487             value = Date.parseDate(value, this.format);
29488         }
29489         if (!value) {
29490             value = new Date();
29491         }
29492         
29493         this.value = value.clearTime(true);
29494         if(this.el){
29495             this.update(this.value);
29496         }
29497     },
29498
29499     /**
29500      * Gets the current selected value of the date field
29501      * @return {Date} The selected date
29502      */
29503     getValue : function(){
29504         return this.value;
29505     },
29506
29507     // private
29508     focus : function(){
29509         if(this.el){
29510             this.update(this.activeDate);
29511         }
29512     },
29513
29514     // privateval
29515     onRender : function(container, position){
29516         
29517         var m = [
29518              '<table cellspacing="0">',
29519                 '<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>',
29520                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29521         var dn = this.dayNames;
29522         for(var i = 0; i < 7; i++){
29523             var d = this.startDay+i;
29524             if(d > 6){
29525                 d = d-7;
29526             }
29527             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29528         }
29529         m[m.length] = "</tr></thead><tbody><tr>";
29530         for(var i = 0; i < 42; i++) {
29531             if(i % 7 == 0 && i != 0){
29532                 m[m.length] = "</tr><tr>";
29533             }
29534             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29535         }
29536         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29537             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29538
29539         var el = document.createElement("div");
29540         el.className = "x-date-picker";
29541         el.innerHTML = m.join("");
29542
29543         container.dom.insertBefore(el, position);
29544
29545         this.el = Roo.get(el);
29546         this.eventEl = Roo.get(el.firstChild);
29547
29548         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29549             handler: this.showPrevMonth,
29550             scope: this,
29551             preventDefault:true,
29552             stopDefault:true
29553         });
29554
29555         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29556             handler: this.showNextMonth,
29557             scope: this,
29558             preventDefault:true,
29559             stopDefault:true
29560         });
29561
29562         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29563
29564         this.monthPicker = this.el.down('div.x-date-mp');
29565         this.monthPicker.enableDisplayMode('block');
29566         
29567         var kn = new Roo.KeyNav(this.eventEl, {
29568             "left" : function(e){
29569                 e.ctrlKey ?
29570                     this.showPrevMonth() :
29571                     this.update(this.activeDate.add("d", -1));
29572             },
29573
29574             "right" : function(e){
29575                 e.ctrlKey ?
29576                     this.showNextMonth() :
29577                     this.update(this.activeDate.add("d", 1));
29578             },
29579
29580             "up" : function(e){
29581                 e.ctrlKey ?
29582                     this.showNextYear() :
29583                     this.update(this.activeDate.add("d", -7));
29584             },
29585
29586             "down" : function(e){
29587                 e.ctrlKey ?
29588                     this.showPrevYear() :
29589                     this.update(this.activeDate.add("d", 7));
29590             },
29591
29592             "pageUp" : function(e){
29593                 this.showNextMonth();
29594             },
29595
29596             "pageDown" : function(e){
29597                 this.showPrevMonth();
29598             },
29599
29600             "enter" : function(e){
29601                 e.stopPropagation();
29602                 return true;
29603             },
29604
29605             scope : this
29606         });
29607
29608         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29609
29610         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29611
29612         this.el.unselectable();
29613         
29614         this.cells = this.el.select("table.x-date-inner tbody td");
29615         this.textNodes = this.el.query("table.x-date-inner tbody span");
29616
29617         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29618             text: "&#160;",
29619             tooltip: this.monthYearText
29620         });
29621
29622         this.mbtn.on('click', this.showMonthPicker, this);
29623         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29624
29625
29626         var today = (new Date()).dateFormat(this.format);
29627         
29628         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29629         if (this.showClear) {
29630             baseTb.add( new Roo.Toolbar.Fill());
29631         }
29632         baseTb.add({
29633             text: String.format(this.todayText, today),
29634             tooltip: String.format(this.todayTip, today),
29635             handler: this.selectToday,
29636             scope: this
29637         });
29638         
29639         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29640             
29641         //});
29642         if (this.showClear) {
29643             
29644             baseTb.add( new Roo.Toolbar.Fill());
29645             baseTb.add({
29646                 text: '&#160;',
29647                 cls: 'x-btn-icon x-btn-clear',
29648                 handler: function() {
29649                     //this.value = '';
29650                     this.fireEvent("select", this, '');
29651                 },
29652                 scope: this
29653             });
29654         }
29655         
29656         
29657         if(Roo.isIE){
29658             this.el.repaint();
29659         }
29660         this.update(this.value);
29661     },
29662
29663     createMonthPicker : function(){
29664         if(!this.monthPicker.dom.firstChild){
29665             var buf = ['<table border="0" cellspacing="0">'];
29666             for(var i = 0; i < 6; i++){
29667                 buf.push(
29668                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29669                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29670                     i == 0 ?
29671                     '<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>' :
29672                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29673                 );
29674             }
29675             buf.push(
29676                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29677                     this.okText,
29678                     '</button><button type="button" class="x-date-mp-cancel">',
29679                     this.cancelText,
29680                     '</button></td></tr>',
29681                 '</table>'
29682             );
29683             this.monthPicker.update(buf.join(''));
29684             this.monthPicker.on('click', this.onMonthClick, this);
29685             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29686
29687             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29688             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29689
29690             this.mpMonths.each(function(m, a, i){
29691                 i += 1;
29692                 if((i%2) == 0){
29693                     m.dom.xmonth = 5 + Math.round(i * .5);
29694                 }else{
29695                     m.dom.xmonth = Math.round((i-1) * .5);
29696                 }
29697             });
29698         }
29699     },
29700
29701     showMonthPicker : function(){
29702         this.createMonthPicker();
29703         var size = this.el.getSize();
29704         this.monthPicker.setSize(size);
29705         this.monthPicker.child('table').setSize(size);
29706
29707         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29708         this.updateMPMonth(this.mpSelMonth);
29709         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29710         this.updateMPYear(this.mpSelYear);
29711
29712         this.monthPicker.slideIn('t', {duration:.2});
29713     },
29714
29715     updateMPYear : function(y){
29716         this.mpyear = y;
29717         var ys = this.mpYears.elements;
29718         for(var i = 1; i <= 10; i++){
29719             var td = ys[i-1], y2;
29720             if((i%2) == 0){
29721                 y2 = y + Math.round(i * .5);
29722                 td.firstChild.innerHTML = y2;
29723                 td.xyear = y2;
29724             }else{
29725                 y2 = y - (5-Math.round(i * .5));
29726                 td.firstChild.innerHTML = y2;
29727                 td.xyear = y2;
29728             }
29729             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29730         }
29731     },
29732
29733     updateMPMonth : function(sm){
29734         this.mpMonths.each(function(m, a, i){
29735             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29736         });
29737     },
29738
29739     selectMPMonth: function(m){
29740         
29741     },
29742
29743     onMonthClick : function(e, t){
29744         e.stopEvent();
29745         var el = new Roo.Element(t), pn;
29746         if(el.is('button.x-date-mp-cancel')){
29747             this.hideMonthPicker();
29748         }
29749         else if(el.is('button.x-date-mp-ok')){
29750             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29751             this.hideMonthPicker();
29752         }
29753         else if(pn = el.up('td.x-date-mp-month', 2)){
29754             this.mpMonths.removeClass('x-date-mp-sel');
29755             pn.addClass('x-date-mp-sel');
29756             this.mpSelMonth = pn.dom.xmonth;
29757         }
29758         else if(pn = el.up('td.x-date-mp-year', 2)){
29759             this.mpYears.removeClass('x-date-mp-sel');
29760             pn.addClass('x-date-mp-sel');
29761             this.mpSelYear = pn.dom.xyear;
29762         }
29763         else if(el.is('a.x-date-mp-prev')){
29764             this.updateMPYear(this.mpyear-10);
29765         }
29766         else if(el.is('a.x-date-mp-next')){
29767             this.updateMPYear(this.mpyear+10);
29768         }
29769     },
29770
29771     onMonthDblClick : function(e, t){
29772         e.stopEvent();
29773         var el = new Roo.Element(t), pn;
29774         if(pn = el.up('td.x-date-mp-month', 2)){
29775             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29776             this.hideMonthPicker();
29777         }
29778         else if(pn = el.up('td.x-date-mp-year', 2)){
29779             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29780             this.hideMonthPicker();
29781         }
29782     },
29783
29784     hideMonthPicker : function(disableAnim){
29785         if(this.monthPicker){
29786             if(disableAnim === true){
29787                 this.monthPicker.hide();
29788             }else{
29789                 this.monthPicker.slideOut('t', {duration:.2});
29790             }
29791         }
29792     },
29793
29794     // private
29795     showPrevMonth : function(e){
29796         this.update(this.activeDate.add("mo", -1));
29797     },
29798
29799     // private
29800     showNextMonth : function(e){
29801         this.update(this.activeDate.add("mo", 1));
29802     },
29803
29804     // private
29805     showPrevYear : function(){
29806         this.update(this.activeDate.add("y", -1));
29807     },
29808
29809     // private
29810     showNextYear : function(){
29811         this.update(this.activeDate.add("y", 1));
29812     },
29813
29814     // private
29815     handleMouseWheel : function(e){
29816         var delta = e.getWheelDelta();
29817         if(delta > 0){
29818             this.showPrevMonth();
29819             e.stopEvent();
29820         } else if(delta < 0){
29821             this.showNextMonth();
29822             e.stopEvent();
29823         }
29824     },
29825
29826     // private
29827     handleDateClick : function(e, t){
29828         e.stopEvent();
29829         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29830             this.setValue(new Date(t.dateValue));
29831             this.fireEvent("select", this, this.value);
29832         }
29833     },
29834
29835     // private
29836     selectToday : function(){
29837         this.setValue(new Date().clearTime());
29838         this.fireEvent("select", this, this.value);
29839     },
29840
29841     // private
29842     update : function(date)
29843     {
29844         var vd = this.activeDate;
29845         this.activeDate = date;
29846         if(vd && this.el){
29847             var t = date.getTime();
29848             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29849                 this.cells.removeClass("x-date-selected");
29850                 this.cells.each(function(c){
29851                    if(c.dom.firstChild.dateValue == t){
29852                        c.addClass("x-date-selected");
29853                        setTimeout(function(){
29854                             try{c.dom.firstChild.focus();}catch(e){}
29855                        }, 50);
29856                        return false;
29857                    }
29858                 });
29859                 return;
29860             }
29861         }
29862         
29863         var days = date.getDaysInMonth();
29864         var firstOfMonth = date.getFirstDateOfMonth();
29865         var startingPos = firstOfMonth.getDay()-this.startDay;
29866
29867         if(startingPos <= this.startDay){
29868             startingPos += 7;
29869         }
29870
29871         var pm = date.add("mo", -1);
29872         var prevStart = pm.getDaysInMonth()-startingPos;
29873
29874         var cells = this.cells.elements;
29875         var textEls = this.textNodes;
29876         days += startingPos;
29877
29878         // convert everything to numbers so it's fast
29879         var day = 86400000;
29880         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29881         var today = new Date().clearTime().getTime();
29882         var sel = date.clearTime().getTime();
29883         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29884         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29885         var ddMatch = this.disabledDatesRE;
29886         var ddText = this.disabledDatesText;
29887         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29888         var ddaysText = this.disabledDaysText;
29889         var format = this.format;
29890
29891         var setCellClass = function(cal, cell){
29892             cell.title = "";
29893             var t = d.getTime();
29894             cell.firstChild.dateValue = t;
29895             if(t == today){
29896                 cell.className += " x-date-today";
29897                 cell.title = cal.todayText;
29898             }
29899             if(t == sel){
29900                 cell.className += " x-date-selected";
29901                 setTimeout(function(){
29902                     try{cell.firstChild.focus();}catch(e){}
29903                 }, 50);
29904             }
29905             // disabling
29906             if(t < min) {
29907                 cell.className = " x-date-disabled";
29908                 cell.title = cal.minText;
29909                 return;
29910             }
29911             if(t > max) {
29912                 cell.className = " x-date-disabled";
29913                 cell.title = cal.maxText;
29914                 return;
29915             }
29916             if(ddays){
29917                 if(ddays.indexOf(d.getDay()) != -1){
29918                     cell.title = ddaysText;
29919                     cell.className = " x-date-disabled";
29920                 }
29921             }
29922             if(ddMatch && format){
29923                 var fvalue = d.dateFormat(format);
29924                 if(ddMatch.test(fvalue)){
29925                     cell.title = ddText.replace("%0", fvalue);
29926                     cell.className = " x-date-disabled";
29927                 }
29928             }
29929         };
29930
29931         var i = 0;
29932         for(; i < startingPos; i++) {
29933             textEls[i].innerHTML = (++prevStart);
29934             d.setDate(d.getDate()+1);
29935             cells[i].className = "x-date-prevday";
29936             setCellClass(this, cells[i]);
29937         }
29938         for(; i < days; i++){
29939             intDay = i - startingPos + 1;
29940             textEls[i].innerHTML = (intDay);
29941             d.setDate(d.getDate()+1);
29942             cells[i].className = "x-date-active";
29943             setCellClass(this, cells[i]);
29944         }
29945         var extraDays = 0;
29946         for(; i < 42; i++) {
29947              textEls[i].innerHTML = (++extraDays);
29948              d.setDate(d.getDate()+1);
29949              cells[i].className = "x-date-nextday";
29950              setCellClass(this, cells[i]);
29951         }
29952
29953         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29954         this.fireEvent('monthchange', this, date);
29955         
29956         if(!this.internalRender){
29957             var main = this.el.dom.firstChild;
29958             var w = main.offsetWidth;
29959             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29960             Roo.fly(main).setWidth(w);
29961             this.internalRender = true;
29962             // opera does not respect the auto grow header center column
29963             // then, after it gets a width opera refuses to recalculate
29964             // without a second pass
29965             if(Roo.isOpera && !this.secondPass){
29966                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29967                 this.secondPass = true;
29968                 this.update.defer(10, this, [date]);
29969             }
29970         }
29971         
29972         
29973     }
29974 });        /*
29975  * Based on:
29976  * Ext JS Library 1.1.1
29977  * Copyright(c) 2006-2007, Ext JS, LLC.
29978  *
29979  * Originally Released Under LGPL - original licence link has changed is not relivant.
29980  *
29981  * Fork - LGPL
29982  * <script type="text/javascript">
29983  */
29984 /**
29985  * @class Roo.TabPanel
29986  * @extends Roo.util.Observable
29987  * A lightweight tab container.
29988  * <br><br>
29989  * Usage:
29990  * <pre><code>
29991 // basic tabs 1, built from existing content
29992 var tabs = new Roo.TabPanel("tabs1");
29993 tabs.addTab("script", "View Script");
29994 tabs.addTab("markup", "View Markup");
29995 tabs.activate("script");
29996
29997 // more advanced tabs, built from javascript
29998 var jtabs = new Roo.TabPanel("jtabs");
29999 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
30000
30001 // set up the UpdateManager
30002 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
30003 var updater = tab2.getUpdateManager();
30004 updater.setDefaultUrl("ajax1.htm");
30005 tab2.on('activate', updater.refresh, updater, true);
30006
30007 // Use setUrl for Ajax loading
30008 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
30009 tab3.setUrl("ajax2.htm", null, true);
30010
30011 // Disabled tab
30012 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
30013 tab4.disable();
30014
30015 jtabs.activate("jtabs-1");
30016  * </code></pre>
30017  * @constructor
30018  * Create a new TabPanel.
30019  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
30020  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
30021  */
30022 Roo.TabPanel = function(container, config){
30023     /**
30024     * The container element for this TabPanel.
30025     * @type Roo.Element
30026     */
30027     this.el = Roo.get(container, true);
30028     if(config){
30029         if(typeof config == "boolean"){
30030             this.tabPosition = config ? "bottom" : "top";
30031         }else{
30032             Roo.apply(this, config);
30033         }
30034     }
30035     if(this.tabPosition == "bottom"){
30036         this.bodyEl = Roo.get(this.createBody(this.el.dom));
30037         this.el.addClass("x-tabs-bottom");
30038     }
30039     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
30040     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
30041     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
30042     if(Roo.isIE){
30043         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
30044     }
30045     if(this.tabPosition != "bottom"){
30046         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
30047          * @type Roo.Element
30048          */
30049         this.bodyEl = Roo.get(this.createBody(this.el.dom));
30050         this.el.addClass("x-tabs-top");
30051     }
30052     this.items = [];
30053
30054     this.bodyEl.setStyle("position", "relative");
30055
30056     this.active = null;
30057     this.activateDelegate = this.activate.createDelegate(this);
30058
30059     this.addEvents({
30060         /**
30061          * @event tabchange
30062          * Fires when the active tab changes
30063          * @param {Roo.TabPanel} this
30064          * @param {Roo.TabPanelItem} activePanel The new active tab
30065          */
30066         "tabchange": true,
30067         /**
30068          * @event beforetabchange
30069          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
30070          * @param {Roo.TabPanel} this
30071          * @param {Object} e Set cancel to true on this object to cancel the tab change
30072          * @param {Roo.TabPanelItem} tab The tab being changed to
30073          */
30074         "beforetabchange" : true
30075     });
30076
30077     Roo.EventManager.onWindowResize(this.onResize, this);
30078     this.cpad = this.el.getPadding("lr");
30079     this.hiddenCount = 0;
30080
30081
30082     // toolbar on the tabbar support...
30083     if (this.toolbar) {
30084         var tcfg = this.toolbar;
30085         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
30086         this.toolbar = new Roo.Toolbar(tcfg);
30087         if (Roo.isSafari) {
30088             var tbl = tcfg.container.child('table', true);
30089             tbl.setAttribute('width', '100%');
30090         }
30091         
30092     }
30093    
30094
30095
30096     Roo.TabPanel.superclass.constructor.call(this);
30097 };
30098
30099 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
30100     /*
30101      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
30102      */
30103     tabPosition : "top",
30104     /*
30105      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
30106      */
30107     currentTabWidth : 0,
30108     /*
30109      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
30110      */
30111     minTabWidth : 40,
30112     /*
30113      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
30114      */
30115     maxTabWidth : 250,
30116     /*
30117      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
30118      */
30119     preferredTabWidth : 175,
30120     /*
30121      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
30122      */
30123     resizeTabs : false,
30124     /*
30125      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
30126      */
30127     monitorResize : true,
30128     /*
30129      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
30130      */
30131     toolbar : false,
30132
30133     /**
30134      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
30135      * @param {String} id The id of the div to use <b>or create</b>
30136      * @param {String} text The text for the tab
30137      * @param {String} content (optional) Content to put in the TabPanelItem body
30138      * @param {Boolean} closable (optional) True to create a close icon on the tab
30139      * @return {Roo.TabPanelItem} The created TabPanelItem
30140      */
30141     addTab : function(id, text, content, closable){
30142         var item = new Roo.TabPanelItem(this, id, text, closable);
30143         this.addTabItem(item);
30144         if(content){
30145             item.setContent(content);
30146         }
30147         return item;
30148     },
30149
30150     /**
30151      * Returns the {@link Roo.TabPanelItem} with the specified id/index
30152      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
30153      * @return {Roo.TabPanelItem}
30154      */
30155     getTab : function(id){
30156         return this.items[id];
30157     },
30158
30159     /**
30160      * Hides the {@link Roo.TabPanelItem} with the specified id/index
30161      * @param {String/Number} id The id or index of the TabPanelItem to hide.
30162      */
30163     hideTab : function(id){
30164         var t = this.items[id];
30165         if(!t.isHidden()){
30166            t.setHidden(true);
30167            this.hiddenCount++;
30168            this.autoSizeTabs();
30169         }
30170     },
30171
30172     /**
30173      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
30174      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
30175      */
30176     unhideTab : function(id){
30177         var t = this.items[id];
30178         if(t.isHidden()){
30179            t.setHidden(false);
30180            this.hiddenCount--;
30181            this.autoSizeTabs();
30182         }
30183     },
30184
30185     /**
30186      * Adds an existing {@link Roo.TabPanelItem}.
30187      * @param {Roo.TabPanelItem} item The TabPanelItem to add
30188      */
30189     addTabItem : function(item){
30190         this.items[item.id] = item;
30191         this.items.push(item);
30192         if(this.resizeTabs){
30193            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
30194            this.autoSizeTabs();
30195         }else{
30196             item.autoSize();
30197         }
30198     },
30199
30200     /**
30201      * Removes a {@link Roo.TabPanelItem}.
30202      * @param {String/Number} id The id or index of the TabPanelItem to remove.
30203      */
30204     removeTab : function(id){
30205         var items = this.items;
30206         var tab = items[id];
30207         if(!tab) { return; }
30208         var index = items.indexOf(tab);
30209         if(this.active == tab && items.length > 1){
30210             var newTab = this.getNextAvailable(index);
30211             if(newTab) {
30212                 newTab.activate();
30213             }
30214         }
30215         this.stripEl.dom.removeChild(tab.pnode.dom);
30216         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30217             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30218         }
30219         items.splice(index, 1);
30220         delete this.items[tab.id];
30221         tab.fireEvent("close", tab);
30222         tab.purgeListeners();
30223         this.autoSizeTabs();
30224     },
30225
30226     getNextAvailable : function(start){
30227         var items = this.items;
30228         var index = start;
30229         // look for a next tab that will slide over to
30230         // replace the one being removed
30231         while(index < items.length){
30232             var item = items[++index];
30233             if(item && !item.isHidden()){
30234                 return item;
30235             }
30236         }
30237         // if one isn't found select the previous tab (on the left)
30238         index = start;
30239         while(index >= 0){
30240             var item = items[--index];
30241             if(item && !item.isHidden()){
30242                 return item;
30243             }
30244         }
30245         return null;
30246     },
30247
30248     /**
30249      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30250      * @param {String/Number} id The id or index of the TabPanelItem to disable.
30251      */
30252     disableTab : function(id){
30253         var tab = this.items[id];
30254         if(tab && this.active != tab){
30255             tab.disable();
30256         }
30257     },
30258
30259     /**
30260      * Enables a {@link Roo.TabPanelItem} that is disabled.
30261      * @param {String/Number} id The id or index of the TabPanelItem to enable.
30262      */
30263     enableTab : function(id){
30264         var tab = this.items[id];
30265         tab.enable();
30266     },
30267
30268     /**
30269      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30270      * @param {String/Number} id The id or index of the TabPanelItem to activate.
30271      * @return {Roo.TabPanelItem} The TabPanelItem.
30272      */
30273     activate : function(id){
30274         var tab = this.items[id];
30275         if(!tab){
30276             return null;
30277         }
30278         if(tab == this.active || tab.disabled){
30279             return tab;
30280         }
30281         var e = {};
30282         this.fireEvent("beforetabchange", this, e, tab);
30283         if(e.cancel !== true && !tab.disabled){
30284             if(this.active){
30285                 this.active.hide();
30286             }
30287             this.active = this.items[id];
30288             this.active.show();
30289             this.fireEvent("tabchange", this, this.active);
30290         }
30291         return tab;
30292     },
30293
30294     /**
30295      * Gets the active {@link Roo.TabPanelItem}.
30296      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30297      */
30298     getActiveTab : function(){
30299         return this.active;
30300     },
30301
30302     /**
30303      * Updates the tab body element to fit the height of the container element
30304      * for overflow scrolling
30305      * @param {Number} targetHeight (optional) Override the starting height from the elements height
30306      */
30307     syncHeight : function(targetHeight){
30308         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30309         var bm = this.bodyEl.getMargins();
30310         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30311         this.bodyEl.setHeight(newHeight);
30312         return newHeight;
30313     },
30314
30315     onResize : function(){
30316         if(this.monitorResize){
30317             this.autoSizeTabs();
30318         }
30319     },
30320
30321     /**
30322      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30323      */
30324     beginUpdate : function(){
30325         this.updating = true;
30326     },
30327
30328     /**
30329      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30330      */
30331     endUpdate : function(){
30332         this.updating = false;
30333         this.autoSizeTabs();
30334     },
30335
30336     /**
30337      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30338      */
30339     autoSizeTabs : function(){
30340         var count = this.items.length;
30341         var vcount = count - this.hiddenCount;
30342         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30343             return;
30344         }
30345         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30346         var availWidth = Math.floor(w / vcount);
30347         var b = this.stripBody;
30348         if(b.getWidth() > w){
30349             var tabs = this.items;
30350             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30351             if(availWidth < this.minTabWidth){
30352                 /*if(!this.sleft){    // incomplete scrolling code
30353                     this.createScrollButtons();
30354                 }
30355                 this.showScroll();
30356                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30357             }
30358         }else{
30359             if(this.currentTabWidth < this.preferredTabWidth){
30360                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30361             }
30362         }
30363     },
30364
30365     /**
30366      * Returns the number of tabs in this TabPanel.
30367      * @return {Number}
30368      */
30369      getCount : function(){
30370          return this.items.length;
30371      },
30372
30373     /**
30374      * Resizes all the tabs to the passed width
30375      * @param {Number} The new width
30376      */
30377     setTabWidth : function(width){
30378         this.currentTabWidth = width;
30379         for(var i = 0, len = this.items.length; i < len; i++) {
30380                 if(!this.items[i].isHidden()) {
30381                 this.items[i].setWidth(width);
30382             }
30383         }
30384     },
30385
30386     /**
30387      * Destroys this TabPanel
30388      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30389      */
30390     destroy : function(removeEl){
30391         Roo.EventManager.removeResizeListener(this.onResize, this);
30392         for(var i = 0, len = this.items.length; i < len; i++){
30393             this.items[i].purgeListeners();
30394         }
30395         if(removeEl === true){
30396             this.el.update("");
30397             this.el.remove();
30398         }
30399     }
30400 });
30401
30402 /**
30403  * @class Roo.TabPanelItem
30404  * @extends Roo.util.Observable
30405  * Represents an individual item (tab plus body) in a TabPanel.
30406  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30407  * @param {String} id The id of this TabPanelItem
30408  * @param {String} text The text for the tab of this TabPanelItem
30409  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30410  */
30411 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30412     /**
30413      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30414      * @type Roo.TabPanel
30415      */
30416     this.tabPanel = tabPanel;
30417     /**
30418      * The id for this TabPanelItem
30419      * @type String
30420      */
30421     this.id = id;
30422     /** @private */
30423     this.disabled = false;
30424     /** @private */
30425     this.text = text;
30426     /** @private */
30427     this.loaded = false;
30428     this.closable = closable;
30429
30430     /**
30431      * The body element for this TabPanelItem.
30432      * @type Roo.Element
30433      */
30434     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30435     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30436     this.bodyEl.setStyle("display", "block");
30437     this.bodyEl.setStyle("zoom", "1");
30438     this.hideAction();
30439
30440     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30441     /** @private */
30442     this.el = Roo.get(els.el, true);
30443     this.inner = Roo.get(els.inner, true);
30444     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30445     this.pnode = Roo.get(els.el.parentNode, true);
30446     this.el.on("mousedown", this.onTabMouseDown, this);
30447     this.el.on("click", this.onTabClick, this);
30448     /** @private */
30449     if(closable){
30450         var c = Roo.get(els.close, true);
30451         c.dom.title = this.closeText;
30452         c.addClassOnOver("close-over");
30453         c.on("click", this.closeClick, this);
30454      }
30455
30456     this.addEvents({
30457          /**
30458          * @event activate
30459          * Fires when this tab becomes the active tab.
30460          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30461          * @param {Roo.TabPanelItem} this
30462          */
30463         "activate": true,
30464         /**
30465          * @event beforeclose
30466          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30467          * @param {Roo.TabPanelItem} this
30468          * @param {Object} e Set cancel to true on this object to cancel the close.
30469          */
30470         "beforeclose": true,
30471         /**
30472          * @event close
30473          * Fires when this tab is closed.
30474          * @param {Roo.TabPanelItem} this
30475          */
30476          "close": true,
30477         /**
30478          * @event deactivate
30479          * Fires when this tab is no longer the active tab.
30480          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30481          * @param {Roo.TabPanelItem} this
30482          */
30483          "deactivate" : true
30484     });
30485     this.hidden = false;
30486
30487     Roo.TabPanelItem.superclass.constructor.call(this);
30488 };
30489
30490 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30491     purgeListeners : function(){
30492        Roo.util.Observable.prototype.purgeListeners.call(this);
30493        this.el.removeAllListeners();
30494     },
30495     /**
30496      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30497      */
30498     show : function(){
30499         this.pnode.addClass("on");
30500         this.showAction();
30501         if(Roo.isOpera){
30502             this.tabPanel.stripWrap.repaint();
30503         }
30504         this.fireEvent("activate", this.tabPanel, this);
30505     },
30506
30507     /**
30508      * Returns true if this tab is the active tab.
30509      * @return {Boolean}
30510      */
30511     isActive : function(){
30512         return this.tabPanel.getActiveTab() == this;
30513     },
30514
30515     /**
30516      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30517      */
30518     hide : function(){
30519         this.pnode.removeClass("on");
30520         this.hideAction();
30521         this.fireEvent("deactivate", this.tabPanel, this);
30522     },
30523
30524     hideAction : function(){
30525         this.bodyEl.hide();
30526         this.bodyEl.setStyle("position", "absolute");
30527         this.bodyEl.setLeft("-20000px");
30528         this.bodyEl.setTop("-20000px");
30529     },
30530
30531     showAction : function(){
30532         this.bodyEl.setStyle("position", "relative");
30533         this.bodyEl.setTop("");
30534         this.bodyEl.setLeft("");
30535         this.bodyEl.show();
30536     },
30537
30538     /**
30539      * Set the tooltip for the tab.
30540      * @param {String} tooltip The tab's tooltip
30541      */
30542     setTooltip : function(text){
30543         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30544             this.textEl.dom.qtip = text;
30545             this.textEl.dom.removeAttribute('title');
30546         }else{
30547             this.textEl.dom.title = text;
30548         }
30549     },
30550
30551     onTabClick : function(e){
30552         e.preventDefault();
30553         this.tabPanel.activate(this.id);
30554     },
30555
30556     onTabMouseDown : function(e){
30557         e.preventDefault();
30558         this.tabPanel.activate(this.id);
30559     },
30560
30561     getWidth : function(){
30562         return this.inner.getWidth();
30563     },
30564
30565     setWidth : function(width){
30566         var iwidth = width - this.pnode.getPadding("lr");
30567         this.inner.setWidth(iwidth);
30568         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30569         this.pnode.setWidth(width);
30570     },
30571
30572     /**
30573      * Show or hide the tab
30574      * @param {Boolean} hidden True to hide or false to show.
30575      */
30576     setHidden : function(hidden){
30577         this.hidden = hidden;
30578         this.pnode.setStyle("display", hidden ? "none" : "");
30579     },
30580
30581     /**
30582      * Returns true if this tab is "hidden"
30583      * @return {Boolean}
30584      */
30585     isHidden : function(){
30586         return this.hidden;
30587     },
30588
30589     /**
30590      * Returns the text for this tab
30591      * @return {String}
30592      */
30593     getText : function(){
30594         return this.text;
30595     },
30596
30597     autoSize : function(){
30598         //this.el.beginMeasure();
30599         this.textEl.setWidth(1);
30600         /*
30601          *  #2804 [new] Tabs in Roojs
30602          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30603          */
30604         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30605         //this.el.endMeasure();
30606     },
30607
30608     /**
30609      * Sets the text for the tab (Note: this also sets the tooltip text)
30610      * @param {String} text The tab's text and tooltip
30611      */
30612     setText : function(text){
30613         this.text = text;
30614         this.textEl.update(text);
30615         this.setTooltip(text);
30616         if(!this.tabPanel.resizeTabs){
30617             this.autoSize();
30618         }
30619     },
30620     /**
30621      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30622      */
30623     activate : function(){
30624         this.tabPanel.activate(this.id);
30625     },
30626
30627     /**
30628      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30629      */
30630     disable : function(){
30631         if(this.tabPanel.active != this){
30632             this.disabled = true;
30633             this.pnode.addClass("disabled");
30634         }
30635     },
30636
30637     /**
30638      * Enables this TabPanelItem if it was previously disabled.
30639      */
30640     enable : function(){
30641         this.disabled = false;
30642         this.pnode.removeClass("disabled");
30643     },
30644
30645     /**
30646      * Sets the content for this TabPanelItem.
30647      * @param {String} content The content
30648      * @param {Boolean} loadScripts true to look for and load scripts
30649      */
30650     setContent : function(content, loadScripts){
30651         this.bodyEl.update(content, loadScripts);
30652     },
30653
30654     /**
30655      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30656      * @return {Roo.UpdateManager} The UpdateManager
30657      */
30658     getUpdateManager : function(){
30659         return this.bodyEl.getUpdateManager();
30660     },
30661
30662     /**
30663      * Set a URL to be used to load the content for this TabPanelItem.
30664      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30665      * @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)
30666      * @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)
30667      * @return {Roo.UpdateManager} The UpdateManager
30668      */
30669     setUrl : function(url, params, loadOnce){
30670         if(this.refreshDelegate){
30671             this.un('activate', this.refreshDelegate);
30672         }
30673         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30674         this.on("activate", this.refreshDelegate);
30675         return this.bodyEl.getUpdateManager();
30676     },
30677
30678     /** @private */
30679     _handleRefresh : function(url, params, loadOnce){
30680         if(!loadOnce || !this.loaded){
30681             var updater = this.bodyEl.getUpdateManager();
30682             updater.update(url, params, this._setLoaded.createDelegate(this));
30683         }
30684     },
30685
30686     /**
30687      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30688      *   Will fail silently if the setUrl method has not been called.
30689      *   This does not activate the panel, just updates its content.
30690      */
30691     refresh : function(){
30692         if(this.refreshDelegate){
30693            this.loaded = false;
30694            this.refreshDelegate();
30695         }
30696     },
30697
30698     /** @private */
30699     _setLoaded : function(){
30700         this.loaded = true;
30701     },
30702
30703     /** @private */
30704     closeClick : function(e){
30705         var o = {};
30706         e.stopEvent();
30707         this.fireEvent("beforeclose", this, o);
30708         if(o.cancel !== true){
30709             this.tabPanel.removeTab(this.id);
30710         }
30711     },
30712     /**
30713      * The text displayed in the tooltip for the close icon.
30714      * @type String
30715      */
30716     closeText : "Close this tab"
30717 });
30718
30719 /** @private */
30720 Roo.TabPanel.prototype.createStrip = function(container){
30721     var strip = document.createElement("div");
30722     strip.className = "x-tabs-wrap";
30723     container.appendChild(strip);
30724     return strip;
30725 };
30726 /** @private */
30727 Roo.TabPanel.prototype.createStripList = function(strip){
30728     // div wrapper for retard IE
30729     // returns the "tr" element.
30730     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30731         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30732         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30733     return strip.firstChild.firstChild.firstChild.firstChild;
30734 };
30735 /** @private */
30736 Roo.TabPanel.prototype.createBody = function(container){
30737     var body = document.createElement("div");
30738     Roo.id(body, "tab-body");
30739     Roo.fly(body).addClass("x-tabs-body");
30740     container.appendChild(body);
30741     return body;
30742 };
30743 /** @private */
30744 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30745     var body = Roo.getDom(id);
30746     if(!body){
30747         body = document.createElement("div");
30748         body.id = id;
30749     }
30750     Roo.fly(body).addClass("x-tabs-item-body");
30751     bodyEl.insertBefore(body, bodyEl.firstChild);
30752     return body;
30753 };
30754 /** @private */
30755 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30756     var td = document.createElement("td");
30757     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30758     //stripEl.appendChild(td);
30759     if(closable){
30760         td.className = "x-tabs-closable";
30761         if(!this.closeTpl){
30762             this.closeTpl = new Roo.Template(
30763                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30764                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30765                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30766             );
30767         }
30768         var el = this.closeTpl.overwrite(td, {"text": text});
30769         var close = el.getElementsByTagName("div")[0];
30770         var inner = el.getElementsByTagName("em")[0];
30771         return {"el": el, "close": close, "inner": inner};
30772     } else {
30773         if(!this.tabTpl){
30774             this.tabTpl = new Roo.Template(
30775                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30776                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30777             );
30778         }
30779         var el = this.tabTpl.overwrite(td, {"text": text});
30780         var inner = el.getElementsByTagName("em")[0];
30781         return {"el": el, "inner": inner};
30782     }
30783 };/*
30784  * Based on:
30785  * Ext JS Library 1.1.1
30786  * Copyright(c) 2006-2007, Ext JS, LLC.
30787  *
30788  * Originally Released Under LGPL - original licence link has changed is not relivant.
30789  *
30790  * Fork - LGPL
30791  * <script type="text/javascript">
30792  */
30793
30794 /**
30795  * @class Roo.Button
30796  * @extends Roo.util.Observable
30797  * Simple Button class
30798  * @cfg {String} text The button text
30799  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30800  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30801  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30802  * @cfg {Object} scope The scope of the handler
30803  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30804  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30805  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30806  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30807  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30808  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30809    applies if enableToggle = true)
30810  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30811  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30812   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30813  * @constructor
30814  * Create a new button
30815  * @param {Object} config The config object
30816  */
30817 Roo.Button = function(renderTo, config)
30818 {
30819     if (!config) {
30820         config = renderTo;
30821         renderTo = config.renderTo || false;
30822     }
30823     
30824     Roo.apply(this, config);
30825     this.addEvents({
30826         /**
30827              * @event click
30828              * Fires when this button is clicked
30829              * @param {Button} this
30830              * @param {EventObject} e The click event
30831              */
30832             "click" : true,
30833         /**
30834              * @event toggle
30835              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30836              * @param {Button} this
30837              * @param {Boolean} pressed
30838              */
30839             "toggle" : true,
30840         /**
30841              * @event mouseover
30842              * Fires when the mouse hovers over the button
30843              * @param {Button} this
30844              * @param {Event} e The event object
30845              */
30846         'mouseover' : true,
30847         /**
30848              * @event mouseout
30849              * Fires when the mouse exits the button
30850              * @param {Button} this
30851              * @param {Event} e The event object
30852              */
30853         'mouseout': true,
30854          /**
30855              * @event render
30856              * Fires when the button is rendered
30857              * @param {Button} this
30858              */
30859         'render': true
30860     });
30861     if(this.menu){
30862         this.menu = Roo.menu.MenuMgr.get(this.menu);
30863     }
30864     // register listeners first!!  - so render can be captured..
30865     Roo.util.Observable.call(this);
30866     if(renderTo){
30867         this.render(renderTo);
30868     }
30869     
30870   
30871 };
30872
30873 Roo.extend(Roo.Button, Roo.util.Observable, {
30874     /**
30875      * 
30876      */
30877     
30878     /**
30879      * Read-only. True if this button is hidden
30880      * @type Boolean
30881      */
30882     hidden : false,
30883     /**
30884      * Read-only. True if this button is disabled
30885      * @type Boolean
30886      */
30887     disabled : false,
30888     /**
30889      * Read-only. True if this button is pressed (only if enableToggle = true)
30890      * @type Boolean
30891      */
30892     pressed : false,
30893
30894     /**
30895      * @cfg {Number} tabIndex 
30896      * The DOM tabIndex for this button (defaults to undefined)
30897      */
30898     tabIndex : undefined,
30899
30900     /**
30901      * @cfg {Boolean} enableToggle
30902      * True to enable pressed/not pressed toggling (defaults to false)
30903      */
30904     enableToggle: false,
30905     /**
30906      * @cfg {Roo.menu.Menu} menu
30907      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30908      */
30909     menu : undefined,
30910     /**
30911      * @cfg {String} menuAlign
30912      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30913      */
30914     menuAlign : "tl-bl?",
30915
30916     /**
30917      * @cfg {String} iconCls
30918      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30919      */
30920     iconCls : undefined,
30921     /**
30922      * @cfg {String} type
30923      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30924      */
30925     type : 'button',
30926
30927     // private
30928     menuClassTarget: 'tr',
30929
30930     /**
30931      * @cfg {String} clickEvent
30932      * The type of event to map to the button's event handler (defaults to 'click')
30933      */
30934     clickEvent : 'click',
30935
30936     /**
30937      * @cfg {Boolean} handleMouseEvents
30938      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30939      */
30940     handleMouseEvents : true,
30941
30942     /**
30943      * @cfg {String} tooltipType
30944      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30945      */
30946     tooltipType : 'qtip',
30947
30948     /**
30949      * @cfg {String} cls
30950      * A CSS class to apply to the button's main element.
30951      */
30952     
30953     /**
30954      * @cfg {Roo.Template} template (Optional)
30955      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30956      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30957      * require code modifications if required elements (e.g. a button) aren't present.
30958      */
30959
30960     // private
30961     render : function(renderTo){
30962         var btn;
30963         if(this.hideParent){
30964             this.parentEl = Roo.get(renderTo);
30965         }
30966         if(!this.dhconfig){
30967             if(!this.template){
30968                 if(!Roo.Button.buttonTemplate){
30969                     // hideous table template
30970                     Roo.Button.buttonTemplate = new Roo.Template(
30971                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30972                         '<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>',
30973                         "</tr></tbody></table>");
30974                 }
30975                 this.template = Roo.Button.buttonTemplate;
30976             }
30977             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30978             var btnEl = btn.child("button:first");
30979             btnEl.on('focus', this.onFocus, this);
30980             btnEl.on('blur', this.onBlur, this);
30981             if(this.cls){
30982                 btn.addClass(this.cls);
30983             }
30984             if(this.icon){
30985                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30986             }
30987             if(this.iconCls){
30988                 btnEl.addClass(this.iconCls);
30989                 if(!this.cls){
30990                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30991                 }
30992             }
30993             if(this.tabIndex !== undefined){
30994                 btnEl.dom.tabIndex = this.tabIndex;
30995             }
30996             if(this.tooltip){
30997                 if(typeof this.tooltip == 'object'){
30998                     Roo.QuickTips.tips(Roo.apply({
30999                           target: btnEl.id
31000                     }, this.tooltip));
31001                 } else {
31002                     btnEl.dom[this.tooltipType] = this.tooltip;
31003                 }
31004             }
31005         }else{
31006             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
31007         }
31008         this.el = btn;
31009         if(this.id){
31010             this.el.dom.id = this.el.id = this.id;
31011         }
31012         if(this.menu){
31013             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
31014             this.menu.on("show", this.onMenuShow, this);
31015             this.menu.on("hide", this.onMenuHide, this);
31016         }
31017         btn.addClass("x-btn");
31018         if(Roo.isIE && !Roo.isIE7){
31019             this.autoWidth.defer(1, this);
31020         }else{
31021             this.autoWidth();
31022         }
31023         if(this.handleMouseEvents){
31024             btn.on("mouseover", this.onMouseOver, this);
31025             btn.on("mouseout", this.onMouseOut, this);
31026             btn.on("mousedown", this.onMouseDown, this);
31027         }
31028         btn.on(this.clickEvent, this.onClick, this);
31029         //btn.on("mouseup", this.onMouseUp, this);
31030         if(this.hidden){
31031             this.hide();
31032         }
31033         if(this.disabled){
31034             this.disable();
31035         }
31036         Roo.ButtonToggleMgr.register(this);
31037         if(this.pressed){
31038             this.el.addClass("x-btn-pressed");
31039         }
31040         if(this.repeat){
31041             var repeater = new Roo.util.ClickRepeater(btn,
31042                 typeof this.repeat == "object" ? this.repeat : {}
31043             );
31044             repeater.on("click", this.onClick,  this);
31045         }
31046         
31047         this.fireEvent('render', this);
31048         
31049     },
31050     /**
31051      * Returns the button's underlying element
31052      * @return {Roo.Element} The element
31053      */
31054     getEl : function(){
31055         return this.el;  
31056     },
31057     
31058     /**
31059      * Destroys this Button and removes any listeners.
31060      */
31061     destroy : function(){
31062         Roo.ButtonToggleMgr.unregister(this);
31063         this.el.removeAllListeners();
31064         this.purgeListeners();
31065         this.el.remove();
31066     },
31067
31068     // private
31069     autoWidth : function(){
31070         if(this.el){
31071             this.el.setWidth("auto");
31072             if(Roo.isIE7 && Roo.isStrict){
31073                 var ib = this.el.child('button');
31074                 if(ib && ib.getWidth() > 20){
31075                     ib.clip();
31076                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31077                 }
31078             }
31079             if(this.minWidth){
31080                 if(this.hidden){
31081                     this.el.beginMeasure();
31082                 }
31083                 if(this.el.getWidth() < this.minWidth){
31084                     this.el.setWidth(this.minWidth);
31085                 }
31086                 if(this.hidden){
31087                     this.el.endMeasure();
31088                 }
31089             }
31090         }
31091     },
31092
31093     /**
31094      * Assigns this button's click handler
31095      * @param {Function} handler The function to call when the button is clicked
31096      * @param {Object} scope (optional) Scope for the function passed in
31097      */
31098     setHandler : function(handler, scope){
31099         this.handler = handler;
31100         this.scope = scope;  
31101     },
31102     
31103     /**
31104      * Sets this button's text
31105      * @param {String} text The button text
31106      */
31107     setText : function(text){
31108         this.text = text;
31109         if(this.el){
31110             this.el.child("td.x-btn-center button.x-btn-text").update(text);
31111         }
31112         this.autoWidth();
31113     },
31114     
31115     /**
31116      * Gets the text for this button
31117      * @return {String} The button text
31118      */
31119     getText : function(){
31120         return this.text;  
31121     },
31122     
31123     /**
31124      * Show this button
31125      */
31126     show: function(){
31127         this.hidden = false;
31128         if(this.el){
31129             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
31130         }
31131     },
31132     
31133     /**
31134      * Hide this button
31135      */
31136     hide: function(){
31137         this.hidden = true;
31138         if(this.el){
31139             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
31140         }
31141     },
31142     
31143     /**
31144      * Convenience function for boolean show/hide
31145      * @param {Boolean} visible True to show, false to hide
31146      */
31147     setVisible: function(visible){
31148         if(visible) {
31149             this.show();
31150         }else{
31151             this.hide();
31152         }
31153     },
31154     /**
31155          * Similar to toggle, but does not trigger event.
31156          * @param {Boolean} state [required] Force a particular state
31157          */
31158         setPressed : function(state)
31159         {
31160             if(state != this.pressed){
31161             if(state){
31162                 this.el.addClass("x-btn-pressed");
31163                 this.pressed = true;
31164             }else{
31165                 this.el.removeClass("x-btn-pressed");
31166                 this.pressed = false;
31167             }
31168         }
31169         },
31170         
31171     /**
31172      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
31173      * @param {Boolean} state (optional) Force a particular state
31174      */
31175     toggle : function(state){
31176         state = state === undefined ? !this.pressed : state;
31177         if(state != this.pressed){
31178             if(state){
31179                 this.el.addClass("x-btn-pressed");
31180                 this.pressed = true;
31181                 this.fireEvent("toggle", this, true);
31182             }else{
31183                 this.el.removeClass("x-btn-pressed");
31184                 this.pressed = false;
31185                 this.fireEvent("toggle", this, false);
31186             }
31187             if(this.toggleHandler){
31188                 this.toggleHandler.call(this.scope || this, this, state);
31189             }
31190         }
31191     },
31192     
31193         
31194         
31195     /**
31196      * Focus the button
31197      */
31198     focus : function(){
31199         this.el.child('button:first').focus();
31200     },
31201     
31202     /**
31203      * Disable this button
31204      */
31205     disable : function(){
31206         if(this.el){
31207             this.el.addClass("x-btn-disabled");
31208         }
31209         this.disabled = true;
31210     },
31211     
31212     /**
31213      * Enable this button
31214      */
31215     enable : function(){
31216         if(this.el){
31217             this.el.removeClass("x-btn-disabled");
31218         }
31219         this.disabled = false;
31220     },
31221
31222     /**
31223      * Convenience function for boolean enable/disable
31224      * @param {Boolean} enabled True to enable, false to disable
31225      */
31226     setDisabled : function(v){
31227         this[v !== true ? "enable" : "disable"]();
31228     },
31229
31230     // private
31231     onClick : function(e)
31232     {
31233         if(e){
31234             e.preventDefault();
31235         }
31236         if(e.button != 0){
31237             return;
31238         }
31239         if(!this.disabled){
31240             if(this.enableToggle){
31241                 this.toggle();
31242             }
31243             if(this.menu && !this.menu.isVisible()){
31244                 this.menu.show(this.el, this.menuAlign);
31245             }
31246             this.fireEvent("click", this, e);
31247             if(this.handler){
31248                 this.el.removeClass("x-btn-over");
31249                 this.handler.call(this.scope || this, this, e);
31250             }
31251         }
31252     },
31253     // private
31254     onMouseOver : function(e){
31255         if(!this.disabled){
31256             this.el.addClass("x-btn-over");
31257             this.fireEvent('mouseover', this, e);
31258         }
31259     },
31260     // private
31261     onMouseOut : function(e){
31262         if(!e.within(this.el,  true)){
31263             this.el.removeClass("x-btn-over");
31264             this.fireEvent('mouseout', this, e);
31265         }
31266     },
31267     // private
31268     onFocus : function(e){
31269         if(!this.disabled){
31270             this.el.addClass("x-btn-focus");
31271         }
31272     },
31273     // private
31274     onBlur : function(e){
31275         this.el.removeClass("x-btn-focus");
31276     },
31277     // private
31278     onMouseDown : function(e){
31279         if(!this.disabled && e.button == 0){
31280             this.el.addClass("x-btn-click");
31281             Roo.get(document).on('mouseup', this.onMouseUp, this);
31282         }
31283     },
31284     // private
31285     onMouseUp : function(e){
31286         if(e.button == 0){
31287             this.el.removeClass("x-btn-click");
31288             Roo.get(document).un('mouseup', this.onMouseUp, this);
31289         }
31290     },
31291     // private
31292     onMenuShow : function(e){
31293         this.el.addClass("x-btn-menu-active");
31294     },
31295     // private
31296     onMenuHide : function(e){
31297         this.el.removeClass("x-btn-menu-active");
31298     }   
31299 });
31300
31301 // Private utility class used by Button
31302 Roo.ButtonToggleMgr = function(){
31303    var groups = {};
31304    
31305    function toggleGroup(btn, state){
31306        if(state){
31307            var g = groups[btn.toggleGroup];
31308            for(var i = 0, l = g.length; i < l; i++){
31309                if(g[i] != btn){
31310                    g[i].toggle(false);
31311                }
31312            }
31313        }
31314    }
31315    
31316    return {
31317        register : function(btn){
31318            if(!btn.toggleGroup){
31319                return;
31320            }
31321            var g = groups[btn.toggleGroup];
31322            if(!g){
31323                g = groups[btn.toggleGroup] = [];
31324            }
31325            g.push(btn);
31326            btn.on("toggle", toggleGroup);
31327        },
31328        
31329        unregister : function(btn){
31330            if(!btn.toggleGroup){
31331                return;
31332            }
31333            var g = groups[btn.toggleGroup];
31334            if(g){
31335                g.remove(btn);
31336                btn.un("toggle", toggleGroup);
31337            }
31338        }
31339    };
31340 }();/*
31341  * Based on:
31342  * Ext JS Library 1.1.1
31343  * Copyright(c) 2006-2007, Ext JS, LLC.
31344  *
31345  * Originally Released Under LGPL - original licence link has changed is not relivant.
31346  *
31347  * Fork - LGPL
31348  * <script type="text/javascript">
31349  */
31350  
31351 /**
31352  * @class Roo.SplitButton
31353  * @extends Roo.Button
31354  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31355  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31356  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31357  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31358  * @cfg {String} arrowTooltip The title attribute of the arrow
31359  * @constructor
31360  * Create a new menu button
31361  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31362  * @param {Object} config The config object
31363  */
31364 Roo.SplitButton = function(renderTo, config){
31365     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31366     /**
31367      * @event arrowclick
31368      * Fires when this button's arrow is clicked
31369      * @param {SplitButton} this
31370      * @param {EventObject} e The click event
31371      */
31372     this.addEvents({"arrowclick":true});
31373 };
31374
31375 Roo.extend(Roo.SplitButton, Roo.Button, {
31376     render : function(renderTo){
31377         // this is one sweet looking template!
31378         var tpl = new Roo.Template(
31379             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31380             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31381             '<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>',
31382             "</tbody></table></td><td>",
31383             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31384             '<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>',
31385             "</tbody></table></td></tr></table>"
31386         );
31387         var btn = tpl.append(renderTo, [this.text, this.type], true);
31388         var btnEl = btn.child("button");
31389         if(this.cls){
31390             btn.addClass(this.cls);
31391         }
31392         if(this.icon){
31393             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31394         }
31395         if(this.iconCls){
31396             btnEl.addClass(this.iconCls);
31397             if(!this.cls){
31398                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31399             }
31400         }
31401         this.el = btn;
31402         if(this.handleMouseEvents){
31403             btn.on("mouseover", this.onMouseOver, this);
31404             btn.on("mouseout", this.onMouseOut, this);
31405             btn.on("mousedown", this.onMouseDown, this);
31406             btn.on("mouseup", this.onMouseUp, this);
31407         }
31408         btn.on(this.clickEvent, this.onClick, this);
31409         if(this.tooltip){
31410             if(typeof this.tooltip == 'object'){
31411                 Roo.QuickTips.tips(Roo.apply({
31412                       target: btnEl.id
31413                 }, this.tooltip));
31414             } else {
31415                 btnEl.dom[this.tooltipType] = this.tooltip;
31416             }
31417         }
31418         if(this.arrowTooltip){
31419             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31420         }
31421         if(this.hidden){
31422             this.hide();
31423         }
31424         if(this.disabled){
31425             this.disable();
31426         }
31427         if(this.pressed){
31428             this.el.addClass("x-btn-pressed");
31429         }
31430         if(Roo.isIE && !Roo.isIE7){
31431             this.autoWidth.defer(1, this);
31432         }else{
31433             this.autoWidth();
31434         }
31435         if(this.menu){
31436             this.menu.on("show", this.onMenuShow, this);
31437             this.menu.on("hide", this.onMenuHide, this);
31438         }
31439         this.fireEvent('render', this);
31440     },
31441
31442     // private
31443     autoWidth : function(){
31444         if(this.el){
31445             var tbl = this.el.child("table:first");
31446             var tbl2 = this.el.child("table:last");
31447             this.el.setWidth("auto");
31448             tbl.setWidth("auto");
31449             if(Roo.isIE7 && Roo.isStrict){
31450                 var ib = this.el.child('button:first');
31451                 if(ib && ib.getWidth() > 20){
31452                     ib.clip();
31453                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31454                 }
31455             }
31456             if(this.minWidth){
31457                 if(this.hidden){
31458                     this.el.beginMeasure();
31459                 }
31460                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31461                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31462                 }
31463                 if(this.hidden){
31464                     this.el.endMeasure();
31465                 }
31466             }
31467             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31468         } 
31469     },
31470     /**
31471      * Sets this button's click handler
31472      * @param {Function} handler The function to call when the button is clicked
31473      * @param {Object} scope (optional) Scope for the function passed above
31474      */
31475     setHandler : function(handler, scope){
31476         this.handler = handler;
31477         this.scope = scope;  
31478     },
31479     
31480     /**
31481      * Sets this button's arrow click handler
31482      * @param {Function} handler The function to call when the arrow is clicked
31483      * @param {Object} scope (optional) Scope for the function passed above
31484      */
31485     setArrowHandler : function(handler, scope){
31486         this.arrowHandler = handler;
31487         this.scope = scope;  
31488     },
31489     
31490     /**
31491      * Focus the button
31492      */
31493     focus : function(){
31494         if(this.el){
31495             this.el.child("button:first").focus();
31496         }
31497     },
31498
31499     // private
31500     onClick : function(e){
31501         e.preventDefault();
31502         if(!this.disabled){
31503             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31504                 if(this.menu && !this.menu.isVisible()){
31505                     this.menu.show(this.el, this.menuAlign);
31506                 }
31507                 this.fireEvent("arrowclick", this, e);
31508                 if(this.arrowHandler){
31509                     this.arrowHandler.call(this.scope || this, this, e);
31510                 }
31511             }else{
31512                 this.fireEvent("click", this, e);
31513                 if(this.handler){
31514                     this.handler.call(this.scope || this, this, e);
31515                 }
31516             }
31517         }
31518     },
31519     // private
31520     onMouseDown : function(e){
31521         if(!this.disabled){
31522             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31523         }
31524     },
31525     // private
31526     onMouseUp : function(e){
31527         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31528     }   
31529 });
31530
31531
31532 // backwards compat
31533 Roo.MenuButton = Roo.SplitButton;/*
31534  * Based on:
31535  * Ext JS Library 1.1.1
31536  * Copyright(c) 2006-2007, Ext JS, LLC.
31537  *
31538  * Originally Released Under LGPL - original licence link has changed is not relivant.
31539  *
31540  * Fork - LGPL
31541  * <script type="text/javascript">
31542  */
31543
31544 /**
31545  * @class Roo.Toolbar
31546  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31547  * Basic Toolbar class.
31548  * @constructor
31549  * Creates a new Toolbar
31550  * @param {Object} container The config object
31551  */ 
31552 Roo.Toolbar = function(container, buttons, config)
31553 {
31554     /// old consturctor format still supported..
31555     if(container instanceof Array){ // omit the container for later rendering
31556         buttons = container;
31557         config = buttons;
31558         container = null;
31559     }
31560     if (typeof(container) == 'object' && container.xtype) {
31561         config = container;
31562         container = config.container;
31563         buttons = config.buttons || []; // not really - use items!!
31564     }
31565     var xitems = [];
31566     if (config && config.items) {
31567         xitems = config.items;
31568         delete config.items;
31569     }
31570     Roo.apply(this, config);
31571     this.buttons = buttons;
31572     
31573     if(container){
31574         this.render(container);
31575     }
31576     this.xitems = xitems;
31577     Roo.each(xitems, function(b) {
31578         this.add(b);
31579     }, this);
31580     
31581 };
31582
31583 Roo.Toolbar.prototype = {
31584     /**
31585      * @cfg {Array} items
31586      * array of button configs or elements to add (will be converted to a MixedCollection)
31587      */
31588     items: false,
31589     /**
31590      * @cfg {String/HTMLElement/Element} container
31591      * The id or element that will contain the toolbar
31592      */
31593     // private
31594     render : function(ct){
31595         this.el = Roo.get(ct);
31596         if(this.cls){
31597             this.el.addClass(this.cls);
31598         }
31599         // using a table allows for vertical alignment
31600         // 100% width is needed by Safari...
31601         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31602         this.tr = this.el.child("tr", true);
31603         var autoId = 0;
31604         this.items = new Roo.util.MixedCollection(false, function(o){
31605             return o.id || ("item" + (++autoId));
31606         });
31607         if(this.buttons){
31608             this.add.apply(this, this.buttons);
31609             delete this.buttons;
31610         }
31611     },
31612
31613     /**
31614      * Adds element(s) to the toolbar -- this function takes a variable number of 
31615      * arguments of mixed type and adds them to the toolbar.
31616      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31617      * <ul>
31618      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31619      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31620      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31621      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31622      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31623      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31624      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31625      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31626      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31627      * </ul>
31628      * @param {Mixed} arg2
31629      * @param {Mixed} etc.
31630      */
31631     add : function(){
31632         var a = arguments, l = a.length;
31633         for(var i = 0; i < l; i++){
31634             this._add(a[i]);
31635         }
31636     },
31637     // private..
31638     _add : function(el) {
31639         
31640         if (el.xtype) {
31641             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31642         }
31643         
31644         if (el.applyTo){ // some kind of form field
31645             return this.addField(el);
31646         } 
31647         if (el.render){ // some kind of Toolbar.Item
31648             return this.addItem(el);
31649         }
31650         if (typeof el == "string"){ // string
31651             if(el == "separator" || el == "-"){
31652                 return this.addSeparator();
31653             }
31654             if (el == " "){
31655                 return this.addSpacer();
31656             }
31657             if(el == "->"){
31658                 return this.addFill();
31659             }
31660             return this.addText(el);
31661             
31662         }
31663         if(el.tagName){ // element
31664             return this.addElement(el);
31665         }
31666         if(typeof el == "object"){ // must be button config?
31667             return this.addButton(el);
31668         }
31669         // and now what?!?!
31670         return false;
31671         
31672     },
31673     
31674     /**
31675      * Add an Xtype element
31676      * @param {Object} xtype Xtype Object
31677      * @return {Object} created Object
31678      */
31679     addxtype : function(e){
31680         return this.add(e);  
31681     },
31682     
31683     /**
31684      * Returns the Element for this toolbar.
31685      * @return {Roo.Element}
31686      */
31687     getEl : function(){
31688         return this.el;  
31689     },
31690     
31691     /**
31692      * Adds a separator
31693      * @return {Roo.Toolbar.Item} The separator item
31694      */
31695     addSeparator : function(){
31696         return this.addItem(new Roo.Toolbar.Separator());
31697     },
31698
31699     /**
31700      * Adds a spacer element
31701      * @return {Roo.Toolbar.Spacer} The spacer item
31702      */
31703     addSpacer : function(){
31704         return this.addItem(new Roo.Toolbar.Spacer());
31705     },
31706
31707     /**
31708      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31709      * @return {Roo.Toolbar.Fill} The fill item
31710      */
31711     addFill : function(){
31712         return this.addItem(new Roo.Toolbar.Fill());
31713     },
31714
31715     /**
31716      * Adds any standard HTML element to the toolbar
31717      * @param {String/HTMLElement/Element} el The element or id of the element to add
31718      * @return {Roo.Toolbar.Item} The element's item
31719      */
31720     addElement : function(el){
31721         return this.addItem(new Roo.Toolbar.Item(el));
31722     },
31723     /**
31724      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31725      * @type Roo.util.MixedCollection  
31726      */
31727     items : false,
31728      
31729     /**
31730      * Adds any Toolbar.Item or subclass
31731      * @param {Roo.Toolbar.Item} item
31732      * @return {Roo.Toolbar.Item} The item
31733      */
31734     addItem : function(item){
31735         var td = this.nextBlock();
31736         item.render(td);
31737         this.items.add(item);
31738         return item;
31739     },
31740     
31741     /**
31742      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31743      * @param {Object/Array} config A button config or array of configs
31744      * @return {Roo.Toolbar.Button/Array}
31745      */
31746     addButton : function(config){
31747         if(config instanceof Array){
31748             var buttons = [];
31749             for(var i = 0, len = config.length; i < len; i++) {
31750                 buttons.push(this.addButton(config[i]));
31751             }
31752             return buttons;
31753         }
31754         var b = config;
31755         if(!(config instanceof Roo.Toolbar.Button)){
31756             b = config.split ?
31757                 new Roo.Toolbar.SplitButton(config) :
31758                 new Roo.Toolbar.Button(config);
31759         }
31760         var td = this.nextBlock();
31761         b.render(td);
31762         this.items.add(b);
31763         return b;
31764     },
31765     
31766     /**
31767      * Adds text to the toolbar
31768      * @param {String} text The text to add
31769      * @return {Roo.Toolbar.Item} The element's item
31770      */
31771     addText : function(text){
31772         return this.addItem(new Roo.Toolbar.TextItem(text));
31773     },
31774     
31775     /**
31776      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31777      * @param {Number} index The index where the item is to be inserted
31778      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31779      * @return {Roo.Toolbar.Button/Item}
31780      */
31781     insertButton : function(index, item){
31782         if(item instanceof Array){
31783             var buttons = [];
31784             for(var i = 0, len = item.length; i < len; i++) {
31785                buttons.push(this.insertButton(index + i, item[i]));
31786             }
31787             return buttons;
31788         }
31789         if (!(item instanceof Roo.Toolbar.Button)){
31790            item = new Roo.Toolbar.Button(item);
31791         }
31792         var td = document.createElement("td");
31793         this.tr.insertBefore(td, this.tr.childNodes[index]);
31794         item.render(td);
31795         this.items.insert(index, item);
31796         return item;
31797     },
31798     
31799     /**
31800      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31801      * @param {Object} config
31802      * @return {Roo.Toolbar.Item} The element's item
31803      */
31804     addDom : function(config, returnEl){
31805         var td = this.nextBlock();
31806         Roo.DomHelper.overwrite(td, config);
31807         var ti = new Roo.Toolbar.Item(td.firstChild);
31808         ti.render(td);
31809         this.items.add(ti);
31810         return ti;
31811     },
31812
31813     /**
31814      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31815      * @type Roo.util.MixedCollection  
31816      */
31817     fields : false,
31818     
31819     /**
31820      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31821      * Note: the field should not have been rendered yet. For a field that has already been
31822      * rendered, use {@link #addElement}.
31823      * @param {Roo.form.Field} field
31824      * @return {Roo.ToolbarItem}
31825      */
31826      
31827       
31828     addField : function(field) {
31829         if (!this.fields) {
31830             var autoId = 0;
31831             this.fields = new Roo.util.MixedCollection(false, function(o){
31832                 return o.id || ("item" + (++autoId));
31833             });
31834
31835         }
31836         
31837         var td = this.nextBlock();
31838         field.render(td);
31839         var ti = new Roo.Toolbar.Item(td.firstChild);
31840         ti.render(td);
31841         this.items.add(ti);
31842         this.fields.add(field);
31843         return ti;
31844     },
31845     /**
31846      * Hide the toolbar
31847      * @method hide
31848      */
31849      
31850       
31851     hide : function()
31852     {
31853         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31854         this.el.child('div').hide();
31855     },
31856     /**
31857      * Show the toolbar
31858      * @method show
31859      */
31860     show : function()
31861     {
31862         this.el.child('div').show();
31863     },
31864       
31865     // private
31866     nextBlock : function(){
31867         var td = document.createElement("td");
31868         this.tr.appendChild(td);
31869         return td;
31870     },
31871
31872     // private
31873     destroy : function(){
31874         if(this.items){ // rendered?
31875             Roo.destroy.apply(Roo, this.items.items);
31876         }
31877         if(this.fields){ // rendered?
31878             Roo.destroy.apply(Roo, this.fields.items);
31879         }
31880         Roo.Element.uncache(this.el, this.tr);
31881     }
31882 };
31883
31884 /**
31885  * @class Roo.Toolbar.Item
31886  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31887  * @constructor
31888  * Creates a new Item
31889  * @param {HTMLElement} el 
31890  */
31891 Roo.Toolbar.Item = function(el){
31892     var cfg = {};
31893     if (typeof (el.xtype) != 'undefined') {
31894         cfg = el;
31895         el = cfg.el;
31896     }
31897     
31898     this.el = Roo.getDom(el);
31899     this.id = Roo.id(this.el);
31900     this.hidden = false;
31901     
31902     this.addEvents({
31903          /**
31904              * @event render
31905              * Fires when the button is rendered
31906              * @param {Button} this
31907              */
31908         'render': true
31909     });
31910     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31911 };
31912 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31913 //Roo.Toolbar.Item.prototype = {
31914     
31915     /**
31916      * Get this item's HTML Element
31917      * @return {HTMLElement}
31918      */
31919     getEl : function(){
31920        return this.el;  
31921     },
31922
31923     // private
31924     render : function(td){
31925         
31926          this.td = td;
31927         td.appendChild(this.el);
31928         
31929         this.fireEvent('render', this);
31930     },
31931     
31932     /**
31933      * Removes and destroys this item.
31934      */
31935     destroy : function(){
31936         this.td.parentNode.removeChild(this.td);
31937     },
31938     
31939     /**
31940      * Shows this item.
31941      */
31942     show: function(){
31943         this.hidden = false;
31944         this.td.style.display = "";
31945     },
31946     
31947     /**
31948      * Hides this item.
31949      */
31950     hide: function(){
31951         this.hidden = true;
31952         this.td.style.display = "none";
31953     },
31954     
31955     /**
31956      * Convenience function for boolean show/hide.
31957      * @param {Boolean} visible true to show/false to hide
31958      */
31959     setVisible: function(visible){
31960         if(visible) {
31961             this.show();
31962         }else{
31963             this.hide();
31964         }
31965     },
31966     
31967     /**
31968      * Try to focus this item.
31969      */
31970     focus : function(){
31971         Roo.fly(this.el).focus();
31972     },
31973     
31974     /**
31975      * Disables this item.
31976      */
31977     disable : function(){
31978         Roo.fly(this.td).addClass("x-item-disabled");
31979         this.disabled = true;
31980         this.el.disabled = true;
31981     },
31982     
31983     /**
31984      * Enables this item.
31985      */
31986     enable : function(){
31987         Roo.fly(this.td).removeClass("x-item-disabled");
31988         this.disabled = false;
31989         this.el.disabled = false;
31990     }
31991 });
31992
31993
31994 /**
31995  * @class Roo.Toolbar.Separator
31996  * @extends Roo.Toolbar.Item
31997  * A simple toolbar separator class
31998  * @constructor
31999  * Creates a new Separator
32000  */
32001 Roo.Toolbar.Separator = function(cfg){
32002     
32003     var s = document.createElement("span");
32004     s.className = "ytb-sep";
32005     if (cfg) {
32006         cfg.el = s;
32007     }
32008     
32009     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
32010 };
32011 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
32012     enable:Roo.emptyFn,
32013     disable:Roo.emptyFn,
32014     focus:Roo.emptyFn
32015 });
32016
32017 /**
32018  * @class Roo.Toolbar.Spacer
32019  * @extends Roo.Toolbar.Item
32020  * A simple element that adds extra horizontal space to a toolbar.
32021  * @constructor
32022  * Creates a new Spacer
32023  */
32024 Roo.Toolbar.Spacer = function(cfg){
32025     var s = document.createElement("div");
32026     s.className = "ytb-spacer";
32027     if (cfg) {
32028         cfg.el = s;
32029     }
32030     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
32031 };
32032 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
32033     enable:Roo.emptyFn,
32034     disable:Roo.emptyFn,
32035     focus:Roo.emptyFn
32036 });
32037
32038 /**
32039  * @class Roo.Toolbar.Fill
32040  * @extends Roo.Toolbar.Spacer
32041  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
32042  * @constructor
32043  * Creates a new Spacer
32044  */
32045 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
32046     // private
32047     render : function(td){
32048         td.style.width = '100%';
32049         Roo.Toolbar.Fill.superclass.render.call(this, td);
32050     }
32051 });
32052
32053 /**
32054  * @class Roo.Toolbar.TextItem
32055  * @extends Roo.Toolbar.Item
32056  * A simple class that renders text directly into a toolbar.
32057  * @constructor
32058  * Creates a new TextItem
32059  * @cfg {string} text 
32060  */
32061 Roo.Toolbar.TextItem = function(cfg){
32062     var  text = cfg || "";
32063     if (typeof(cfg) == 'object') {
32064         text = cfg.text || "";
32065     }  else {
32066         cfg = null;
32067     }
32068     var s = document.createElement("span");
32069     s.className = "ytb-text";
32070     s.innerHTML = text;
32071     if (cfg) {
32072         cfg.el  = s;
32073     }
32074     
32075     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
32076 };
32077 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
32078     
32079      
32080     enable:Roo.emptyFn,
32081     disable:Roo.emptyFn,
32082     focus:Roo.emptyFn,
32083      /**
32084      * Shows this button
32085      */
32086     show: function(){
32087         this.hidden = false;
32088         this.el.style.display = "";
32089     },
32090     
32091     /**
32092      * Hides this button
32093      */
32094     hide: function(){
32095         this.hidden = true;
32096         this.el.style.display = "none";
32097     }
32098     
32099 });
32100
32101 /**
32102  * @class Roo.Toolbar.Button
32103  * @extends Roo.Button
32104  * A button that renders into a toolbar.
32105  * @constructor
32106  * Creates a new Button
32107  * @param {Object} config A standard {@link Roo.Button} config object
32108  */
32109 Roo.Toolbar.Button = function(config){
32110     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
32111 };
32112 Roo.extend(Roo.Toolbar.Button, Roo.Button,
32113 {
32114     
32115     
32116     render : function(td){
32117         this.td = td;
32118         Roo.Toolbar.Button.superclass.render.call(this, td);
32119     },
32120     
32121     /**
32122      * Removes and destroys this button
32123      */
32124     destroy : function(){
32125         Roo.Toolbar.Button.superclass.destroy.call(this);
32126         this.td.parentNode.removeChild(this.td);
32127     },
32128     
32129     /**
32130      * Shows this button
32131      */
32132     show: function(){
32133         this.hidden = false;
32134         this.td.style.display = "";
32135     },
32136     
32137     /**
32138      * Hides this button
32139      */
32140     hide: function(){
32141         this.hidden = true;
32142         this.td.style.display = "none";
32143     },
32144
32145     /**
32146      * Disables this item
32147      */
32148     disable : function(){
32149         Roo.fly(this.td).addClass("x-item-disabled");
32150         this.disabled = true;
32151     },
32152
32153     /**
32154      * Enables this item
32155      */
32156     enable : function(){
32157         Roo.fly(this.td).removeClass("x-item-disabled");
32158         this.disabled = false;
32159     }
32160 });
32161 // backwards compat
32162 Roo.ToolbarButton = Roo.Toolbar.Button;
32163
32164 /**
32165  * @class Roo.Toolbar.SplitButton
32166  * @extends Roo.SplitButton
32167  * A menu button that renders into a toolbar.
32168  * @constructor
32169  * Creates a new SplitButton
32170  * @param {Object} config A standard {@link Roo.SplitButton} config object
32171  */
32172 Roo.Toolbar.SplitButton = function(config){
32173     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
32174 };
32175 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
32176     render : function(td){
32177         this.td = td;
32178         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
32179     },
32180     
32181     /**
32182      * Removes and destroys this button
32183      */
32184     destroy : function(){
32185         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
32186         this.td.parentNode.removeChild(this.td);
32187     },
32188     
32189     /**
32190      * Shows this button
32191      */
32192     show: function(){
32193         this.hidden = false;
32194         this.td.style.display = "";
32195     },
32196     
32197     /**
32198      * Hides this button
32199      */
32200     hide: function(){
32201         this.hidden = true;
32202         this.td.style.display = "none";
32203     }
32204 });
32205
32206 // backwards compat
32207 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
32208  * Based on:
32209  * Ext JS Library 1.1.1
32210  * Copyright(c) 2006-2007, Ext JS, LLC.
32211  *
32212  * Originally Released Under LGPL - original licence link has changed is not relivant.
32213  *
32214  * Fork - LGPL
32215  * <script type="text/javascript">
32216  */
32217  
32218 /**
32219  * @class Roo.PagingToolbar
32220  * @extends Roo.Toolbar
32221  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
32222  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32223  * @constructor
32224  * Create a new PagingToolbar
32225  * @param {Object} config The config object
32226  */
32227 Roo.PagingToolbar = function(el, ds, config)
32228 {
32229     // old args format still supported... - xtype is prefered..
32230     if (typeof(el) == 'object' && el.xtype) {
32231         // created from xtype...
32232         config = el;
32233         ds = el.dataSource;
32234         el = config.container;
32235     }
32236     var items = [];
32237     if (config.items) {
32238         items = config.items;
32239         config.items = [];
32240     }
32241     
32242     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32243     this.ds = ds;
32244     this.cursor = 0;
32245     this.renderButtons(this.el);
32246     this.bind(ds);
32247     
32248     // supprot items array.
32249    
32250     Roo.each(items, function(e) {
32251         this.add(Roo.factory(e));
32252     },this);
32253     
32254 };
32255
32256 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32257    
32258     /**
32259      * @cfg {String/HTMLElement/Element} container
32260      * container The id or element that will contain the toolbar
32261      */
32262     /**
32263      * @cfg {Boolean} displayInfo
32264      * True to display the displayMsg (defaults to false)
32265      */
32266     
32267     
32268     /**
32269      * @cfg {Number} pageSize
32270      * The number of records to display per page (defaults to 20)
32271      */
32272     pageSize: 20,
32273     /**
32274      * @cfg {String} displayMsg
32275      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32276      */
32277     displayMsg : 'Displaying {0} - {1} of {2}',
32278     /**
32279      * @cfg {String} emptyMsg
32280      * The message to display when no records are found (defaults to "No data to display")
32281      */
32282     emptyMsg : 'No data to display',
32283     /**
32284      * Customizable piece of the default paging text (defaults to "Page")
32285      * @type String
32286      */
32287     beforePageText : "Page",
32288     /**
32289      * Customizable piece of the default paging text (defaults to "of %0")
32290      * @type String
32291      */
32292     afterPageText : "of {0}",
32293     /**
32294      * Customizable piece of the default paging text (defaults to "First Page")
32295      * @type String
32296      */
32297     firstText : "First Page",
32298     /**
32299      * Customizable piece of the default paging text (defaults to "Previous Page")
32300      * @type String
32301      */
32302     prevText : "Previous Page",
32303     /**
32304      * Customizable piece of the default paging text (defaults to "Next Page")
32305      * @type String
32306      */
32307     nextText : "Next Page",
32308     /**
32309      * Customizable piece of the default paging text (defaults to "Last Page")
32310      * @type String
32311      */
32312     lastText : "Last Page",
32313     /**
32314      * Customizable piece of the default paging text (defaults to "Refresh")
32315      * @type String
32316      */
32317     refreshText : "Refresh",
32318
32319     // private
32320     renderButtons : function(el){
32321         Roo.PagingToolbar.superclass.render.call(this, el);
32322         this.first = this.addButton({
32323             tooltip: this.firstText,
32324             cls: "x-btn-icon x-grid-page-first",
32325             disabled: true,
32326             handler: this.onClick.createDelegate(this, ["first"])
32327         });
32328         this.prev = this.addButton({
32329             tooltip: this.prevText,
32330             cls: "x-btn-icon x-grid-page-prev",
32331             disabled: true,
32332             handler: this.onClick.createDelegate(this, ["prev"])
32333         });
32334         //this.addSeparator();
32335         this.add(this.beforePageText);
32336         this.field = Roo.get(this.addDom({
32337            tag: "input",
32338            type: "text",
32339            size: "3",
32340            value: "1",
32341            cls: "x-grid-page-number"
32342         }).el);
32343         this.field.on("keydown", this.onPagingKeydown, this);
32344         this.field.on("focus", function(){this.dom.select();});
32345         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32346         this.field.setHeight(18);
32347         //this.addSeparator();
32348         this.next = this.addButton({
32349             tooltip: this.nextText,
32350             cls: "x-btn-icon x-grid-page-next",
32351             disabled: true,
32352             handler: this.onClick.createDelegate(this, ["next"])
32353         });
32354         this.last = this.addButton({
32355             tooltip: this.lastText,
32356             cls: "x-btn-icon x-grid-page-last",
32357             disabled: true,
32358             handler: this.onClick.createDelegate(this, ["last"])
32359         });
32360         //this.addSeparator();
32361         this.loading = this.addButton({
32362             tooltip: this.refreshText,
32363             cls: "x-btn-icon x-grid-loading",
32364             handler: this.onClick.createDelegate(this, ["refresh"])
32365         });
32366
32367         if(this.displayInfo){
32368             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32369         }
32370     },
32371
32372     // private
32373     updateInfo : function(){
32374         if(this.displayEl){
32375             var count = this.ds.getCount();
32376             var msg = count == 0 ?
32377                 this.emptyMsg :
32378                 String.format(
32379                     this.displayMsg,
32380                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32381                 );
32382             this.displayEl.update(msg);
32383         }
32384     },
32385
32386     // private
32387     onLoad : function(ds, r, o){
32388        this.cursor = o.params ? o.params.start : 0;
32389        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32390
32391        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32392        this.field.dom.value = ap;
32393        this.first.setDisabled(ap == 1);
32394        this.prev.setDisabled(ap == 1);
32395        this.next.setDisabled(ap == ps);
32396        this.last.setDisabled(ap == ps);
32397        this.loading.enable();
32398        this.updateInfo();
32399     },
32400
32401     // private
32402     getPageData : function(){
32403         var total = this.ds.getTotalCount();
32404         return {
32405             total : total,
32406             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32407             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32408         };
32409     },
32410
32411     // private
32412     onLoadError : function(){
32413         this.loading.enable();
32414     },
32415
32416     // private
32417     onPagingKeydown : function(e){
32418         var k = e.getKey();
32419         var d = this.getPageData();
32420         if(k == e.RETURN){
32421             var v = this.field.dom.value, pageNum;
32422             if(!v || isNaN(pageNum = parseInt(v, 10))){
32423                 this.field.dom.value = d.activePage;
32424                 return;
32425             }
32426             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32427             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32428             e.stopEvent();
32429         }
32430         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))
32431         {
32432           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32433           this.field.dom.value = pageNum;
32434           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32435           e.stopEvent();
32436         }
32437         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32438         {
32439           var v = this.field.dom.value, pageNum; 
32440           var increment = (e.shiftKey) ? 10 : 1;
32441           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32442             increment *= -1;
32443           }
32444           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32445             this.field.dom.value = d.activePage;
32446             return;
32447           }
32448           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32449           {
32450             this.field.dom.value = parseInt(v, 10) + increment;
32451             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32452             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32453           }
32454           e.stopEvent();
32455         }
32456     },
32457
32458     // private
32459     beforeLoad : function(){
32460         if(this.loading){
32461             this.loading.disable();
32462         }
32463     },
32464     /**
32465      * event that occurs when you click on the navigation buttons - can be used to trigger load of a grid.
32466      * @param {String} which (first|prev|next|last|refresh)  which button to press.
32467      *
32468      */
32469     // private
32470     onClick : function(which){
32471         var ds = this.ds;
32472         switch(which){
32473             case "first":
32474                 ds.load({params:{start: 0, limit: this.pageSize}});
32475             break;
32476             case "prev":
32477                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32478             break;
32479             case "next":
32480                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32481             break;
32482             case "last":
32483                 var total = ds.getTotalCount();
32484                 var extra = total % this.pageSize;
32485                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32486                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32487             break;
32488             case "refresh":
32489                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32490             break;
32491         }
32492     },
32493
32494     /**
32495      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32496      * @param {Roo.data.Store} store The data store to unbind
32497      */
32498     unbind : function(ds){
32499         ds.un("beforeload", this.beforeLoad, this);
32500         ds.un("load", this.onLoad, this);
32501         ds.un("loadexception", this.onLoadError, this);
32502         ds.un("remove", this.updateInfo, this);
32503         ds.un("add", this.updateInfo, this);
32504         this.ds = undefined;
32505     },
32506
32507     /**
32508      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32509      * @param {Roo.data.Store} store The data store to bind
32510      */
32511     bind : function(ds){
32512         ds.on("beforeload", this.beforeLoad, this);
32513         ds.on("load", this.onLoad, this);
32514         ds.on("loadexception", this.onLoadError, this);
32515         ds.on("remove", this.updateInfo, this);
32516         ds.on("add", this.updateInfo, this);
32517         this.ds = ds;
32518     }
32519 });/*
32520  * Based on:
32521  * Ext JS Library 1.1.1
32522  * Copyright(c) 2006-2007, Ext JS, LLC.
32523  *
32524  * Originally Released Under LGPL - original licence link has changed is not relivant.
32525  *
32526  * Fork - LGPL
32527  * <script type="text/javascript">
32528  */
32529
32530 /**
32531  * @class Roo.Resizable
32532  * @extends Roo.util.Observable
32533  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32534  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32535  * 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
32536  * the element will be wrapped for you automatically.</p>
32537  * <p>Here is the list of valid resize handles:</p>
32538  * <pre>
32539 Value   Description
32540 ------  -------------------
32541  'n'     north
32542  's'     south
32543  'e'     east
32544  'w'     west
32545  'nw'    northwest
32546  'sw'    southwest
32547  'se'    southeast
32548  'ne'    northeast
32549  'hd'    horizontal drag
32550  'all'   all
32551 </pre>
32552  * <p>Here's an example showing the creation of a typical Resizable:</p>
32553  * <pre><code>
32554 var resizer = new Roo.Resizable("element-id", {
32555     handles: 'all',
32556     minWidth: 200,
32557     minHeight: 100,
32558     maxWidth: 500,
32559     maxHeight: 400,
32560     pinned: true
32561 });
32562 resizer.on("resize", myHandler);
32563 </code></pre>
32564  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32565  * resizer.east.setDisplayed(false);</p>
32566  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32567  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32568  * resize operation's new size (defaults to [0, 0])
32569  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32570  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32571  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32572  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32573  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32574  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32575  * @cfg {Number} width The width of the element in pixels (defaults to null)
32576  * @cfg {Number} height The height of the element in pixels (defaults to null)
32577  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32578  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32579  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32580  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32581  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32582  * in favor of the handles config option (defaults to false)
32583  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32584  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32585  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32586  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32587  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32588  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32589  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32590  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32591  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32592  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32593  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32594  * @constructor
32595  * Create a new resizable component
32596  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32597  * @param {Object} config configuration options
32598   */
32599 Roo.Resizable = function(el, config)
32600 {
32601     this.el = Roo.get(el);
32602
32603     if(config && config.wrap){
32604         config.resizeChild = this.el;
32605         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32606         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32607         this.el.setStyle("overflow", "hidden");
32608         this.el.setPositioning(config.resizeChild.getPositioning());
32609         config.resizeChild.clearPositioning();
32610         if(!config.width || !config.height){
32611             var csize = config.resizeChild.getSize();
32612             this.el.setSize(csize.width, csize.height);
32613         }
32614         if(config.pinned && !config.adjustments){
32615             config.adjustments = "auto";
32616         }
32617     }
32618
32619     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32620     this.proxy.unselectable();
32621     this.proxy.enableDisplayMode('block');
32622
32623     Roo.apply(this, config);
32624
32625     if(this.pinned){
32626         this.disableTrackOver = true;
32627         this.el.addClass("x-resizable-pinned");
32628     }
32629     // if the element isn't positioned, make it relative
32630     var position = this.el.getStyle("position");
32631     if(position != "absolute" && position != "fixed"){
32632         this.el.setStyle("position", "relative");
32633     }
32634     if(!this.handles){ // no handles passed, must be legacy style
32635         this.handles = 's,e,se';
32636         if(this.multiDirectional){
32637             this.handles += ',n,w';
32638         }
32639     }
32640     if(this.handles == "all"){
32641         this.handles = "n s e w ne nw se sw";
32642     }
32643     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32644     var ps = Roo.Resizable.positions;
32645     for(var i = 0, len = hs.length; i < len; i++){
32646         if(hs[i] && ps[hs[i]]){
32647             var pos = ps[hs[i]];
32648             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32649         }
32650     }
32651     // legacy
32652     this.corner = this.southeast;
32653     
32654     // updateBox = the box can move..
32655     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32656         this.updateBox = true;
32657     }
32658
32659     this.activeHandle = null;
32660
32661     if(this.resizeChild){
32662         if(typeof this.resizeChild == "boolean"){
32663             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32664         }else{
32665             this.resizeChild = Roo.get(this.resizeChild, true);
32666         }
32667     }
32668     
32669     if(this.adjustments == "auto"){
32670         var rc = this.resizeChild;
32671         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32672         if(rc && (hw || hn)){
32673             rc.position("relative");
32674             rc.setLeft(hw ? hw.el.getWidth() : 0);
32675             rc.setTop(hn ? hn.el.getHeight() : 0);
32676         }
32677         this.adjustments = [
32678             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32679             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32680         ];
32681     }
32682
32683     if(this.draggable){
32684         this.dd = this.dynamic ?
32685             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32686         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32687     }
32688
32689     // public events
32690     this.addEvents({
32691         /**
32692          * @event beforeresize
32693          * Fired before resize is allowed. Set enabled to false to cancel resize.
32694          * @param {Roo.Resizable} this
32695          * @param {Roo.EventObject} e The mousedown event
32696          */
32697         "beforeresize" : true,
32698         /**
32699          * @event resizing
32700          * Fired a resizing.
32701          * @param {Roo.Resizable} this
32702          * @param {Number} x The new x position
32703          * @param {Number} y The new y position
32704          * @param {Number} w The new w width
32705          * @param {Number} h The new h hight
32706          * @param {Roo.EventObject} e The mouseup event
32707          */
32708         "resizing" : true,
32709         /**
32710          * @event resize
32711          * Fired after a resize.
32712          * @param {Roo.Resizable} this
32713          * @param {Number} width The new width
32714          * @param {Number} height The new height
32715          * @param {Roo.EventObject} e The mouseup event
32716          */
32717         "resize" : true
32718     });
32719
32720     if(this.width !== null && this.height !== null){
32721         this.resizeTo(this.width, this.height);
32722     }else{
32723         this.updateChildSize();
32724     }
32725     if(Roo.isIE){
32726         this.el.dom.style.zoom = 1;
32727     }
32728     Roo.Resizable.superclass.constructor.call(this);
32729 };
32730
32731 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32732         resizeChild : false,
32733         adjustments : [0, 0],
32734         minWidth : 5,
32735         minHeight : 5,
32736         maxWidth : 10000,
32737         maxHeight : 10000,
32738         enabled : true,
32739         animate : false,
32740         duration : .35,
32741         dynamic : false,
32742         handles : false,
32743         multiDirectional : false,
32744         disableTrackOver : false,
32745         easing : 'easeOutStrong',
32746         widthIncrement : 0,
32747         heightIncrement : 0,
32748         pinned : false,
32749         width : null,
32750         height : null,
32751         preserveRatio : false,
32752         transparent: false,
32753         minX: 0,
32754         minY: 0,
32755         draggable: false,
32756
32757         /**
32758          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32759          */
32760         constrainTo: undefined,
32761         /**
32762          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32763          */
32764         resizeRegion: undefined,
32765
32766
32767     /**
32768      * Perform a manual resize
32769      * @param {Number} width
32770      * @param {Number} height
32771      */
32772     resizeTo : function(width, height){
32773         this.el.setSize(width, height);
32774         this.updateChildSize();
32775         this.fireEvent("resize", this, width, height, null);
32776     },
32777
32778     // private
32779     startSizing : function(e, handle){
32780         this.fireEvent("beforeresize", this, e);
32781         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32782
32783             if(!this.overlay){
32784                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32785                 this.overlay.unselectable();
32786                 this.overlay.enableDisplayMode("block");
32787                 this.overlay.on("mousemove", this.onMouseMove, this);
32788                 this.overlay.on("mouseup", this.onMouseUp, this);
32789             }
32790             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32791
32792             this.resizing = true;
32793             this.startBox = this.el.getBox();
32794             this.startPoint = e.getXY();
32795             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32796                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32797
32798             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32799             this.overlay.show();
32800
32801             if(this.constrainTo) {
32802                 var ct = Roo.get(this.constrainTo);
32803                 this.resizeRegion = ct.getRegion().adjust(
32804                     ct.getFrameWidth('t'),
32805                     ct.getFrameWidth('l'),
32806                     -ct.getFrameWidth('b'),
32807                     -ct.getFrameWidth('r')
32808                 );
32809             }
32810
32811             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32812             this.proxy.show();
32813             this.proxy.setBox(this.startBox);
32814             if(!this.dynamic){
32815                 this.proxy.setStyle('visibility', 'visible');
32816             }
32817         }
32818     },
32819
32820     // private
32821     onMouseDown : function(handle, e){
32822         if(this.enabled){
32823             e.stopEvent();
32824             this.activeHandle = handle;
32825             this.startSizing(e, handle);
32826         }
32827     },
32828
32829     // private
32830     onMouseUp : function(e){
32831         var size = this.resizeElement();
32832         this.resizing = false;
32833         this.handleOut();
32834         this.overlay.hide();
32835         this.proxy.hide();
32836         this.fireEvent("resize", this, size.width, size.height, e);
32837     },
32838
32839     // private
32840     updateChildSize : function(){
32841         
32842         if(this.resizeChild){
32843             var el = this.el;
32844             var child = this.resizeChild;
32845             var adj = this.adjustments;
32846             if(el.dom.offsetWidth){
32847                 var b = el.getSize(true);
32848                 child.setSize(b.width+adj[0], b.height+adj[1]);
32849             }
32850             // Second call here for IE
32851             // The first call enables instant resizing and
32852             // the second call corrects scroll bars if they
32853             // exist
32854             if(Roo.isIE){
32855                 setTimeout(function(){
32856                     if(el.dom.offsetWidth){
32857                         var b = el.getSize(true);
32858                         child.setSize(b.width+adj[0], b.height+adj[1]);
32859                     }
32860                 }, 10);
32861             }
32862         }
32863     },
32864
32865     // private
32866     snap : function(value, inc, min){
32867         if(!inc || !value) {
32868             return value;
32869         }
32870         var newValue = value;
32871         var m = value % inc;
32872         if(m > 0){
32873             if(m > (inc/2)){
32874                 newValue = value + (inc-m);
32875             }else{
32876                 newValue = value - m;
32877             }
32878         }
32879         return Math.max(min, newValue);
32880     },
32881
32882     // private
32883     resizeElement : function(){
32884         var box = this.proxy.getBox();
32885         if(this.updateBox){
32886             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32887         }else{
32888             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32889         }
32890         this.updateChildSize();
32891         if(!this.dynamic){
32892             this.proxy.hide();
32893         }
32894         return box;
32895     },
32896
32897     // private
32898     constrain : function(v, diff, m, mx){
32899         if(v - diff < m){
32900             diff = v - m;
32901         }else if(v - diff > mx){
32902             diff = mx - v;
32903         }
32904         return diff;
32905     },
32906
32907     // private
32908     onMouseMove : function(e){
32909         
32910         if(this.enabled){
32911             try{// try catch so if something goes wrong the user doesn't get hung
32912
32913             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32914                 return;
32915             }
32916
32917             //var curXY = this.startPoint;
32918             var curSize = this.curSize || this.startBox;
32919             var x = this.startBox.x, y = this.startBox.y;
32920             var ox = x, oy = y;
32921             var w = curSize.width, h = curSize.height;
32922             var ow = w, oh = h;
32923             var mw = this.minWidth, mh = this.minHeight;
32924             var mxw = this.maxWidth, mxh = this.maxHeight;
32925             var wi = this.widthIncrement;
32926             var hi = this.heightIncrement;
32927
32928             var eventXY = e.getXY();
32929             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32930             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32931
32932             var pos = this.activeHandle.position;
32933
32934             switch(pos){
32935                 case "east":
32936                     w += diffX;
32937                     w = Math.min(Math.max(mw, w), mxw);
32938                     break;
32939              
32940                 case "south":
32941                     h += diffY;
32942                     h = Math.min(Math.max(mh, h), mxh);
32943                     break;
32944                 case "southeast":
32945                     w += diffX;
32946                     h += diffY;
32947                     w = Math.min(Math.max(mw, w), mxw);
32948                     h = Math.min(Math.max(mh, h), mxh);
32949                     break;
32950                 case "north":
32951                     diffY = this.constrain(h, diffY, mh, mxh);
32952                     y += diffY;
32953                     h -= diffY;
32954                     break;
32955                 case "hdrag":
32956                     
32957                     if (wi) {
32958                         var adiffX = Math.abs(diffX);
32959                         var sub = (adiffX % wi); // how much 
32960                         if (sub > (wi/2)) { // far enough to snap
32961                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32962                         } else {
32963                             // remove difference.. 
32964                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32965                         }
32966                     }
32967                     x += diffX;
32968                     x = Math.max(this.minX, x);
32969                     break;
32970                 case "west":
32971                     diffX = this.constrain(w, diffX, mw, mxw);
32972                     x += diffX;
32973                     w -= diffX;
32974                     break;
32975                 case "northeast":
32976                     w += diffX;
32977                     w = Math.min(Math.max(mw, w), mxw);
32978                     diffY = this.constrain(h, diffY, mh, mxh);
32979                     y += diffY;
32980                     h -= diffY;
32981                     break;
32982                 case "northwest":
32983                     diffX = this.constrain(w, diffX, mw, mxw);
32984                     diffY = this.constrain(h, diffY, mh, mxh);
32985                     y += diffY;
32986                     h -= diffY;
32987                     x += diffX;
32988                     w -= diffX;
32989                     break;
32990                case "southwest":
32991                     diffX = this.constrain(w, diffX, mw, mxw);
32992                     h += diffY;
32993                     h = Math.min(Math.max(mh, h), mxh);
32994                     x += diffX;
32995                     w -= diffX;
32996                     break;
32997             }
32998
32999             var sw = this.snap(w, wi, mw);
33000             var sh = this.snap(h, hi, mh);
33001             if(sw != w || sh != h){
33002                 switch(pos){
33003                     case "northeast":
33004                         y -= sh - h;
33005                     break;
33006                     case "north":
33007                         y -= sh - h;
33008                         break;
33009                     case "southwest":
33010                         x -= sw - w;
33011                     break;
33012                     case "west":
33013                         x -= sw - w;
33014                         break;
33015                     case "northwest":
33016                         x -= sw - w;
33017                         y -= sh - h;
33018                     break;
33019                 }
33020                 w = sw;
33021                 h = sh;
33022             }
33023
33024             if(this.preserveRatio){
33025                 switch(pos){
33026                     case "southeast":
33027                     case "east":
33028                         h = oh * (w/ow);
33029                         h = Math.min(Math.max(mh, h), mxh);
33030                         w = ow * (h/oh);
33031                        break;
33032                     case "south":
33033                         w = ow * (h/oh);
33034                         w = Math.min(Math.max(mw, w), mxw);
33035                         h = oh * (w/ow);
33036                         break;
33037                     case "northeast":
33038                         w = ow * (h/oh);
33039                         w = Math.min(Math.max(mw, w), mxw);
33040                         h = oh * (w/ow);
33041                     break;
33042                     case "north":
33043                         var tw = w;
33044                         w = ow * (h/oh);
33045                         w = Math.min(Math.max(mw, w), mxw);
33046                         h = oh * (w/ow);
33047                         x += (tw - w) / 2;
33048                         break;
33049                     case "southwest":
33050                         h = oh * (w/ow);
33051                         h = Math.min(Math.max(mh, h), mxh);
33052                         var tw = w;
33053                         w = ow * (h/oh);
33054                         x += tw - w;
33055                         break;
33056                     case "west":
33057                         var th = h;
33058                         h = oh * (w/ow);
33059                         h = Math.min(Math.max(mh, h), mxh);
33060                         y += (th - h) / 2;
33061                         var tw = w;
33062                         w = ow * (h/oh);
33063                         x += tw - w;
33064                        break;
33065                     case "northwest":
33066                         var tw = w;
33067                         var th = h;
33068                         h = oh * (w/ow);
33069                         h = Math.min(Math.max(mh, h), mxh);
33070                         w = ow * (h/oh);
33071                         y += th - h;
33072                         x += tw - w;
33073                        break;
33074
33075                 }
33076             }
33077             if (pos == 'hdrag') {
33078                 w = ow;
33079             }
33080             this.proxy.setBounds(x, y, w, h);
33081             if(this.dynamic){
33082                 this.resizeElement();
33083             }
33084             }catch(e){}
33085         }
33086         this.fireEvent("resizing", this, x, y, w, h, e);
33087     },
33088
33089     // private
33090     handleOver : function(){
33091         if(this.enabled){
33092             this.el.addClass("x-resizable-over");
33093         }
33094     },
33095
33096     // private
33097     handleOut : function(){
33098         if(!this.resizing){
33099             this.el.removeClass("x-resizable-over");
33100         }
33101     },
33102
33103     /**
33104      * Returns the element this component is bound to.
33105      * @return {Roo.Element}
33106      */
33107     getEl : function(){
33108         return this.el;
33109     },
33110
33111     /**
33112      * Returns the resizeChild element (or null).
33113      * @return {Roo.Element}
33114      */
33115     getResizeChild : function(){
33116         return this.resizeChild;
33117     },
33118     groupHandler : function()
33119     {
33120         
33121     },
33122     /**
33123      * Destroys this resizable. If the element was wrapped and
33124      * removeEl is not true then the element remains.
33125      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33126      */
33127     destroy : function(removeEl){
33128         this.proxy.remove();
33129         if(this.overlay){
33130             this.overlay.removeAllListeners();
33131             this.overlay.remove();
33132         }
33133         var ps = Roo.Resizable.positions;
33134         for(var k in ps){
33135             if(typeof ps[k] != "function" && this[ps[k]]){
33136                 var h = this[ps[k]];
33137                 h.el.removeAllListeners();
33138                 h.el.remove();
33139             }
33140         }
33141         if(removeEl){
33142             this.el.update("");
33143             this.el.remove();
33144         }
33145     }
33146 });
33147
33148 // private
33149 // hash to map config positions to true positions
33150 Roo.Resizable.positions = {
33151     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
33152     hd: "hdrag"
33153 };
33154
33155 // private
33156 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
33157     if(!this.tpl){
33158         // only initialize the template if resizable is used
33159         var tpl = Roo.DomHelper.createTemplate(
33160             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
33161         );
33162         tpl.compile();
33163         Roo.Resizable.Handle.prototype.tpl = tpl;
33164     }
33165     this.position = pos;
33166     this.rz = rz;
33167     // show north drag fro topdra
33168     var handlepos = pos == 'hdrag' ? 'north' : pos;
33169     
33170     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
33171     if (pos == 'hdrag') {
33172         this.el.setStyle('cursor', 'pointer');
33173     }
33174     this.el.unselectable();
33175     if(transparent){
33176         this.el.setOpacity(0);
33177     }
33178     this.el.on("mousedown", this.onMouseDown, this);
33179     if(!disableTrackOver){
33180         this.el.on("mouseover", this.onMouseOver, this);
33181         this.el.on("mouseout", this.onMouseOut, this);
33182     }
33183 };
33184
33185 // private
33186 Roo.Resizable.Handle.prototype = {
33187     afterResize : function(rz){
33188         Roo.log('after?');
33189         // do nothing
33190     },
33191     // private
33192     onMouseDown : function(e){
33193         this.rz.onMouseDown(this, e);
33194     },
33195     // private
33196     onMouseOver : function(e){
33197         this.rz.handleOver(this, e);
33198     },
33199     // private
33200     onMouseOut : function(e){
33201         this.rz.handleOut(this, e);
33202     }
33203 };/*
33204  * Based on:
33205  * Ext JS Library 1.1.1
33206  * Copyright(c) 2006-2007, Ext JS, LLC.
33207  *
33208  * Originally Released Under LGPL - original licence link has changed is not relivant.
33209  *
33210  * Fork - LGPL
33211  * <script type="text/javascript">
33212  */
33213
33214 /**
33215  * @class Roo.Editor
33216  * @extends Roo.Component
33217  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
33218  * @constructor
33219  * Create a new Editor
33220  * @param {Roo.form.Field} field The Field object (or descendant)
33221  * @param {Object} config The config object
33222  */
33223 Roo.Editor = function(field, config){
33224     Roo.Editor.superclass.constructor.call(this, config);
33225     this.field = field;
33226     this.addEvents({
33227         /**
33228              * @event beforestartedit
33229              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33230              * false from the handler of this event.
33231              * @param {Editor} this
33232              * @param {Roo.Element} boundEl The underlying element bound to this editor
33233              * @param {Mixed} value The field value being set
33234              */
33235         "beforestartedit" : true,
33236         /**
33237              * @event startedit
33238              * Fires when this editor is displayed
33239              * @param {Roo.Element} boundEl The underlying element bound to this editor
33240              * @param {Mixed} value The starting field value
33241              */
33242         "startedit" : true,
33243         /**
33244              * @event beforecomplete
33245              * Fires after a change has been made to the field, but before the change is reflected in the underlying
33246              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
33247              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33248              * event will not fire since no edit actually occurred.
33249              * @param {Editor} this
33250              * @param {Mixed} value The current field value
33251              * @param {Mixed} startValue The original field value
33252              */
33253         "beforecomplete" : true,
33254         /**
33255              * @event complete
33256              * Fires after editing is complete and any changed value has been written to the underlying field.
33257              * @param {Editor} this
33258              * @param {Mixed} value The current field value
33259              * @param {Mixed} startValue The original field value
33260              */
33261         "complete" : true,
33262         /**
33263          * @event specialkey
33264          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
33265          * {@link Roo.EventObject#getKey} to determine which key was pressed.
33266          * @param {Roo.form.Field} this
33267          * @param {Roo.EventObject} e The event object
33268          */
33269         "specialkey" : true
33270     });
33271 };
33272
33273 Roo.extend(Roo.Editor, Roo.Component, {
33274     /**
33275      * @cfg {Boolean/String} autosize
33276      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33277      * or "height" to adopt the height only (defaults to false)
33278      */
33279     /**
33280      * @cfg {Boolean} revertInvalid
33281      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33282      * validation fails (defaults to true)
33283      */
33284     /**
33285      * @cfg {Boolean} ignoreNoChange
33286      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33287      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
33288      * will never be ignored.
33289      */
33290     /**
33291      * @cfg {Boolean} hideEl
33292      * False to keep the bound element visible while the editor is displayed (defaults to true)
33293      */
33294     /**
33295      * @cfg {Mixed} value
33296      * The data value of the underlying field (defaults to "")
33297      */
33298     value : "",
33299     /**
33300      * @cfg {String} alignment
33301      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33302      */
33303     alignment: "c-c?",
33304     /**
33305      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33306      * for bottom-right shadow (defaults to "frame")
33307      */
33308     shadow : "frame",
33309     /**
33310      * @cfg {Boolean} constrain True to constrain the editor to the viewport
33311      */
33312     constrain : false,
33313     /**
33314      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33315      */
33316     completeOnEnter : false,
33317     /**
33318      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33319      */
33320     cancelOnEsc : false,
33321     /**
33322      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33323      */
33324     updateEl : false,
33325
33326     // private
33327     onRender : function(ct, position){
33328         this.el = new Roo.Layer({
33329             shadow: this.shadow,
33330             cls: "x-editor",
33331             parentEl : ct,
33332             shim : this.shim,
33333             shadowOffset:4,
33334             id: this.id,
33335             constrain: this.constrain
33336         });
33337         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33338         if(this.field.msgTarget != 'title'){
33339             this.field.msgTarget = 'qtip';
33340         }
33341         this.field.render(this.el);
33342         if(Roo.isGecko){
33343             this.field.el.dom.setAttribute('autocomplete', 'off');
33344         }
33345         this.field.on("specialkey", this.onSpecialKey, this);
33346         if(this.swallowKeys){
33347             this.field.el.swallowEvent(['keydown','keypress']);
33348         }
33349         this.field.show();
33350         this.field.on("blur", this.onBlur, this);
33351         if(this.field.grow){
33352             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
33353         }
33354     },
33355
33356     onSpecialKey : function(field, e)
33357     {
33358         //Roo.log('editor onSpecialKey');
33359         if(this.completeOnEnter && e.getKey() == e.ENTER){
33360             e.stopEvent();
33361             this.completeEdit();
33362             return;
33363         }
33364         // do not fire special key otherwise it might hide close the editor...
33365         if(e.getKey() == e.ENTER){    
33366             return;
33367         }
33368         if(this.cancelOnEsc && e.getKey() == e.ESC){
33369             this.cancelEdit();
33370             return;
33371         } 
33372         this.fireEvent('specialkey', field, e);
33373     
33374     },
33375
33376     /**
33377      * Starts the editing process and shows the editor.
33378      * @param {String/HTMLElement/Element} el The element to edit
33379      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33380       * to the innerHTML of el.
33381      */
33382     startEdit : function(el, value){
33383         if(this.editing){
33384             this.completeEdit();
33385         }
33386         this.boundEl = Roo.get(el);
33387         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33388         if(!this.rendered){
33389             this.render(this.parentEl || document.body);
33390         }
33391         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33392             return;
33393         }
33394         this.startValue = v;
33395         this.field.setValue(v);
33396         if(this.autoSize){
33397             var sz = this.boundEl.getSize();
33398             switch(this.autoSize){
33399                 case "width":
33400                 this.setSize(sz.width,  "");
33401                 break;
33402                 case "height":
33403                 this.setSize("",  sz.height);
33404                 break;
33405                 default:
33406                 this.setSize(sz.width,  sz.height);
33407             }
33408         }
33409         this.el.alignTo(this.boundEl, this.alignment);
33410         this.editing = true;
33411         if(Roo.QuickTips){
33412             Roo.QuickTips.disable();
33413         }
33414         this.show();
33415     },
33416
33417     /**
33418      * Sets the height and width of this editor.
33419      * @param {Number} width The new width
33420      * @param {Number} height The new height
33421      */
33422     setSize : function(w, h){
33423         this.field.setSize(w, h);
33424         if(this.el){
33425             this.el.sync();
33426         }
33427     },
33428
33429     /**
33430      * Realigns the editor to the bound field based on the current alignment config value.
33431      */
33432     realign : function(){
33433         this.el.alignTo(this.boundEl, this.alignment);
33434     },
33435
33436     /**
33437      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33438      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33439      */
33440     completeEdit : function(remainVisible){
33441         if(!this.editing){
33442             return;
33443         }
33444         var v = this.getValue();
33445         if(this.revertInvalid !== false && !this.field.isValid()){
33446             v = this.startValue;
33447             this.cancelEdit(true);
33448         }
33449         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33450             this.editing = false;
33451             this.hide();
33452             return;
33453         }
33454         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33455             this.editing = false;
33456             if(this.updateEl && this.boundEl){
33457                 this.boundEl.update(v);
33458             }
33459             if(remainVisible !== true){
33460                 this.hide();
33461             }
33462             this.fireEvent("complete", this, v, this.startValue);
33463         }
33464     },
33465
33466     // private
33467     onShow : function(){
33468         this.el.show();
33469         if(this.hideEl !== false){
33470             this.boundEl.hide();
33471         }
33472         this.field.show();
33473         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33474             this.fixIEFocus = true;
33475             this.deferredFocus.defer(50, this);
33476         }else{
33477             this.field.focus();
33478         }
33479         this.fireEvent("startedit", this.boundEl, this.startValue);
33480     },
33481
33482     deferredFocus : function(){
33483         if(this.editing){
33484             this.field.focus();
33485         }
33486     },
33487
33488     /**
33489      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33490      * reverted to the original starting value.
33491      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33492      * cancel (defaults to false)
33493      */
33494     cancelEdit : function(remainVisible){
33495         if(this.editing){
33496             this.setValue(this.startValue);
33497             if(remainVisible !== true){
33498                 this.hide();
33499             }
33500         }
33501     },
33502
33503     // private
33504     onBlur : function(){
33505         if(this.allowBlur !== true && this.editing){
33506             this.completeEdit();
33507         }
33508     },
33509
33510     // private
33511     onHide : function(){
33512         if(this.editing){
33513             this.completeEdit();
33514             return;
33515         }
33516         this.field.blur();
33517         if(this.field.collapse){
33518             this.field.collapse();
33519         }
33520         this.el.hide();
33521         if(this.hideEl !== false){
33522             this.boundEl.show();
33523         }
33524         if(Roo.QuickTips){
33525             Roo.QuickTips.enable();
33526         }
33527     },
33528
33529     /**
33530      * Sets the data value of the editor
33531      * @param {Mixed} value Any valid value supported by the underlying field
33532      */
33533     setValue : function(v){
33534         this.field.setValue(v);
33535     },
33536
33537     /**
33538      * Gets the data value of the editor
33539      * @return {Mixed} The data value
33540      */
33541     getValue : function(){
33542         return this.field.getValue();
33543     }
33544 });/*
33545  * Based on:
33546  * Ext JS Library 1.1.1
33547  * Copyright(c) 2006-2007, Ext JS, LLC.
33548  *
33549  * Originally Released Under LGPL - original licence link has changed is not relivant.
33550  *
33551  * Fork - LGPL
33552  * <script type="text/javascript">
33553  */
33554  
33555 /**
33556  * @class Roo.BasicDialog
33557  * @extends Roo.util.Observable
33558  * @parent none builder
33559  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33560  * <pre><code>
33561 var dlg = new Roo.BasicDialog("my-dlg", {
33562     height: 200,
33563     width: 300,
33564     minHeight: 100,
33565     minWidth: 150,
33566     modal: true,
33567     proxyDrag: true,
33568     shadow: true
33569 });
33570 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33571 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33572 dlg.addButton('Cancel', dlg.hide, dlg);
33573 dlg.show();
33574 </code></pre>
33575   <b>A Dialog should always be a direct child of the body element.</b>
33576  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33577  * @cfg {String} title Default text to display in the title bar (defaults to null)
33578  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33579  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33580  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33581  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33582  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33583  * (defaults to null with no animation)
33584  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33585  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33586  * property for valid values (defaults to 'all')
33587  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33588  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33589  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33590  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33591  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33592  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33593  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33594  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33595  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33596  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33597  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33598  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33599  * draggable = true (defaults to false)
33600  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33601  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33602  * shadow (defaults to false)
33603  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33604  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33605  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33606  * @cfg {Array} buttons Array of buttons
33607  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33608  * @constructor
33609  * Create a new BasicDialog.
33610  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33611  * @param {Object} config Configuration options
33612  */
33613 Roo.BasicDialog = function(el, config){
33614     this.el = Roo.get(el);
33615     var dh = Roo.DomHelper;
33616     if(!this.el && config && config.autoCreate){
33617         if(typeof config.autoCreate == "object"){
33618             if(!config.autoCreate.id){
33619                 config.autoCreate.id = el;
33620             }
33621             this.el = dh.append(document.body,
33622                         config.autoCreate, true);
33623         }else{
33624             this.el = dh.append(document.body,
33625                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33626         }
33627     }
33628     el = this.el;
33629     el.setDisplayed(true);
33630     el.hide = this.hideAction;
33631     this.id = el.id;
33632     el.addClass("x-dlg");
33633
33634     Roo.apply(this, config);
33635
33636     this.proxy = el.createProxy("x-dlg-proxy");
33637     this.proxy.hide = this.hideAction;
33638     this.proxy.setOpacity(.5);
33639     this.proxy.hide();
33640
33641     if(config.width){
33642         el.setWidth(config.width);
33643     }
33644     if(config.height){
33645         el.setHeight(config.height);
33646     }
33647     this.size = el.getSize();
33648     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33649         this.xy = [config.x,config.y];
33650     }else{
33651         this.xy = el.getCenterXY(true);
33652     }
33653     /** The header element @type Roo.Element */
33654     this.header = el.child("> .x-dlg-hd");
33655     /** The body element @type Roo.Element */
33656     this.body = el.child("> .x-dlg-bd");
33657     /** The footer element @type Roo.Element */
33658     this.footer = el.child("> .x-dlg-ft");
33659
33660     if(!this.header){
33661         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33662     }
33663     if(!this.body){
33664         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33665     }
33666
33667     this.header.unselectable();
33668     if(this.title){
33669         this.header.update(this.title);
33670     }
33671     // this element allows the dialog to be focused for keyboard event
33672     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33673     this.focusEl.swallowEvent("click", true);
33674
33675     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33676
33677     // wrap the body and footer for special rendering
33678     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33679     if(this.footer){
33680         this.bwrap.dom.appendChild(this.footer.dom);
33681     }
33682
33683     this.bg = this.el.createChild({
33684         tag: "div", cls:"x-dlg-bg",
33685         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33686     });
33687     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33688
33689
33690     if(this.autoScroll !== false && !this.autoTabs){
33691         this.body.setStyle("overflow", "auto");
33692     }
33693
33694     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33695
33696     if(this.closable !== false){
33697         this.el.addClass("x-dlg-closable");
33698         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33699         this.close.on("click", this.closeClick, this);
33700         this.close.addClassOnOver("x-dlg-close-over");
33701     }
33702     if(this.collapsible !== false){
33703         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33704         this.collapseBtn.on("click", this.collapseClick, this);
33705         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33706         this.header.on("dblclick", this.collapseClick, this);
33707     }
33708     if(this.resizable !== false){
33709         this.el.addClass("x-dlg-resizable");
33710         this.resizer = new Roo.Resizable(el, {
33711             minWidth: this.minWidth || 80,
33712             minHeight:this.minHeight || 80,
33713             handles: this.resizeHandles || "all",
33714             pinned: true
33715         });
33716         this.resizer.on("beforeresize", this.beforeResize, this);
33717         this.resizer.on("resize", this.onResize, this);
33718     }
33719     if(this.draggable !== false){
33720         el.addClass("x-dlg-draggable");
33721         if (!this.proxyDrag) {
33722             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33723         }
33724         else {
33725             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33726         }
33727         dd.setHandleElId(this.header.id);
33728         dd.endDrag = this.endMove.createDelegate(this);
33729         dd.startDrag = this.startMove.createDelegate(this);
33730         dd.onDrag = this.onDrag.createDelegate(this);
33731         dd.scroll = false;
33732         this.dd = dd;
33733     }
33734     if(this.modal){
33735         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33736         this.mask.enableDisplayMode("block");
33737         this.mask.hide();
33738         this.el.addClass("x-dlg-modal");
33739     }
33740     if(this.shadow){
33741         this.shadow = new Roo.Shadow({
33742             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33743             offset : this.shadowOffset
33744         });
33745     }else{
33746         this.shadowOffset = 0;
33747     }
33748     if(Roo.useShims && this.shim !== false){
33749         this.shim = this.el.createShim();
33750         this.shim.hide = this.hideAction;
33751         this.shim.hide();
33752     }else{
33753         this.shim = false;
33754     }
33755     if(this.autoTabs){
33756         this.initTabs();
33757     }
33758     if (this.buttons) { 
33759         var bts= this.buttons;
33760         this.buttons = [];
33761         Roo.each(bts, function(b) {
33762             this.addButton(b);
33763         }, this);
33764     }
33765     
33766     
33767     this.addEvents({
33768         /**
33769          * @event keydown
33770          * Fires when a key is pressed
33771          * @param {Roo.BasicDialog} this
33772          * @param {Roo.EventObject} e
33773          */
33774         "keydown" : true,
33775         /**
33776          * @event move
33777          * Fires when this dialog is moved by the user.
33778          * @param {Roo.BasicDialog} this
33779          * @param {Number} x The new page X
33780          * @param {Number} y The new page Y
33781          */
33782         "move" : true,
33783         /**
33784          * @event resize
33785          * Fires when this dialog is resized by the user.
33786          * @param {Roo.BasicDialog} this
33787          * @param {Number} width The new width
33788          * @param {Number} height The new height
33789          */
33790         "resize" : true,
33791         /**
33792          * @event beforehide
33793          * Fires before this dialog is hidden.
33794          * @param {Roo.BasicDialog} this
33795          */
33796         "beforehide" : true,
33797         /**
33798          * @event hide
33799          * Fires when this dialog is hidden.
33800          * @param {Roo.BasicDialog} this
33801          */
33802         "hide" : true,
33803         /**
33804          * @event beforeshow
33805          * Fires before this dialog is shown.
33806          * @param {Roo.BasicDialog} this
33807          */
33808         "beforeshow" : true,
33809         /**
33810          * @event show
33811          * Fires when this dialog is shown.
33812          * @param {Roo.BasicDialog} this
33813          */
33814         "show" : true
33815     });
33816     el.on("keydown", this.onKeyDown, this);
33817     el.on("mousedown", this.toFront, this);
33818     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33819     this.el.hide();
33820     Roo.DialogManager.register(this);
33821     Roo.BasicDialog.superclass.constructor.call(this);
33822 };
33823
33824 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33825     shadowOffset: Roo.isIE ? 6 : 5,
33826     minHeight: 80,
33827     minWidth: 200,
33828     minButtonWidth: 75,
33829     defaultButton: null,
33830     buttonAlign: "right",
33831     tabTag: 'div',
33832     firstShow: true,
33833
33834     /**
33835      * Sets the dialog title text
33836      * @param {String} text The title text to display
33837      * @return {Roo.BasicDialog} this
33838      */
33839     setTitle : function(text){
33840         this.header.update(text);
33841         return this;
33842     },
33843
33844     // private
33845     closeClick : function(){
33846         this.hide();
33847     },
33848
33849     // private
33850     collapseClick : function(){
33851         this[this.collapsed ? "expand" : "collapse"]();
33852     },
33853
33854     /**
33855      * Collapses the dialog to its minimized state (only the title bar is visible).
33856      * Equivalent to the user clicking the collapse dialog button.
33857      */
33858     collapse : function(){
33859         if(!this.collapsed){
33860             this.collapsed = true;
33861             this.el.addClass("x-dlg-collapsed");
33862             this.restoreHeight = this.el.getHeight();
33863             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33864         }
33865     },
33866
33867     /**
33868      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33869      * clicking the expand dialog button.
33870      */
33871     expand : function(){
33872         if(this.collapsed){
33873             this.collapsed = false;
33874             this.el.removeClass("x-dlg-collapsed");
33875             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33876         }
33877     },
33878
33879     /**
33880      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33881      * @return {Roo.TabPanel} The tabs component
33882      */
33883     initTabs : function(){
33884         var tabs = this.getTabs();
33885         while(tabs.getTab(0)){
33886             tabs.removeTab(0);
33887         }
33888         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33889             var dom = el.dom;
33890             tabs.addTab(Roo.id(dom), dom.title);
33891             dom.title = "";
33892         });
33893         tabs.activate(0);
33894         return tabs;
33895     },
33896
33897     // private
33898     beforeResize : function(){
33899         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33900     },
33901
33902     // private
33903     onResize : function(){
33904         this.refreshSize();
33905         this.syncBodyHeight();
33906         this.adjustAssets();
33907         this.focus();
33908         this.fireEvent("resize", this, this.size.width, this.size.height);
33909     },
33910
33911     // private
33912     onKeyDown : function(e){
33913         if(this.isVisible()){
33914             this.fireEvent("keydown", this, e);
33915         }
33916     },
33917
33918     /**
33919      * Resizes the dialog.
33920      * @param {Number} width
33921      * @param {Number} height
33922      * @return {Roo.BasicDialog} this
33923      */
33924     resizeTo : function(width, height){
33925         this.el.setSize(width, height);
33926         this.size = {width: width, height: height};
33927         this.syncBodyHeight();
33928         if(this.fixedcenter){
33929             this.center();
33930         }
33931         if(this.isVisible()){
33932             this.constrainXY();
33933             this.adjustAssets();
33934         }
33935         this.fireEvent("resize", this, width, height);
33936         return this;
33937     },
33938
33939
33940     /**
33941      * Resizes the dialog to fit the specified content size.
33942      * @param {Number} width
33943      * @param {Number} height
33944      * @return {Roo.BasicDialog} this
33945      */
33946     setContentSize : function(w, h){
33947         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33948         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33949         //if(!this.el.isBorderBox()){
33950             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33951             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33952         //}
33953         if(this.tabs){
33954             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33955             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33956         }
33957         this.resizeTo(w, h);
33958         return this;
33959     },
33960
33961     /**
33962      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33963      * executed in response to a particular key being pressed while the dialog is active.
33964      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33965      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33966      * @param {Function} fn The function to call
33967      * @param {Object} scope (optional) The scope of the function
33968      * @return {Roo.BasicDialog} this
33969      */
33970     addKeyListener : function(key, fn, scope){
33971         var keyCode, shift, ctrl, alt;
33972         if(typeof key == "object" && !(key instanceof Array)){
33973             keyCode = key["key"];
33974             shift = key["shift"];
33975             ctrl = key["ctrl"];
33976             alt = key["alt"];
33977         }else{
33978             keyCode = key;
33979         }
33980         var handler = function(dlg, e){
33981             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33982                 var k = e.getKey();
33983                 if(keyCode instanceof Array){
33984                     for(var i = 0, len = keyCode.length; i < len; i++){
33985                         if(keyCode[i] == k){
33986                           fn.call(scope || window, dlg, k, e);
33987                           return;
33988                         }
33989                     }
33990                 }else{
33991                     if(k == keyCode){
33992                         fn.call(scope || window, dlg, k, e);
33993                     }
33994                 }
33995             }
33996         };
33997         this.on("keydown", handler);
33998         return this;
33999     },
34000
34001     /**
34002      * Returns the TabPanel component (creates it if it doesn't exist).
34003      * Note: If you wish to simply check for the existence of tabs without creating them,
34004      * check for a null 'tabs' property.
34005      * @return {Roo.TabPanel} The tabs component
34006      */
34007     getTabs : function(){
34008         if(!this.tabs){
34009             this.el.addClass("x-dlg-auto-tabs");
34010             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
34011             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
34012         }
34013         return this.tabs;
34014     },
34015
34016     /**
34017      * Adds a button to the footer section of the dialog.
34018      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
34019      * object or a valid Roo.DomHelper element config
34020      * @param {Function} handler The function called when the button is clicked
34021      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
34022      * @return {Roo.Button} The new button
34023      */
34024     addButton : function(config, handler, scope){
34025         var dh = Roo.DomHelper;
34026         if(!this.footer){
34027             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
34028         }
34029         if(!this.btnContainer){
34030             var tb = this.footer.createChild({
34031
34032                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
34033                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
34034             }, null, true);
34035             this.btnContainer = tb.firstChild.firstChild.firstChild;
34036         }
34037         var bconfig = {
34038             handler: handler,
34039             scope: scope,
34040             minWidth: this.minButtonWidth,
34041             hideParent:true
34042         };
34043         if(typeof config == "string"){
34044             bconfig.text = config;
34045         }else{
34046             if(config.tag){
34047                 bconfig.dhconfig = config;
34048             }else{
34049                 Roo.apply(bconfig, config);
34050             }
34051         }
34052         var fc = false;
34053         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
34054             bconfig.position = Math.max(0, bconfig.position);
34055             fc = this.btnContainer.childNodes[bconfig.position];
34056         }
34057          
34058         var btn = new Roo.Button(
34059             fc ? 
34060                 this.btnContainer.insertBefore(document.createElement("td"),fc)
34061                 : this.btnContainer.appendChild(document.createElement("td")),
34062             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
34063             bconfig
34064         );
34065         this.syncBodyHeight();
34066         if(!this.buttons){
34067             /**
34068              * Array of all the buttons that have been added to this dialog via addButton
34069              * @type Array
34070              */
34071             this.buttons = [];
34072         }
34073         this.buttons.push(btn);
34074         return btn;
34075     },
34076
34077     /**
34078      * Sets the default button to be focused when the dialog is displayed.
34079      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
34080      * @return {Roo.BasicDialog} this
34081      */
34082     setDefaultButton : function(btn){
34083         this.defaultButton = btn;
34084         return this;
34085     },
34086
34087     // private
34088     getHeaderFooterHeight : function(safe){
34089         var height = 0;
34090         if(this.header){
34091            height += this.header.getHeight();
34092         }
34093         if(this.footer){
34094            var fm = this.footer.getMargins();
34095             height += (this.footer.getHeight()+fm.top+fm.bottom);
34096         }
34097         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
34098         height += this.centerBg.getPadding("tb");
34099         return height;
34100     },
34101
34102     // private
34103     syncBodyHeight : function()
34104     {
34105         var bd = this.body, // the text
34106             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
34107             bw = this.bwrap;
34108         var height = this.size.height - this.getHeaderFooterHeight(false);
34109         bd.setHeight(height-bd.getMargins("tb"));
34110         var hh = this.header.getHeight();
34111         var h = this.size.height-hh;
34112         cb.setHeight(h);
34113         
34114         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
34115         bw.setHeight(h-cb.getPadding("tb"));
34116         
34117         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
34118         bd.setWidth(bw.getWidth(true));
34119         if(this.tabs){
34120             this.tabs.syncHeight();
34121             if(Roo.isIE){
34122                 this.tabs.el.repaint();
34123             }
34124         }
34125     },
34126
34127     /**
34128      * Restores the previous state of the dialog if Roo.state is configured.
34129      * @return {Roo.BasicDialog} this
34130      */
34131     restoreState : function(){
34132         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
34133         if(box && box.width){
34134             this.xy = [box.x, box.y];
34135             this.resizeTo(box.width, box.height);
34136         }
34137         return this;
34138     },
34139
34140     // private
34141     beforeShow : function(){
34142         this.expand();
34143         if(this.fixedcenter){
34144             this.xy = this.el.getCenterXY(true);
34145         }
34146         if(this.modal){
34147             Roo.get(document.body).addClass("x-body-masked");
34148             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34149             this.mask.show();
34150         }
34151         this.constrainXY();
34152     },
34153
34154     // private
34155     animShow : function(){
34156         var b = Roo.get(this.animateTarget).getBox();
34157         this.proxy.setSize(b.width, b.height);
34158         this.proxy.setLocation(b.x, b.y);
34159         this.proxy.show();
34160         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
34161                     true, .35, this.showEl.createDelegate(this));
34162     },
34163
34164     /**
34165      * Shows the dialog.
34166      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
34167      * @return {Roo.BasicDialog} this
34168      */
34169     show : function(animateTarget){
34170         if (this.fireEvent("beforeshow", this) === false){
34171             return;
34172         }
34173         if(this.syncHeightBeforeShow){
34174             this.syncBodyHeight();
34175         }else if(this.firstShow){
34176             this.firstShow = false;
34177             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
34178         }
34179         this.animateTarget = animateTarget || this.animateTarget;
34180         if(!this.el.isVisible()){
34181             this.beforeShow();
34182             if(this.animateTarget && Roo.get(this.animateTarget)){
34183                 this.animShow();
34184             }else{
34185                 this.showEl();
34186             }
34187         }
34188         return this;
34189     },
34190
34191     // private
34192     showEl : function(){
34193         this.proxy.hide();
34194         this.el.setXY(this.xy);
34195         this.el.show();
34196         this.adjustAssets(true);
34197         this.toFront();
34198         this.focus();
34199         // IE peekaboo bug - fix found by Dave Fenwick
34200         if(Roo.isIE){
34201             this.el.repaint();
34202         }
34203         this.fireEvent("show", this);
34204     },
34205
34206     /**
34207      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
34208      * dialog itself will receive focus.
34209      */
34210     focus : function(){
34211         if(this.defaultButton){
34212             this.defaultButton.focus();
34213         }else{
34214             this.focusEl.focus();
34215         }
34216     },
34217
34218     // private
34219     constrainXY : function(){
34220         if(this.constraintoviewport !== false){
34221             if(!this.viewSize){
34222                 if(this.container){
34223                     var s = this.container.getSize();
34224                     this.viewSize = [s.width, s.height];
34225                 }else{
34226                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
34227                 }
34228             }
34229             var s = Roo.get(this.container||document).getScroll();
34230
34231             var x = this.xy[0], y = this.xy[1];
34232             var w = this.size.width, h = this.size.height;
34233             var vw = this.viewSize[0], vh = this.viewSize[1];
34234             // only move it if it needs it
34235             var moved = false;
34236             // first validate right/bottom
34237             if(x + w > vw+s.left){
34238                 x = vw - w;
34239                 moved = true;
34240             }
34241             if(y + h > vh+s.top){
34242                 y = vh - h;
34243                 moved = true;
34244             }
34245             // then make sure top/left isn't negative
34246             if(x < s.left){
34247                 x = s.left;
34248                 moved = true;
34249             }
34250             if(y < s.top){
34251                 y = s.top;
34252                 moved = true;
34253             }
34254             if(moved){
34255                 // cache xy
34256                 this.xy = [x, y];
34257                 if(this.isVisible()){
34258                     this.el.setLocation(x, y);
34259                     this.adjustAssets();
34260                 }
34261             }
34262         }
34263     },
34264
34265     // private
34266     onDrag : function(){
34267         if(!this.proxyDrag){
34268             this.xy = this.el.getXY();
34269             this.adjustAssets();
34270         }
34271     },
34272
34273     // private
34274     adjustAssets : function(doShow){
34275         var x = this.xy[0], y = this.xy[1];
34276         var w = this.size.width, h = this.size.height;
34277         if(doShow === true){
34278             if(this.shadow){
34279                 this.shadow.show(this.el);
34280             }
34281             if(this.shim){
34282                 this.shim.show();
34283             }
34284         }
34285         if(this.shadow && this.shadow.isVisible()){
34286             this.shadow.show(this.el);
34287         }
34288         if(this.shim && this.shim.isVisible()){
34289             this.shim.setBounds(x, y, w, h);
34290         }
34291     },
34292
34293     // private
34294     adjustViewport : function(w, h){
34295         if(!w || !h){
34296             w = Roo.lib.Dom.getViewWidth();
34297             h = Roo.lib.Dom.getViewHeight();
34298         }
34299         // cache the size
34300         this.viewSize = [w, h];
34301         if(this.modal && this.mask.isVisible()){
34302             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34303             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34304         }
34305         if(this.isVisible()){
34306             this.constrainXY();
34307         }
34308     },
34309
34310     /**
34311      * Destroys this dialog and all its supporting elements (including any tabs, shim,
34312      * shadow, proxy, mask, etc.)  Also removes all event listeners.
34313      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34314      */
34315     destroy : function(removeEl){
34316         if(this.isVisible()){
34317             this.animateTarget = null;
34318             this.hide();
34319         }
34320         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34321         if(this.tabs){
34322             this.tabs.destroy(removeEl);
34323         }
34324         Roo.destroy(
34325              this.shim,
34326              this.proxy,
34327              this.resizer,
34328              this.close,
34329              this.mask
34330         );
34331         if(this.dd){
34332             this.dd.unreg();
34333         }
34334         if(this.buttons){
34335            for(var i = 0, len = this.buttons.length; i < len; i++){
34336                this.buttons[i].destroy();
34337            }
34338         }
34339         this.el.removeAllListeners();
34340         if(removeEl === true){
34341             this.el.update("");
34342             this.el.remove();
34343         }
34344         Roo.DialogManager.unregister(this);
34345     },
34346
34347     // private
34348     startMove : function(){
34349         if(this.proxyDrag){
34350             this.proxy.show();
34351         }
34352         if(this.constraintoviewport !== false){
34353             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34354         }
34355     },
34356
34357     // private
34358     endMove : function(){
34359         if(!this.proxyDrag){
34360             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34361         }else{
34362             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34363             this.proxy.hide();
34364         }
34365         this.refreshSize();
34366         this.adjustAssets();
34367         this.focus();
34368         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34369     },
34370
34371     /**
34372      * Brings this dialog to the front of any other visible dialogs
34373      * @return {Roo.BasicDialog} this
34374      */
34375     toFront : function(){
34376         Roo.DialogManager.bringToFront(this);
34377         return this;
34378     },
34379
34380     /**
34381      * Sends this dialog to the back (under) of any other visible dialogs
34382      * @return {Roo.BasicDialog} this
34383      */
34384     toBack : function(){
34385         Roo.DialogManager.sendToBack(this);
34386         return this;
34387     },
34388
34389     /**
34390      * Centers this dialog in the viewport
34391      * @return {Roo.BasicDialog} this
34392      */
34393     center : function(){
34394         var xy = this.el.getCenterXY(true);
34395         this.moveTo(xy[0], xy[1]);
34396         return this;
34397     },
34398
34399     /**
34400      * Moves the dialog's top-left corner to the specified point
34401      * @param {Number} x
34402      * @param {Number} y
34403      * @return {Roo.BasicDialog} this
34404      */
34405     moveTo : function(x, y){
34406         this.xy = [x,y];
34407         if(this.isVisible()){
34408             this.el.setXY(this.xy);
34409             this.adjustAssets();
34410         }
34411         return this;
34412     },
34413
34414     /**
34415      * Aligns the dialog to the specified element
34416      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34417      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34418      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34419      * @return {Roo.BasicDialog} this
34420      */
34421     alignTo : function(element, position, offsets){
34422         this.xy = this.el.getAlignToXY(element, position, offsets);
34423         if(this.isVisible()){
34424             this.el.setXY(this.xy);
34425             this.adjustAssets();
34426         }
34427         return this;
34428     },
34429
34430     /**
34431      * Anchors an element to another element and realigns it when the window is resized.
34432      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34433      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34434      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34435      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34436      * is a number, it is used as the buffer delay (defaults to 50ms).
34437      * @return {Roo.BasicDialog} this
34438      */
34439     anchorTo : function(el, alignment, offsets, monitorScroll){
34440         var action = function(){
34441             this.alignTo(el, alignment, offsets);
34442         };
34443         Roo.EventManager.onWindowResize(action, this);
34444         var tm = typeof monitorScroll;
34445         if(tm != 'undefined'){
34446             Roo.EventManager.on(window, 'scroll', action, this,
34447                 {buffer: tm == 'number' ? monitorScroll : 50});
34448         }
34449         action.call(this);
34450         return this;
34451     },
34452
34453     /**
34454      * Returns true if the dialog is visible
34455      * @return {Boolean}
34456      */
34457     isVisible : function(){
34458         return this.el.isVisible();
34459     },
34460
34461     // private
34462     animHide : function(callback){
34463         var b = Roo.get(this.animateTarget).getBox();
34464         this.proxy.show();
34465         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34466         this.el.hide();
34467         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34468                     this.hideEl.createDelegate(this, [callback]));
34469     },
34470
34471     /**
34472      * Hides the dialog.
34473      * @param {Function} callback (optional) Function to call when the dialog is hidden
34474      * @return {Roo.BasicDialog} this
34475      */
34476     hide : function(callback){
34477         if (this.fireEvent("beforehide", this) === false){
34478             return;
34479         }
34480         if(this.shadow){
34481             this.shadow.hide();
34482         }
34483         if(this.shim) {
34484           this.shim.hide();
34485         }
34486         // sometimes animateTarget seems to get set.. causing problems...
34487         // this just double checks..
34488         if(this.animateTarget && Roo.get(this.animateTarget)) {
34489            this.animHide(callback);
34490         }else{
34491             this.el.hide();
34492             this.hideEl(callback);
34493         }
34494         return this;
34495     },
34496
34497     // private
34498     hideEl : function(callback){
34499         this.proxy.hide();
34500         if(this.modal){
34501             this.mask.hide();
34502             Roo.get(document.body).removeClass("x-body-masked");
34503         }
34504         this.fireEvent("hide", this);
34505         if(typeof callback == "function"){
34506             callback();
34507         }
34508     },
34509
34510     // private
34511     hideAction : function(){
34512         this.setLeft("-10000px");
34513         this.setTop("-10000px");
34514         this.setStyle("visibility", "hidden");
34515     },
34516
34517     // private
34518     refreshSize : function(){
34519         this.size = this.el.getSize();
34520         this.xy = this.el.getXY();
34521         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34522     },
34523
34524     // private
34525     // z-index is managed by the DialogManager and may be overwritten at any time
34526     setZIndex : function(index){
34527         if(this.modal){
34528             this.mask.setStyle("z-index", index);
34529         }
34530         if(this.shim){
34531             this.shim.setStyle("z-index", ++index);
34532         }
34533         if(this.shadow){
34534             this.shadow.setZIndex(++index);
34535         }
34536         this.el.setStyle("z-index", ++index);
34537         if(this.proxy){
34538             this.proxy.setStyle("z-index", ++index);
34539         }
34540         if(this.resizer){
34541             this.resizer.proxy.setStyle("z-index", ++index);
34542         }
34543
34544         this.lastZIndex = index;
34545     },
34546
34547     /**
34548      * Returns the element for this dialog
34549      * @return {Roo.Element} The underlying dialog Element
34550      */
34551     getEl : function(){
34552         return this.el;
34553     }
34554 });
34555
34556 /**
34557  * @class Roo.DialogManager
34558  * Provides global access to BasicDialogs that have been created and
34559  * support for z-indexing (layering) multiple open dialogs.
34560  */
34561 Roo.DialogManager = function(){
34562     var list = {};
34563     var accessList = [];
34564     var front = null;
34565
34566     // private
34567     var sortDialogs = function(d1, d2){
34568         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34569     };
34570
34571     // private
34572     var orderDialogs = function(){
34573         accessList.sort(sortDialogs);
34574         var seed = Roo.DialogManager.zseed;
34575         for(var i = 0, len = accessList.length; i < len; i++){
34576             var dlg = accessList[i];
34577             if(dlg){
34578                 dlg.setZIndex(seed + (i*10));
34579             }
34580         }
34581     };
34582
34583     return {
34584         /**
34585          * The starting z-index for BasicDialogs (defaults to 9000)
34586          * @type Number The z-index value
34587          */
34588         zseed : 9000,
34589
34590         // private
34591         register : function(dlg){
34592             list[dlg.id] = dlg;
34593             accessList.push(dlg);
34594         },
34595
34596         // private
34597         unregister : function(dlg){
34598             delete list[dlg.id];
34599             var i=0;
34600             var len=0;
34601             if(!accessList.indexOf){
34602                 for(  i = 0, len = accessList.length; i < len; i++){
34603                     if(accessList[i] == dlg){
34604                         accessList.splice(i, 1);
34605                         return;
34606                     }
34607                 }
34608             }else{
34609                  i = accessList.indexOf(dlg);
34610                 if(i != -1){
34611                     accessList.splice(i, 1);
34612                 }
34613             }
34614         },
34615
34616         /**
34617          * Gets a registered dialog by id
34618          * @param {String/Object} id The id of the dialog or a dialog
34619          * @return {Roo.BasicDialog} this
34620          */
34621         get : function(id){
34622             return typeof id == "object" ? id : list[id];
34623         },
34624
34625         /**
34626          * Brings the specified dialog to the front
34627          * @param {String/Object} dlg The id of the dialog or a dialog
34628          * @return {Roo.BasicDialog} this
34629          */
34630         bringToFront : function(dlg){
34631             dlg = this.get(dlg);
34632             if(dlg != front){
34633                 front = dlg;
34634                 dlg._lastAccess = new Date().getTime();
34635                 orderDialogs();
34636             }
34637             return dlg;
34638         },
34639
34640         /**
34641          * Sends the specified dialog to the back
34642          * @param {String/Object} dlg The id of the dialog or a dialog
34643          * @return {Roo.BasicDialog} this
34644          */
34645         sendToBack : function(dlg){
34646             dlg = this.get(dlg);
34647             dlg._lastAccess = -(new Date().getTime());
34648             orderDialogs();
34649             return dlg;
34650         },
34651
34652         /**
34653          * Hides all dialogs
34654          */
34655         hideAll : function(){
34656             for(var id in list){
34657                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34658                     list[id].hide();
34659                 }
34660             }
34661         }
34662     };
34663 }();
34664
34665 /**
34666  * @class Roo.LayoutDialog
34667  * @extends Roo.BasicDialog
34668  * @children Roo.ContentPanel
34669  * @parent builder none
34670  * Dialog which provides adjustments for working with a layout in a Dialog.
34671  * Add your necessary layout config options to the dialog's config.<br>
34672  * Example usage (including a nested layout):
34673  * <pre><code>
34674 if(!dialog){
34675     dialog = new Roo.LayoutDialog("download-dlg", {
34676         modal: true,
34677         width:600,
34678         height:450,
34679         shadow:true,
34680         minWidth:500,
34681         minHeight:350,
34682         autoTabs:true,
34683         proxyDrag:true,
34684         // layout config merges with the dialog config
34685         center:{
34686             tabPosition: "top",
34687             alwaysShowTabs: true
34688         }
34689     });
34690     dialog.addKeyListener(27, dialog.hide, dialog);
34691     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34692     dialog.addButton("Build It!", this.getDownload, this);
34693
34694     // we can even add nested layouts
34695     var innerLayout = new Roo.BorderLayout("dl-inner", {
34696         east: {
34697             initialSize: 200,
34698             autoScroll:true,
34699             split:true
34700         },
34701         center: {
34702             autoScroll:true
34703         }
34704     });
34705     innerLayout.beginUpdate();
34706     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34707     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34708     innerLayout.endUpdate(true);
34709
34710     var layout = dialog.getLayout();
34711     layout.beginUpdate();
34712     layout.add("center", new Roo.ContentPanel("standard-panel",
34713                         {title: "Download the Source", fitToFrame:true}));
34714     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34715                {title: "Build your own roo.js"}));
34716     layout.getRegion("center").showPanel(sp);
34717     layout.endUpdate();
34718 }
34719 </code></pre>
34720     * @constructor
34721     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34722     * @param {Object} config configuration options
34723   */
34724 Roo.LayoutDialog = function(el, cfg){
34725     
34726     var config=  cfg;
34727     if (typeof(cfg) == 'undefined') {
34728         config = Roo.apply({}, el);
34729         // not sure why we use documentElement here.. - it should always be body.
34730         // IE7 borks horribly if we use documentElement.
34731         // webkit also does not like documentElement - it creates a body element...
34732         el = Roo.get( document.body || document.documentElement ).createChild();
34733         //config.autoCreate = true;
34734     }
34735     
34736     
34737     config.autoTabs = false;
34738     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34739     this.body.setStyle({overflow:"hidden", position:"relative"});
34740     this.layout = new Roo.BorderLayout(this.body.dom, config);
34741     this.layout.monitorWindowResize = false;
34742     this.el.addClass("x-dlg-auto-layout");
34743     // fix case when center region overwrites center function
34744     this.center = Roo.BasicDialog.prototype.center;
34745     this.on("show", this.layout.layout, this.layout, true);
34746     if (config.items) {
34747         var xitems = config.items;
34748         delete config.items;
34749         Roo.each(xitems, this.addxtype, this);
34750     }
34751     
34752     
34753 };
34754 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34755     
34756     
34757     /**
34758      * @cfg {Roo.LayoutRegion} east  
34759      */
34760     /**
34761      * @cfg {Roo.LayoutRegion} west
34762      */
34763     /**
34764      * @cfg {Roo.LayoutRegion} south
34765      */
34766     /**
34767      * @cfg {Roo.LayoutRegion} north
34768      */
34769     /**
34770      * @cfg {Roo.LayoutRegion} center
34771      */
34772     /**
34773      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34774      */
34775     
34776     
34777     /**
34778      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34779      * @deprecated
34780      */
34781     endUpdate : function(){
34782         this.layout.endUpdate();
34783     },
34784
34785     /**
34786      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34787      *  @deprecated
34788      */
34789     beginUpdate : function(){
34790         this.layout.beginUpdate();
34791     },
34792
34793     /**
34794      * Get the BorderLayout for this dialog
34795      * @return {Roo.BorderLayout}
34796      */
34797     getLayout : function(){
34798         return this.layout;
34799     },
34800
34801     showEl : function(){
34802         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34803         if(Roo.isIE7){
34804             this.layout.layout();
34805         }
34806     },
34807
34808     // private
34809     // Use the syncHeightBeforeShow config option to control this automatically
34810     syncBodyHeight : function(){
34811         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34812         if(this.layout){this.layout.layout();}
34813     },
34814     
34815       /**
34816      * Add an xtype element (actually adds to the layout.)
34817      * @return {Object} xdata xtype object data.
34818      */
34819     
34820     addxtype : function(c) {
34821         return this.layout.addxtype(c);
34822     }
34823 });/*
34824  * Based on:
34825  * Ext JS Library 1.1.1
34826  * Copyright(c) 2006-2007, Ext JS, LLC.
34827  *
34828  * Originally Released Under LGPL - original licence link has changed is not relivant.
34829  *
34830  * Fork - LGPL
34831  * <script type="text/javascript">
34832  */
34833  
34834 /**
34835  * @class Roo.MessageBox
34836  * @static
34837  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34838  * Example usage:
34839  *<pre><code>
34840 // Basic alert:
34841 Roo.Msg.alert('Status', 'Changes saved successfully.');
34842
34843 // Prompt for user data:
34844 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34845     if (btn == 'ok'){
34846         // process text value...
34847     }
34848 });
34849
34850 // Show a dialog using config options:
34851 Roo.Msg.show({
34852    title:'Save Changes?',
34853    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34854    buttons: Roo.Msg.YESNOCANCEL,
34855    fn: processResult,
34856    animEl: 'elId'
34857 });
34858 </code></pre>
34859  * @static
34860  */
34861 Roo.MessageBox = function(){
34862     var dlg, opt, mask, waitTimer;
34863     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34864     var buttons, activeTextEl, bwidth;
34865
34866     // private
34867     var handleButton = function(button){
34868         dlg.hide();
34869         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34870     };
34871
34872     // private
34873     var handleHide = function(){
34874         if(opt && opt.cls){
34875             dlg.el.removeClass(opt.cls);
34876         }
34877         if(waitTimer){
34878             Roo.TaskMgr.stop(waitTimer);
34879             waitTimer = null;
34880         }
34881     };
34882
34883     // private
34884     var updateButtons = function(b){
34885         var width = 0;
34886         if(!b){
34887             buttons["ok"].hide();
34888             buttons["cancel"].hide();
34889             buttons["yes"].hide();
34890             buttons["no"].hide();
34891             dlg.footer.dom.style.display = 'none';
34892             return width;
34893         }
34894         dlg.footer.dom.style.display = '';
34895         for(var k in buttons){
34896             if(typeof buttons[k] != "function"){
34897                 if(b[k]){
34898                     buttons[k].show();
34899                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34900                     width += buttons[k].el.getWidth()+15;
34901                 }else{
34902                     buttons[k].hide();
34903                 }
34904             }
34905         }
34906         return width;
34907     };
34908
34909     // private
34910     var handleEsc = function(d, k, e){
34911         if(opt && opt.closable !== false){
34912             dlg.hide();
34913         }
34914         if(e){
34915             e.stopEvent();
34916         }
34917     };
34918
34919     return {
34920         /**
34921          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34922          * @return {Roo.BasicDialog} The BasicDialog element
34923          */
34924         getDialog : function(){
34925            if(!dlg){
34926                 dlg = new Roo.BasicDialog("x-msg-box", {
34927                     autoCreate : true,
34928                     shadow: true,
34929                     draggable: true,
34930                     resizable:false,
34931                     constraintoviewport:false,
34932                     fixedcenter:true,
34933                     collapsible : false,
34934                     shim:true,
34935                     modal: true,
34936                     width:400, height:100,
34937                     buttonAlign:"center",
34938                     closeClick : function(){
34939                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34940                             handleButton("no");
34941                         }else{
34942                             handleButton("cancel");
34943                         }
34944                     }
34945                 });
34946               
34947                 dlg.on("hide", handleHide);
34948                 mask = dlg.mask;
34949                 dlg.addKeyListener(27, handleEsc);
34950                 buttons = {};
34951                 var bt = this.buttonText;
34952                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34953                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34954                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34955                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34956                 bodyEl = dlg.body.createChild({
34957
34958                     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>'
34959                 });
34960                 msgEl = bodyEl.dom.firstChild;
34961                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34962                 textboxEl.enableDisplayMode();
34963                 textboxEl.addKeyListener([10,13], function(){
34964                     if(dlg.isVisible() && opt && opt.buttons){
34965                         if(opt.buttons.ok){
34966                             handleButton("ok");
34967                         }else if(opt.buttons.yes){
34968                             handleButton("yes");
34969                         }
34970                     }
34971                 });
34972                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34973                 textareaEl.enableDisplayMode();
34974                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34975                 progressEl.enableDisplayMode();
34976                 var pf = progressEl.dom.firstChild;
34977                 if (pf) {
34978                     pp = Roo.get(pf.firstChild);
34979                     pp.setHeight(pf.offsetHeight);
34980                 }
34981                 
34982             }
34983             return dlg;
34984         },
34985
34986         /**
34987          * Updates the message box body text
34988          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34989          * the XHTML-compliant non-breaking space character '&amp;#160;')
34990          * @return {Roo.MessageBox} This message box
34991          */
34992         updateText : function(text){
34993             if(!dlg.isVisible() && !opt.width){
34994                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34995             }
34996             msgEl.innerHTML = text || '&#160;';
34997       
34998             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34999             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
35000             var w = Math.max(
35001                     Math.min(opt.width || cw , this.maxWidth), 
35002                     Math.max(opt.minWidth || this.minWidth, bwidth)
35003             );
35004             if(opt.prompt){
35005                 activeTextEl.setWidth(w);
35006             }
35007             if(dlg.isVisible()){
35008                 dlg.fixedcenter = false;
35009             }
35010             // to big, make it scroll. = But as usual stupid IE does not support
35011             // !important..
35012             
35013             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
35014                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
35015                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
35016             } else {
35017                 bodyEl.dom.style.height = '';
35018                 bodyEl.dom.style.overflowY = '';
35019             }
35020             if (cw > w) {
35021                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
35022             } else {
35023                 bodyEl.dom.style.overflowX = '';
35024             }
35025             
35026             dlg.setContentSize(w, bodyEl.getHeight());
35027             if(dlg.isVisible()){
35028                 dlg.fixedcenter = true;
35029             }
35030             return this;
35031         },
35032
35033         /**
35034          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
35035          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
35036          * @param {Number} value Any number between 0 and 1 (e.g., .5)
35037          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
35038          * @return {Roo.MessageBox} This message box
35039          */
35040         updateProgress : function(value, text){
35041             if(text){
35042                 this.updateText(text);
35043             }
35044             if (pp) { // weird bug on my firefox - for some reason this is not defined
35045                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
35046             }
35047             return this;
35048         },        
35049
35050         /**
35051          * Returns true if the message box is currently displayed
35052          * @return {Boolean} True if the message box is visible, else false
35053          */
35054         isVisible : function(){
35055             return dlg && dlg.isVisible();  
35056         },
35057
35058         /**
35059          * Hides the message box if it is displayed
35060          */
35061         hide : function(){
35062             if(this.isVisible()){
35063                 dlg.hide();
35064             }  
35065         },
35066
35067         /**
35068          * Displays a new message box, or reinitializes an existing message box, based on the config options
35069          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
35070          * The following config object properties are supported:
35071          * <pre>
35072 Property    Type             Description
35073 ----------  ---------------  ------------------------------------------------------------------------------------
35074 animEl            String/Element   An id or Element from which the message box should animate as it opens and
35075                                    closes (defaults to undefined)
35076 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
35077                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
35078 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
35079                                    progress and wait dialogs will ignore this property and always hide the
35080                                    close button as they can only be closed programmatically.
35081 cls               String           A custom CSS class to apply to the message box element
35082 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
35083                                    displayed (defaults to 75)
35084 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
35085                                    function will be btn (the name of the button that was clicked, if applicable,
35086                                    e.g. "ok"), and text (the value of the active text field, if applicable).
35087                                    Progress and wait dialogs will ignore this option since they do not respond to
35088                                    user actions and can only be closed programmatically, so any required function
35089                                    should be called by the same code after it closes the dialog.
35090 icon              String           A CSS class that provides a background image to be used as an icon for
35091                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
35092 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
35093 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
35094 modal             Boolean          False to allow user interaction with the page while the message box is
35095                                    displayed (defaults to true)
35096 msg               String           A string that will replace the existing message box body text (defaults
35097                                    to the XHTML-compliant non-breaking space character '&#160;')
35098 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
35099 progress          Boolean          True to display a progress bar (defaults to false)
35100 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
35101 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
35102 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
35103 title             String           The title text
35104 value             String           The string value to set into the active textbox element if displayed
35105 wait              Boolean          True to display a progress bar (defaults to false)
35106 width             Number           The width of the dialog in pixels
35107 </pre>
35108          *
35109          * Example usage:
35110          * <pre><code>
35111 Roo.Msg.show({
35112    title: 'Address',
35113    msg: 'Please enter your address:',
35114    width: 300,
35115    buttons: Roo.MessageBox.OKCANCEL,
35116    multiline: true,
35117    fn: saveAddress,
35118    animEl: 'addAddressBtn'
35119 });
35120 </code></pre>
35121          * @param {Object} config Configuration options
35122          * @return {Roo.MessageBox} This message box
35123          */
35124         show : function(options)
35125         {
35126             
35127             // this causes nightmares if you show one dialog after another
35128             // especially on callbacks..
35129              
35130             if(this.isVisible()){
35131                 
35132                 this.hide();
35133                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
35134                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
35135                 Roo.log("New Dialog Message:" +  options.msg )
35136                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
35137                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
35138                 
35139             }
35140             var d = this.getDialog();
35141             opt = options;
35142             d.setTitle(opt.title || "&#160;");
35143             d.close.setDisplayed(opt.closable !== false);
35144             activeTextEl = textboxEl;
35145             opt.prompt = opt.prompt || (opt.multiline ? true : false);
35146             if(opt.prompt){
35147                 if(opt.multiline){
35148                     textboxEl.hide();
35149                     textareaEl.show();
35150                     textareaEl.setHeight(typeof opt.multiline == "number" ?
35151                         opt.multiline : this.defaultTextHeight);
35152                     activeTextEl = textareaEl;
35153                 }else{
35154                     textboxEl.show();
35155                     textareaEl.hide();
35156                 }
35157             }else{
35158                 textboxEl.hide();
35159                 textareaEl.hide();
35160             }
35161             progressEl.setDisplayed(opt.progress === true);
35162             this.updateProgress(0);
35163             activeTextEl.dom.value = opt.value || "";
35164             if(opt.prompt){
35165                 dlg.setDefaultButton(activeTextEl);
35166             }else{
35167                 var bs = opt.buttons;
35168                 var db = null;
35169                 if(bs && bs.ok){
35170                     db = buttons["ok"];
35171                 }else if(bs && bs.yes){
35172                     db = buttons["yes"];
35173                 }
35174                 dlg.setDefaultButton(db);
35175             }
35176             bwidth = updateButtons(opt.buttons);
35177             this.updateText(opt.msg);
35178             if(opt.cls){
35179                 d.el.addClass(opt.cls);
35180             }
35181             d.proxyDrag = opt.proxyDrag === true;
35182             d.modal = opt.modal !== false;
35183             d.mask = opt.modal !== false ? mask : false;
35184             if(!d.isVisible()){
35185                 // force it to the end of the z-index stack so it gets a cursor in FF
35186                 document.body.appendChild(dlg.el.dom);
35187                 d.animateTarget = null;
35188                 d.show(options.animEl);
35189             }
35190             dlg.toFront();
35191             return this;
35192         },
35193
35194         /**
35195          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
35196          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
35197          * and closing the message box when the process is complete.
35198          * @param {String} title The title bar text
35199          * @param {String} msg The message box body text
35200          * @return {Roo.MessageBox} This message box
35201          */
35202         progress : function(title, msg){
35203             this.show({
35204                 title : title,
35205                 msg : msg,
35206                 buttons: false,
35207                 progress:true,
35208                 closable:false,
35209                 minWidth: this.minProgressWidth,
35210                 modal : true
35211             });
35212             return this;
35213         },
35214
35215         /**
35216          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
35217          * If a callback function is passed it will be called after the user clicks the button, and the
35218          * id of the button that was clicked will be passed as the only parameter to the callback
35219          * (could also be the top-right close button).
35220          * @param {String} title The title bar text
35221          * @param {String} msg The message box body text
35222          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35223          * @param {Object} scope (optional) The scope of the callback function
35224          * @return {Roo.MessageBox} This message box
35225          */
35226         alert : function(title, msg, fn, scope){
35227             this.show({
35228                 title : title,
35229                 msg : msg,
35230                 buttons: this.OK,
35231                 fn: fn,
35232                 scope : scope,
35233                 modal : true
35234             });
35235             return this;
35236         },
35237
35238         /**
35239          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
35240          * interaction while waiting for a long-running process to complete that does not have defined intervals.
35241          * You are responsible for closing the message box when the process is complete.
35242          * @param {String} msg The message box body text
35243          * @param {String} title (optional) The title bar text
35244          * @return {Roo.MessageBox} This message box
35245          */
35246         wait : function(msg, title){
35247             this.show({
35248                 title : title,
35249                 msg : msg,
35250                 buttons: false,
35251                 closable:false,
35252                 progress:true,
35253                 modal:true,
35254                 width:300,
35255                 wait:true
35256             });
35257             waitTimer = Roo.TaskMgr.start({
35258                 run: function(i){
35259                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35260                 },
35261                 interval: 1000
35262             });
35263             return this;
35264         },
35265
35266         /**
35267          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35268          * If a callback function is passed it will be called after the user clicks either button, and the id of the
35269          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35270          * @param {String} title The title bar text
35271          * @param {String} msg The message box body text
35272          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35273          * @param {Object} scope (optional) The scope of the callback function
35274          * @return {Roo.MessageBox} This message box
35275          */
35276         confirm : function(title, msg, fn, scope){
35277             this.show({
35278                 title : title,
35279                 msg : msg,
35280                 buttons: this.YESNO,
35281                 fn: fn,
35282                 scope : scope,
35283                 modal : true
35284             });
35285             return this;
35286         },
35287
35288         /**
35289          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35290          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
35291          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35292          * (could also be the top-right close button) and the text that was entered will be passed as the two
35293          * parameters to the callback.
35294          * @param {String} title The title bar text
35295          * @param {String} msg The message box body text
35296          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35297          * @param {Object} scope (optional) The scope of the callback function
35298          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35299          * property, or the height in pixels to create the textbox (defaults to false / single-line)
35300          * @return {Roo.MessageBox} This message box
35301          */
35302         prompt : function(title, msg, fn, scope, multiline){
35303             this.show({
35304                 title : title,
35305                 msg : msg,
35306                 buttons: this.OKCANCEL,
35307                 fn: fn,
35308                 minWidth:250,
35309                 scope : scope,
35310                 prompt:true,
35311                 multiline: multiline,
35312                 modal : true
35313             });
35314             return this;
35315         },
35316
35317         /**
35318          * Button config that displays a single OK button
35319          * @type Object
35320          */
35321         OK : {ok:true},
35322         /**
35323          * Button config that displays Yes and No buttons
35324          * @type Object
35325          */
35326         YESNO : {yes:true, no:true},
35327         /**
35328          * Button config that displays OK and Cancel buttons
35329          * @type Object
35330          */
35331         OKCANCEL : {ok:true, cancel:true},
35332         /**
35333          * Button config that displays Yes, No and Cancel buttons
35334          * @type Object
35335          */
35336         YESNOCANCEL : {yes:true, no:true, cancel:true},
35337
35338         /**
35339          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35340          * @type Number
35341          */
35342         defaultTextHeight : 75,
35343         /**
35344          * The maximum width in pixels of the message box (defaults to 600)
35345          * @type Number
35346          */
35347         maxWidth : 600,
35348         /**
35349          * The minimum width in pixels of the message box (defaults to 100)
35350          * @type Number
35351          */
35352         minWidth : 100,
35353         /**
35354          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
35355          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35356          * @type Number
35357          */
35358         minProgressWidth : 250,
35359         /**
35360          * An object containing the default button text strings that can be overriden for localized language support.
35361          * Supported properties are: ok, cancel, yes and no.
35362          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35363          * @type Object
35364          */
35365         buttonText : {
35366             ok : "OK",
35367             cancel : "Cancel",
35368             yes : "Yes",
35369             no : "No"
35370         }
35371     };
35372 }();
35373
35374 /**
35375  * Shorthand for {@link Roo.MessageBox}
35376  */
35377 Roo.Msg = Roo.MessageBox;/*
35378  * Based on:
35379  * Ext JS Library 1.1.1
35380  * Copyright(c) 2006-2007, Ext JS, LLC.
35381  *
35382  * Originally Released Under LGPL - original licence link has changed is not relivant.
35383  *
35384  * Fork - LGPL
35385  * <script type="text/javascript">
35386  */
35387 /**
35388  * @class Roo.QuickTips
35389  * Provides attractive and customizable tooltips for any element.
35390  * @static
35391  */
35392 Roo.QuickTips = function(){
35393     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35394     var ce, bd, xy, dd;
35395     var visible = false, disabled = true, inited = false;
35396     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35397     
35398     var onOver = function(e){
35399         if(disabled){
35400             return;
35401         }
35402         var t = e.getTarget();
35403         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35404             return;
35405         }
35406         if(ce && t == ce.el){
35407             clearTimeout(hideProc);
35408             return;
35409         }
35410         if(t && tagEls[t.id]){
35411             tagEls[t.id].el = t;
35412             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35413             return;
35414         }
35415         var ttp, et = Roo.fly(t);
35416         var ns = cfg.namespace;
35417         if(tm.interceptTitles && t.title){
35418             ttp = t.title;
35419             t.qtip = ttp;
35420             t.removeAttribute("title");
35421             e.preventDefault();
35422         }else{
35423             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35424         }
35425         if(ttp){
35426             showProc = show.defer(tm.showDelay, tm, [{
35427                 el: t, 
35428                 text: ttp.replace(/\\n/g,'<br/>'),
35429                 width: et.getAttributeNS(ns, cfg.width),
35430                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35431                 title: et.getAttributeNS(ns, cfg.title),
35432                     cls: et.getAttributeNS(ns, cfg.cls)
35433             }]);
35434         }
35435     };
35436     
35437     var onOut = function(e){
35438         clearTimeout(showProc);
35439         var t = e.getTarget();
35440         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35441             hideProc = setTimeout(hide, tm.hideDelay);
35442         }
35443     };
35444     
35445     var onMove = function(e){
35446         if(disabled){
35447             return;
35448         }
35449         xy = e.getXY();
35450         xy[1] += 18;
35451         if(tm.trackMouse && ce){
35452             el.setXY(xy);
35453         }
35454     };
35455     
35456     var onDown = function(e){
35457         clearTimeout(showProc);
35458         clearTimeout(hideProc);
35459         if(!e.within(el)){
35460             if(tm.hideOnClick){
35461                 hide();
35462                 tm.disable();
35463                 tm.enable.defer(100, tm);
35464             }
35465         }
35466     };
35467     
35468     var getPad = function(){
35469         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35470     };
35471
35472     var show = function(o){
35473         if(disabled){
35474             return;
35475         }
35476         clearTimeout(dismissProc);
35477         ce = o;
35478         if(removeCls){ // in case manually hidden
35479             el.removeClass(removeCls);
35480             removeCls = null;
35481         }
35482         if(ce.cls){
35483             el.addClass(ce.cls);
35484             removeCls = ce.cls;
35485         }
35486         if(ce.title){
35487             tipTitle.update(ce.title);
35488             tipTitle.show();
35489         }else{
35490             tipTitle.update('');
35491             tipTitle.hide();
35492         }
35493         el.dom.style.width  = tm.maxWidth+'px';
35494         //tipBody.dom.style.width = '';
35495         tipBodyText.update(o.text);
35496         var p = getPad(), w = ce.width;
35497         if(!w){
35498             var td = tipBodyText.dom;
35499             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35500             if(aw > tm.maxWidth){
35501                 w = tm.maxWidth;
35502             }else if(aw < tm.minWidth){
35503                 w = tm.minWidth;
35504             }else{
35505                 w = aw;
35506             }
35507         }
35508         //tipBody.setWidth(w);
35509         el.setWidth(parseInt(w, 10) + p);
35510         if(ce.autoHide === false){
35511             close.setDisplayed(true);
35512             if(dd){
35513                 dd.unlock();
35514             }
35515         }else{
35516             close.setDisplayed(false);
35517             if(dd){
35518                 dd.lock();
35519             }
35520         }
35521         if(xy){
35522             el.avoidY = xy[1]-18;
35523             el.setXY(xy);
35524         }
35525         if(tm.animate){
35526             el.setOpacity(.1);
35527             el.setStyle("visibility", "visible");
35528             el.fadeIn({callback: afterShow});
35529         }else{
35530             afterShow();
35531         }
35532     };
35533     
35534     var afterShow = function(){
35535         if(ce){
35536             el.show();
35537             esc.enable();
35538             if(tm.autoDismiss && ce.autoHide !== false){
35539                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35540             }
35541         }
35542     };
35543     
35544     var hide = function(noanim){
35545         clearTimeout(dismissProc);
35546         clearTimeout(hideProc);
35547         ce = null;
35548         if(el.isVisible()){
35549             esc.disable();
35550             if(noanim !== true && tm.animate){
35551                 el.fadeOut({callback: afterHide});
35552             }else{
35553                 afterHide();
35554             } 
35555         }
35556     };
35557     
35558     var afterHide = function(){
35559         el.hide();
35560         if(removeCls){
35561             el.removeClass(removeCls);
35562             removeCls = null;
35563         }
35564     };
35565     
35566     return {
35567         /**
35568         * @cfg {Number} minWidth
35569         * The minimum width of the quick tip (defaults to 40)
35570         */
35571        minWidth : 40,
35572         /**
35573         * @cfg {Number} maxWidth
35574         * The maximum width of the quick tip (defaults to 300)
35575         */
35576        maxWidth : 300,
35577         /**
35578         * @cfg {Boolean} interceptTitles
35579         * True to automatically use the element's DOM title value if available (defaults to false)
35580         */
35581        interceptTitles : false,
35582         /**
35583         * @cfg {Boolean} trackMouse
35584         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35585         */
35586        trackMouse : false,
35587         /**
35588         * @cfg {Boolean} hideOnClick
35589         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35590         */
35591        hideOnClick : true,
35592         /**
35593         * @cfg {Number} showDelay
35594         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35595         */
35596        showDelay : 500,
35597         /**
35598         * @cfg {Number} hideDelay
35599         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35600         */
35601        hideDelay : 200,
35602         /**
35603         * @cfg {Boolean} autoHide
35604         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35605         * Used in conjunction with hideDelay.
35606         */
35607        autoHide : true,
35608         /**
35609         * @cfg {Boolean}
35610         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35611         * (defaults to true).  Used in conjunction with autoDismissDelay.
35612         */
35613        autoDismiss : true,
35614         /**
35615         * @cfg {Number}
35616         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35617         */
35618        autoDismissDelay : 5000,
35619        /**
35620         * @cfg {Boolean} animate
35621         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35622         */
35623        animate : false,
35624
35625        /**
35626         * @cfg {String} title
35627         * Title text to display (defaults to '').  This can be any valid HTML markup.
35628         */
35629         title: '',
35630        /**
35631         * @cfg {String} text
35632         * Body text to display (defaults to '').  This can be any valid HTML markup.
35633         */
35634         text : '',
35635        /**
35636         * @cfg {String} cls
35637         * A CSS class to apply to the base quick tip element (defaults to '').
35638         */
35639         cls : '',
35640        /**
35641         * @cfg {Number} width
35642         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35643         * minWidth or maxWidth.
35644         */
35645         width : null,
35646
35647     /**
35648      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35649      * or display QuickTips in a page.
35650      */
35651        init : function(){
35652           tm = Roo.QuickTips;
35653           cfg = tm.tagConfig;
35654           if(!inited){
35655               if(!Roo.isReady){ // allow calling of init() before onReady
35656                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35657                   return;
35658               }
35659               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35660               el.fxDefaults = {stopFx: true};
35661               // maximum custom styling
35662               //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>');
35663               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>');              
35664               tipTitle = el.child('h3');
35665               tipTitle.enableDisplayMode("block");
35666               tipBody = el.child('div.x-tip-bd');
35667               tipBodyText = el.child('div.x-tip-bd-inner');
35668               //bdLeft = el.child('div.x-tip-bd-left');
35669               //bdRight = el.child('div.x-tip-bd-right');
35670               close = el.child('div.x-tip-close');
35671               close.enableDisplayMode("block");
35672               close.on("click", hide);
35673               var d = Roo.get(document);
35674               d.on("mousedown", onDown);
35675               d.on("mouseover", onOver);
35676               d.on("mouseout", onOut);
35677               d.on("mousemove", onMove);
35678               esc = d.addKeyListener(27, hide);
35679               esc.disable();
35680               if(Roo.dd.DD){
35681                   dd = el.initDD("default", null, {
35682                       onDrag : function(){
35683                           el.sync();  
35684                       }
35685                   });
35686                   dd.setHandleElId(tipTitle.id);
35687                   dd.lock();
35688               }
35689               inited = true;
35690           }
35691           this.enable(); 
35692        },
35693
35694     /**
35695      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35696      * are supported:
35697      * <pre>
35698 Property    Type                   Description
35699 ----------  ---------------------  ------------------------------------------------------------------------
35700 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35701      * </ul>
35702      * @param {Object} config The config object
35703      */
35704        register : function(config){
35705            var cs = config instanceof Array ? config : arguments;
35706            for(var i = 0, len = cs.length; i < len; i++) {
35707                var c = cs[i];
35708                var target = c.target;
35709                if(target){
35710                    if(target instanceof Array){
35711                        for(var j = 0, jlen = target.length; j < jlen; j++){
35712                            tagEls[target[j]] = c;
35713                        }
35714                    }else{
35715                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35716                    }
35717                }
35718            }
35719        },
35720
35721     /**
35722      * Removes this quick tip from its element and destroys it.
35723      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35724      */
35725        unregister : function(el){
35726            delete tagEls[Roo.id(el)];
35727        },
35728
35729     /**
35730      * Enable this quick tip.
35731      */
35732        enable : function(){
35733            if(inited && disabled){
35734                locks.pop();
35735                if(locks.length < 1){
35736                    disabled = false;
35737                }
35738            }
35739        },
35740
35741     /**
35742      * Disable this quick tip.
35743      */
35744        disable : function(){
35745           disabled = true;
35746           clearTimeout(showProc);
35747           clearTimeout(hideProc);
35748           clearTimeout(dismissProc);
35749           if(ce){
35750               hide(true);
35751           }
35752           locks.push(1);
35753        },
35754
35755     /**
35756      * Returns true if the quick tip is enabled, else false.
35757      */
35758        isEnabled : function(){
35759             return !disabled;
35760        },
35761
35762         // private
35763        tagConfig : {
35764            namespace : "roo", // was ext?? this may break..
35765            alt_namespace : "ext",
35766            attribute : "qtip",
35767            width : "width",
35768            target : "target",
35769            title : "qtitle",
35770            hide : "hide",
35771            cls : "qclass"
35772        }
35773    };
35774 }();
35775
35776 // backwards compat
35777 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35778  * Based on:
35779  * Ext JS Library 1.1.1
35780  * Copyright(c) 2006-2007, Ext JS, LLC.
35781  *
35782  * Originally Released Under LGPL - original licence link has changed is not relivant.
35783  *
35784  * Fork - LGPL
35785  * <script type="text/javascript">
35786  */
35787  
35788
35789 /**
35790  * @class Roo.tree.TreePanel
35791  * @extends Roo.data.Tree
35792  * @cfg {Roo.tree.TreeNode} root The root node
35793  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35794  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35795  * @cfg {Boolean} enableDD true to enable drag and drop
35796  * @cfg {Boolean} enableDrag true to enable just drag
35797  * @cfg {Boolean} enableDrop true to enable just drop
35798  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35799  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35800  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35801  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35802  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35803  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35804  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35805  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35806  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35807  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35808  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35809  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35810  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35811  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35812  * @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>
35813  * @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>
35814  * 
35815  * @constructor
35816  * @param {String/HTMLElement/Element} el The container element
35817  * @param {Object} config
35818  */
35819 Roo.tree.TreePanel = function(el, config){
35820     var root = false;
35821     var loader = false;
35822     if (config.root) {
35823         root = config.root;
35824         delete config.root;
35825     }
35826     if (config.loader) {
35827         loader = config.loader;
35828         delete config.loader;
35829     }
35830     
35831     Roo.apply(this, config);
35832     Roo.tree.TreePanel.superclass.constructor.call(this);
35833     this.el = Roo.get(el);
35834     this.el.addClass('x-tree');
35835     //console.log(root);
35836     if (root) {
35837         this.setRootNode( Roo.factory(root, Roo.tree));
35838     }
35839     if (loader) {
35840         this.loader = Roo.factory(loader, Roo.tree);
35841     }
35842    /**
35843     * Read-only. The id of the container element becomes this TreePanel's id.
35844     */
35845     this.id = this.el.id;
35846     this.addEvents({
35847         /**
35848         * @event beforeload
35849         * Fires before a node is loaded, return false to cancel
35850         * @param {Node} node The node being loaded
35851         */
35852         "beforeload" : true,
35853         /**
35854         * @event load
35855         * Fires when a node is loaded
35856         * @param {Node} node The node that was loaded
35857         */
35858         "load" : true,
35859         /**
35860         * @event textchange
35861         * Fires when the text for a node is changed
35862         * @param {Node} node The node
35863         * @param {String} text The new text
35864         * @param {String} oldText The old text
35865         */
35866         "textchange" : true,
35867         /**
35868         * @event beforeexpand
35869         * Fires before a node is expanded, return false to cancel.
35870         * @param {Node} node The node
35871         * @param {Boolean} deep
35872         * @param {Boolean} anim
35873         */
35874         "beforeexpand" : true,
35875         /**
35876         * @event beforecollapse
35877         * Fires before a node is collapsed, return false to cancel.
35878         * @param {Node} node The node
35879         * @param {Boolean} deep
35880         * @param {Boolean} anim
35881         */
35882         "beforecollapse" : true,
35883         /**
35884         * @event expand
35885         * Fires when a node is expanded
35886         * @param {Node} node The node
35887         */
35888         "expand" : true,
35889         /**
35890         * @event disabledchange
35891         * Fires when the disabled status of a node changes
35892         * @param {Node} node The node
35893         * @param {Boolean} disabled
35894         */
35895         "disabledchange" : true,
35896         /**
35897         * @event collapse
35898         * Fires when a node is collapsed
35899         * @param {Node} node The node
35900         */
35901         "collapse" : true,
35902         /**
35903         * @event beforeclick
35904         * Fires before click processing on a node. Return false to cancel the default action.
35905         * @param {Node} node The node
35906         * @param {Roo.EventObject} e The event object
35907         */
35908         "beforeclick":true,
35909         /**
35910         * @event checkchange
35911         * Fires when a node with a checkbox's checked property changes
35912         * @param {Node} this This node
35913         * @param {Boolean} checked
35914         */
35915         "checkchange":true,
35916         /**
35917         * @event click
35918         * Fires when a node is clicked
35919         * @param {Node} node The node
35920         * @param {Roo.EventObject} e The event object
35921         */
35922         "click":true,
35923         /**
35924         * @event dblclick
35925         * Fires when a node is double clicked
35926         * @param {Node} node The node
35927         * @param {Roo.EventObject} e The event object
35928         */
35929         "dblclick":true,
35930         /**
35931         * @event contextmenu
35932         * Fires when a node is right clicked
35933         * @param {Node} node The node
35934         * @param {Roo.EventObject} e The event object
35935         */
35936         "contextmenu":true,
35937         /**
35938         * @event beforechildrenrendered
35939         * Fires right before the child nodes for a node are rendered
35940         * @param {Node} node The node
35941         */
35942         "beforechildrenrendered":true,
35943         /**
35944         * @event startdrag
35945         * Fires when a node starts being dragged
35946         * @param {Roo.tree.TreePanel} this
35947         * @param {Roo.tree.TreeNode} node
35948         * @param {event} e The raw browser event
35949         */ 
35950        "startdrag" : true,
35951        /**
35952         * @event enddrag
35953         * Fires when a drag operation is complete
35954         * @param {Roo.tree.TreePanel} this
35955         * @param {Roo.tree.TreeNode} node
35956         * @param {event} e The raw browser event
35957         */
35958        "enddrag" : true,
35959        /**
35960         * @event dragdrop
35961         * Fires when a dragged node is dropped on a valid DD target
35962         * @param {Roo.tree.TreePanel} this
35963         * @param {Roo.tree.TreeNode} node
35964         * @param {DD} dd The dd it was dropped on
35965         * @param {event} e The raw browser event
35966         */
35967        "dragdrop" : true,
35968        /**
35969         * @event beforenodedrop
35970         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35971         * passed to handlers has the following properties:<br />
35972         * <ul style="padding:5px;padding-left:16px;">
35973         * <li>tree - The TreePanel</li>
35974         * <li>target - The node being targeted for the drop</li>
35975         * <li>data - The drag data from the drag source</li>
35976         * <li>point - The point of the drop - append, above or below</li>
35977         * <li>source - The drag source</li>
35978         * <li>rawEvent - Raw mouse event</li>
35979         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35980         * to be inserted by setting them on this object.</li>
35981         * <li>cancel - Set this to true to cancel the drop.</li>
35982         * </ul>
35983         * @param {Object} dropEvent
35984         */
35985        "beforenodedrop" : true,
35986        /**
35987         * @event nodedrop
35988         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35989         * passed to handlers has the following properties:<br />
35990         * <ul style="padding:5px;padding-left:16px;">
35991         * <li>tree - The TreePanel</li>
35992         * <li>target - The node being targeted for the drop</li>
35993         * <li>data - The drag data from the drag source</li>
35994         * <li>point - The point of the drop - append, above or below</li>
35995         * <li>source - The drag source</li>
35996         * <li>rawEvent - Raw mouse event</li>
35997         * <li>dropNode - Dropped node(s).</li>
35998         * </ul>
35999         * @param {Object} dropEvent
36000         */
36001        "nodedrop" : true,
36002         /**
36003         * @event nodedragover
36004         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
36005         * passed to handlers has the following properties:<br />
36006         * <ul style="padding:5px;padding-left:16px;">
36007         * <li>tree - The TreePanel</li>
36008         * <li>target - The node being targeted for the drop</li>
36009         * <li>data - The drag data from the drag source</li>
36010         * <li>point - The point of the drop - append, above or below</li>
36011         * <li>source - The drag source</li>
36012         * <li>rawEvent - Raw mouse event</li>
36013         * <li>dropNode - Drop node(s) provided by the source.</li>
36014         * <li>cancel - Set this to true to signal drop not allowed.</li>
36015         * </ul>
36016         * @param {Object} dragOverEvent
36017         */
36018        "nodedragover" : true,
36019        /**
36020         * @event appendnode
36021         * Fires when append node to the tree
36022         * @param {Roo.tree.TreePanel} this
36023         * @param {Roo.tree.TreeNode} node
36024         * @param {Number} index The index of the newly appended node
36025         */
36026        "appendnode" : true
36027         
36028     });
36029     if(this.singleExpand){
36030        this.on("beforeexpand", this.restrictExpand, this);
36031     }
36032     if (this.editor) {
36033         this.editor.tree = this;
36034         this.editor = Roo.factory(this.editor, Roo.tree);
36035     }
36036     
36037     if (this.selModel) {
36038         this.selModel = Roo.factory(this.selModel, Roo.tree);
36039     }
36040    
36041 };
36042 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
36043     rootVisible : true,
36044     animate: Roo.enableFx,
36045     lines : true,
36046     enableDD : false,
36047     hlDrop : Roo.enableFx,
36048   
36049     renderer: false,
36050     
36051     rendererTip: false,
36052     // private
36053     restrictExpand : function(node){
36054         var p = node.parentNode;
36055         if(p){
36056             if(p.expandedChild && p.expandedChild.parentNode == p){
36057                 p.expandedChild.collapse();
36058             }
36059             p.expandedChild = node;
36060         }
36061     },
36062
36063     // private override
36064     setRootNode : function(node){
36065         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
36066         if(!this.rootVisible){
36067             node.ui = new Roo.tree.RootTreeNodeUI(node);
36068         }
36069         return node;
36070     },
36071
36072     /**
36073      * Returns the container element for this TreePanel
36074      */
36075     getEl : function(){
36076         return this.el;
36077     },
36078
36079     /**
36080      * Returns the default TreeLoader for this TreePanel
36081      */
36082     getLoader : function(){
36083         return this.loader;
36084     },
36085
36086     /**
36087      * Expand all nodes
36088      */
36089     expandAll : function(){
36090         this.root.expand(true);
36091     },
36092
36093     /**
36094      * Collapse all nodes
36095      */
36096     collapseAll : function(){
36097         this.root.collapse(true);
36098     },
36099
36100     /**
36101      * Returns the selection model used by this TreePanel
36102      */
36103     getSelectionModel : function(){
36104         if(!this.selModel){
36105             this.selModel = new Roo.tree.DefaultSelectionModel();
36106         }
36107         return this.selModel;
36108     },
36109
36110     /**
36111      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
36112      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
36113      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
36114      * @return {Array}
36115      */
36116     getChecked : function(a, startNode){
36117         startNode = startNode || this.root;
36118         var r = [];
36119         var f = function(){
36120             if(this.attributes.checked){
36121                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
36122             }
36123         }
36124         startNode.cascade(f);
36125         return r;
36126     },
36127
36128     /**
36129      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36130      * @param {String} path
36131      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36132      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
36133      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
36134      */
36135     expandPath : function(path, attr, callback){
36136         attr = attr || "id";
36137         var keys = path.split(this.pathSeparator);
36138         var curNode = this.root;
36139         if(curNode.attributes[attr] != keys[1]){ // invalid root
36140             if(callback){
36141                 callback(false, null);
36142             }
36143             return;
36144         }
36145         var index = 1;
36146         var f = function(){
36147             if(++index == keys.length){
36148                 if(callback){
36149                     callback(true, curNode);
36150                 }
36151                 return;
36152             }
36153             var c = curNode.findChild(attr, keys[index]);
36154             if(!c){
36155                 if(callback){
36156                     callback(false, curNode);
36157                 }
36158                 return;
36159             }
36160             curNode = c;
36161             c.expand(false, false, f);
36162         };
36163         curNode.expand(false, false, f);
36164     },
36165
36166     /**
36167      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
36168      * @param {String} path
36169      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
36170      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
36171      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
36172      */
36173     selectPath : function(path, attr, callback){
36174         attr = attr || "id";
36175         var keys = path.split(this.pathSeparator);
36176         var v = keys.pop();
36177         if(keys.length > 0){
36178             var f = function(success, node){
36179                 if(success && node){
36180                     var n = node.findChild(attr, v);
36181                     if(n){
36182                         n.select();
36183                         if(callback){
36184                             callback(true, n);
36185                         }
36186                     }else if(callback){
36187                         callback(false, n);
36188                     }
36189                 }else{
36190                     if(callback){
36191                         callback(false, n);
36192                     }
36193                 }
36194             };
36195             this.expandPath(keys.join(this.pathSeparator), attr, f);
36196         }else{
36197             this.root.select();
36198             if(callback){
36199                 callback(true, this.root);
36200             }
36201         }
36202     },
36203
36204     getTreeEl : function(){
36205         return this.el;
36206     },
36207
36208     /**
36209      * Trigger rendering of this TreePanel
36210      */
36211     render : function(){
36212         if (this.innerCt) {
36213             return this; // stop it rendering more than once!!
36214         }
36215         
36216         this.innerCt = this.el.createChild({tag:"ul",
36217                cls:"x-tree-root-ct " +
36218                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
36219
36220         if(this.containerScroll){
36221             Roo.dd.ScrollManager.register(this.el);
36222         }
36223         if((this.enableDD || this.enableDrop) && !this.dropZone){
36224            /**
36225             * The dropZone used by this tree if drop is enabled
36226             * @type Roo.tree.TreeDropZone
36227             */
36228              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
36229                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
36230            });
36231         }
36232         if((this.enableDD || this.enableDrag) && !this.dragZone){
36233            /**
36234             * The dragZone used by this tree if drag is enabled
36235             * @type Roo.tree.TreeDragZone
36236             */
36237             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
36238                ddGroup: this.ddGroup || "TreeDD",
36239                scroll: this.ddScroll
36240            });
36241         }
36242         this.getSelectionModel().init(this);
36243         if (!this.root) {
36244             Roo.log("ROOT not set in tree");
36245             return this;
36246         }
36247         this.root.render();
36248         if(!this.rootVisible){
36249             this.root.renderChildren();
36250         }
36251         return this;
36252     }
36253 });/*
36254  * Based on:
36255  * Ext JS Library 1.1.1
36256  * Copyright(c) 2006-2007, Ext JS, LLC.
36257  *
36258  * Originally Released Under LGPL - original licence link has changed is not relivant.
36259  *
36260  * Fork - LGPL
36261  * <script type="text/javascript">
36262  */
36263  
36264
36265 /**
36266  * @class Roo.tree.DefaultSelectionModel
36267  * @extends Roo.util.Observable
36268  * The default single selection for a TreePanel.
36269  * @param {Object} cfg Configuration
36270  */
36271 Roo.tree.DefaultSelectionModel = function(cfg){
36272    this.selNode = null;
36273    
36274    
36275    
36276    this.addEvents({
36277        /**
36278         * @event selectionchange
36279         * Fires when the selected node changes
36280         * @param {DefaultSelectionModel} this
36281         * @param {TreeNode} node the new selection
36282         */
36283        "selectionchange" : true,
36284
36285        /**
36286         * @event beforeselect
36287         * Fires before the selected node changes, return false to cancel the change
36288         * @param {DefaultSelectionModel} this
36289         * @param {TreeNode} node the new selection
36290         * @param {TreeNode} node the old selection
36291         */
36292        "beforeselect" : true
36293    });
36294    
36295     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36296 };
36297
36298 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36299     init : function(tree){
36300         this.tree = tree;
36301         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36302         tree.on("click", this.onNodeClick, this);
36303     },
36304     
36305     onNodeClick : function(node, e){
36306         if (e.ctrlKey && this.selNode == node)  {
36307             this.unselect(node);
36308             return;
36309         }
36310         this.select(node);
36311     },
36312     
36313     /**
36314      * Select a node.
36315      * @param {TreeNode} node The node to select
36316      * @return {TreeNode} The selected node
36317      */
36318     select : function(node){
36319         var last = this.selNode;
36320         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36321             if(last){
36322                 last.ui.onSelectedChange(false);
36323             }
36324             this.selNode = node;
36325             node.ui.onSelectedChange(true);
36326             this.fireEvent("selectionchange", this, node, last);
36327         }
36328         return node;
36329     },
36330     
36331     /**
36332      * Deselect a node.
36333      * @param {TreeNode} node The node to unselect
36334      */
36335     unselect : function(node){
36336         if(this.selNode == node){
36337             this.clearSelections();
36338         }    
36339     },
36340     
36341     /**
36342      * Clear all selections
36343      */
36344     clearSelections : function(){
36345         var n = this.selNode;
36346         if(n){
36347             n.ui.onSelectedChange(false);
36348             this.selNode = null;
36349             this.fireEvent("selectionchange", this, null);
36350         }
36351         return n;
36352     },
36353     
36354     /**
36355      * Get the selected node
36356      * @return {TreeNode} The selected node
36357      */
36358     getSelectedNode : function(){
36359         return this.selNode;    
36360     },
36361     
36362     /**
36363      * Returns true if the node is selected
36364      * @param {TreeNode} node The node to check
36365      * @return {Boolean}
36366      */
36367     isSelected : function(node){
36368         return this.selNode == node;  
36369     },
36370
36371     /**
36372      * Selects the node above the selected node in the tree, intelligently walking the nodes
36373      * @return TreeNode The new selection
36374      */
36375     selectPrevious : function(){
36376         var s = this.selNode || this.lastSelNode;
36377         if(!s){
36378             return null;
36379         }
36380         var ps = s.previousSibling;
36381         if(ps){
36382             if(!ps.isExpanded() || ps.childNodes.length < 1){
36383                 return this.select(ps);
36384             } else{
36385                 var lc = ps.lastChild;
36386                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36387                     lc = lc.lastChild;
36388                 }
36389                 return this.select(lc);
36390             }
36391         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36392             return this.select(s.parentNode);
36393         }
36394         return null;
36395     },
36396
36397     /**
36398      * Selects the node above the selected node in the tree, intelligently walking the nodes
36399      * @return TreeNode The new selection
36400      */
36401     selectNext : function(){
36402         var s = this.selNode || this.lastSelNode;
36403         if(!s){
36404             return null;
36405         }
36406         if(s.firstChild && s.isExpanded()){
36407              return this.select(s.firstChild);
36408          }else if(s.nextSibling){
36409              return this.select(s.nextSibling);
36410          }else if(s.parentNode){
36411             var newS = null;
36412             s.parentNode.bubble(function(){
36413                 if(this.nextSibling){
36414                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36415                     return false;
36416                 }
36417             });
36418             return newS;
36419          }
36420         return null;
36421     },
36422
36423     onKeyDown : function(e){
36424         var s = this.selNode || this.lastSelNode;
36425         // undesirable, but required
36426         var sm = this;
36427         if(!s){
36428             return;
36429         }
36430         var k = e.getKey();
36431         switch(k){
36432              case e.DOWN:
36433                  e.stopEvent();
36434                  this.selectNext();
36435              break;
36436              case e.UP:
36437                  e.stopEvent();
36438                  this.selectPrevious();
36439              break;
36440              case e.RIGHT:
36441                  e.preventDefault();
36442                  if(s.hasChildNodes()){
36443                      if(!s.isExpanded()){
36444                          s.expand();
36445                      }else if(s.firstChild){
36446                          this.select(s.firstChild, e);
36447                      }
36448                  }
36449              break;
36450              case e.LEFT:
36451                  e.preventDefault();
36452                  if(s.hasChildNodes() && s.isExpanded()){
36453                      s.collapse();
36454                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36455                      this.select(s.parentNode, e);
36456                  }
36457              break;
36458         };
36459     }
36460 });
36461
36462 /**
36463  * @class Roo.tree.MultiSelectionModel
36464  * @extends Roo.util.Observable
36465  * Multi selection for a TreePanel.
36466  * @param {Object} cfg Configuration
36467  */
36468 Roo.tree.MultiSelectionModel = function(){
36469    this.selNodes = [];
36470    this.selMap = {};
36471    this.addEvents({
36472        /**
36473         * @event selectionchange
36474         * Fires when the selected nodes change
36475         * @param {MultiSelectionModel} this
36476         * @param {Array} nodes Array of the selected nodes
36477         */
36478        "selectionchange" : true
36479    });
36480    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36481    
36482 };
36483
36484 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36485     init : function(tree){
36486         this.tree = tree;
36487         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36488         tree.on("click", this.onNodeClick, this);
36489     },
36490     
36491     onNodeClick : function(node, e){
36492         this.select(node, e, e.ctrlKey);
36493     },
36494     
36495     /**
36496      * Select a node.
36497      * @param {TreeNode} node The node to select
36498      * @param {EventObject} e (optional) An event associated with the selection
36499      * @param {Boolean} keepExisting True to retain existing selections
36500      * @return {TreeNode} The selected node
36501      */
36502     select : function(node, e, keepExisting){
36503         if(keepExisting !== true){
36504             this.clearSelections(true);
36505         }
36506         if(this.isSelected(node)){
36507             this.lastSelNode = node;
36508             return node;
36509         }
36510         this.selNodes.push(node);
36511         this.selMap[node.id] = node;
36512         this.lastSelNode = node;
36513         node.ui.onSelectedChange(true);
36514         this.fireEvent("selectionchange", this, this.selNodes);
36515         return node;
36516     },
36517     
36518     /**
36519      * Deselect a node.
36520      * @param {TreeNode} node The node to unselect
36521      */
36522     unselect : function(node){
36523         if(this.selMap[node.id]){
36524             node.ui.onSelectedChange(false);
36525             var sn = this.selNodes;
36526             var index = -1;
36527             if(sn.indexOf){
36528                 index = sn.indexOf(node);
36529             }else{
36530                 for(var i = 0, len = sn.length; i < len; i++){
36531                     if(sn[i] == node){
36532                         index = i;
36533                         break;
36534                     }
36535                 }
36536             }
36537             if(index != -1){
36538                 this.selNodes.splice(index, 1);
36539             }
36540             delete this.selMap[node.id];
36541             this.fireEvent("selectionchange", this, this.selNodes);
36542         }
36543     },
36544     
36545     /**
36546      * Clear all selections
36547      */
36548     clearSelections : function(suppressEvent){
36549         var sn = this.selNodes;
36550         if(sn.length > 0){
36551             for(var i = 0, len = sn.length; i < len; i++){
36552                 sn[i].ui.onSelectedChange(false);
36553             }
36554             this.selNodes = [];
36555             this.selMap = {};
36556             if(suppressEvent !== true){
36557                 this.fireEvent("selectionchange", this, this.selNodes);
36558             }
36559         }
36560     },
36561     
36562     /**
36563      * Returns true if the node is selected
36564      * @param {TreeNode} node The node to check
36565      * @return {Boolean}
36566      */
36567     isSelected : function(node){
36568         return this.selMap[node.id] ? true : false;  
36569     },
36570     
36571     /**
36572      * Returns an array of the selected nodes
36573      * @return {Array}
36574      */
36575     getSelectedNodes : function(){
36576         return this.selNodes;    
36577     },
36578
36579     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36580
36581     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36582
36583     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36584 });/*
36585  * Based on:
36586  * Ext JS Library 1.1.1
36587  * Copyright(c) 2006-2007, Ext JS, LLC.
36588  *
36589  * Originally Released Under LGPL - original licence link has changed is not relivant.
36590  *
36591  * Fork - LGPL
36592  * <script type="text/javascript">
36593  */
36594  
36595 /**
36596  * @class Roo.tree.TreeNode
36597  * @extends Roo.data.Node
36598  * @cfg {String} text The text for this node
36599  * @cfg {Boolean} expanded true to start the node expanded
36600  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36601  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36602  * @cfg {Boolean} disabled true to start the node disabled
36603  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36604  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36605  * @cfg {String} cls A css class to be added to the node
36606  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36607  * @cfg {String} href URL of the link used for the node (defaults to #)
36608  * @cfg {String} hrefTarget target frame for the link
36609  * @cfg {String} qtip An Ext QuickTip for the node
36610  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36611  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36612  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36613  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36614  * (defaults to undefined with no checkbox rendered)
36615  * @constructor
36616  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36617  */
36618 Roo.tree.TreeNode = function(attributes){
36619     attributes = attributes || {};
36620     if(typeof attributes == "string"){
36621         attributes = {text: attributes};
36622     }
36623     this.childrenRendered = false;
36624     this.rendered = false;
36625     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36626     this.expanded = attributes.expanded === true;
36627     this.isTarget = attributes.isTarget !== false;
36628     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36629     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36630
36631     /**
36632      * Read-only. The text for this node. To change it use setText().
36633      * @type String
36634      */
36635     this.text = attributes.text;
36636     /**
36637      * True if this node is disabled.
36638      * @type Boolean
36639      */
36640     this.disabled = attributes.disabled === true;
36641
36642     this.addEvents({
36643         /**
36644         * @event textchange
36645         * Fires when the text for this node is changed
36646         * @param {Node} this This node
36647         * @param {String} text The new text
36648         * @param {String} oldText The old text
36649         */
36650         "textchange" : true,
36651         /**
36652         * @event beforeexpand
36653         * Fires before this node is expanded, return false to cancel.
36654         * @param {Node} this This node
36655         * @param {Boolean} deep
36656         * @param {Boolean} anim
36657         */
36658         "beforeexpand" : true,
36659         /**
36660         * @event beforecollapse
36661         * Fires before this node is collapsed, return false to cancel.
36662         * @param {Node} this This node
36663         * @param {Boolean} deep
36664         * @param {Boolean} anim
36665         */
36666         "beforecollapse" : true,
36667         /**
36668         * @event expand
36669         * Fires when this node is expanded
36670         * @param {Node} this This node
36671         */
36672         "expand" : true,
36673         /**
36674         * @event disabledchange
36675         * Fires when the disabled status of this node changes
36676         * @param {Node} this This node
36677         * @param {Boolean} disabled
36678         */
36679         "disabledchange" : true,
36680         /**
36681         * @event collapse
36682         * Fires when this node is collapsed
36683         * @param {Node} this This node
36684         */
36685         "collapse" : true,
36686         /**
36687         * @event beforeclick
36688         * Fires before click processing. Return false to cancel the default action.
36689         * @param {Node} this This node
36690         * @param {Roo.EventObject} e The event object
36691         */
36692         "beforeclick":true,
36693         /**
36694         * @event checkchange
36695         * Fires when a node with a checkbox's checked property changes
36696         * @param {Node} this This node
36697         * @param {Boolean} checked
36698         */
36699         "checkchange":true,
36700         /**
36701         * @event click
36702         * Fires when this node is clicked
36703         * @param {Node} this This node
36704         * @param {Roo.EventObject} e The event object
36705         */
36706         "click":true,
36707         /**
36708         * @event dblclick
36709         * Fires when this node is double clicked
36710         * @param {Node} this This node
36711         * @param {Roo.EventObject} e The event object
36712         */
36713         "dblclick":true,
36714         /**
36715         * @event contextmenu
36716         * Fires when this node is right clicked
36717         * @param {Node} this This node
36718         * @param {Roo.EventObject} e The event object
36719         */
36720         "contextmenu":true,
36721         /**
36722         * @event beforechildrenrendered
36723         * Fires right before the child nodes for this node are rendered
36724         * @param {Node} this This node
36725         */
36726         "beforechildrenrendered":true
36727     });
36728
36729     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36730
36731     /**
36732      * Read-only. The UI for this node
36733      * @type TreeNodeUI
36734      */
36735     this.ui = new uiClass(this);
36736     
36737     // finally support items[]
36738     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36739         return;
36740     }
36741     
36742     
36743     Roo.each(this.attributes.items, function(c) {
36744         this.appendChild(Roo.factory(c,Roo.Tree));
36745     }, this);
36746     delete this.attributes.items;
36747     
36748     
36749     
36750 };
36751 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36752     preventHScroll: true,
36753     /**
36754      * Returns true if this node is expanded
36755      * @return {Boolean}
36756      */
36757     isExpanded : function(){
36758         return this.expanded;
36759     },
36760
36761     /**
36762      * Returns the UI object for this node
36763      * @return {TreeNodeUI}
36764      */
36765     getUI : function(){
36766         return this.ui;
36767     },
36768
36769     // private override
36770     setFirstChild : function(node){
36771         var of = this.firstChild;
36772         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36773         if(this.childrenRendered && of && node != of){
36774             of.renderIndent(true, true);
36775         }
36776         if(this.rendered){
36777             this.renderIndent(true, true);
36778         }
36779     },
36780
36781     // private override
36782     setLastChild : function(node){
36783         var ol = this.lastChild;
36784         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36785         if(this.childrenRendered && ol && node != ol){
36786             ol.renderIndent(true, true);
36787         }
36788         if(this.rendered){
36789             this.renderIndent(true, true);
36790         }
36791     },
36792
36793     // these methods are overridden to provide lazy rendering support
36794     // private override
36795     appendChild : function()
36796     {
36797         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36798         if(node && this.childrenRendered){
36799             node.render();
36800         }
36801         this.ui.updateExpandIcon();
36802         return node;
36803     },
36804
36805     // private override
36806     removeChild : function(node){
36807         this.ownerTree.getSelectionModel().unselect(node);
36808         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36809         // if it's been rendered remove dom node
36810         if(this.childrenRendered){
36811             node.ui.remove();
36812         }
36813         if(this.childNodes.length < 1){
36814             this.collapse(false, false);
36815         }else{
36816             this.ui.updateExpandIcon();
36817         }
36818         if(!this.firstChild) {
36819             this.childrenRendered = false;
36820         }
36821         return node;
36822     },
36823
36824     // private override
36825     insertBefore : function(node, refNode){
36826         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36827         if(newNode && refNode && this.childrenRendered){
36828             node.render();
36829         }
36830         this.ui.updateExpandIcon();
36831         return newNode;
36832     },
36833
36834     /**
36835      * Sets the text for this node
36836      * @param {String} text
36837      */
36838     setText : function(text){
36839         var oldText = this.text;
36840         this.text = text;
36841         this.attributes.text = text;
36842         if(this.rendered){ // event without subscribing
36843             this.ui.onTextChange(this, text, oldText);
36844         }
36845         this.fireEvent("textchange", this, text, oldText);
36846     },
36847
36848     /**
36849      * Triggers selection of this node
36850      */
36851     select : function(){
36852         this.getOwnerTree().getSelectionModel().select(this);
36853     },
36854
36855     /**
36856      * Triggers deselection of this node
36857      */
36858     unselect : function(){
36859         this.getOwnerTree().getSelectionModel().unselect(this);
36860     },
36861
36862     /**
36863      * Returns true if this node is selected
36864      * @return {Boolean}
36865      */
36866     isSelected : function(){
36867         return this.getOwnerTree().getSelectionModel().isSelected(this);
36868     },
36869
36870     /**
36871      * Expand this node.
36872      * @param {Boolean} deep (optional) True to expand all children as well
36873      * @param {Boolean} anim (optional) false to cancel the default animation
36874      * @param {Function} callback (optional) A callback to be called when
36875      * expanding this node completes (does not wait for deep expand to complete).
36876      * Called with 1 parameter, this node.
36877      */
36878     expand : function(deep, anim, callback){
36879         if(!this.expanded){
36880             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36881                 return;
36882             }
36883             if(!this.childrenRendered){
36884                 this.renderChildren();
36885             }
36886             this.expanded = true;
36887             
36888             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36889                 this.ui.animExpand(function(){
36890                     this.fireEvent("expand", this);
36891                     if(typeof callback == "function"){
36892                         callback(this);
36893                     }
36894                     if(deep === true){
36895                         this.expandChildNodes(true);
36896                     }
36897                 }.createDelegate(this));
36898                 return;
36899             }else{
36900                 this.ui.expand();
36901                 this.fireEvent("expand", this);
36902                 if(typeof callback == "function"){
36903                     callback(this);
36904                 }
36905             }
36906         }else{
36907            if(typeof callback == "function"){
36908                callback(this);
36909            }
36910         }
36911         if(deep === true){
36912             this.expandChildNodes(true);
36913         }
36914     },
36915
36916     isHiddenRoot : function(){
36917         return this.isRoot && !this.getOwnerTree().rootVisible;
36918     },
36919
36920     /**
36921      * Collapse this node.
36922      * @param {Boolean} deep (optional) True to collapse all children as well
36923      * @param {Boolean} anim (optional) false to cancel the default animation
36924      */
36925     collapse : function(deep, anim){
36926         if(this.expanded && !this.isHiddenRoot()){
36927             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36928                 return;
36929             }
36930             this.expanded = false;
36931             if((this.getOwnerTree().animate && anim !== false) || anim){
36932                 this.ui.animCollapse(function(){
36933                     this.fireEvent("collapse", this);
36934                     if(deep === true){
36935                         this.collapseChildNodes(true);
36936                     }
36937                 }.createDelegate(this));
36938                 return;
36939             }else{
36940                 this.ui.collapse();
36941                 this.fireEvent("collapse", this);
36942             }
36943         }
36944         if(deep === true){
36945             var cs = this.childNodes;
36946             for(var i = 0, len = cs.length; i < len; i++) {
36947                 cs[i].collapse(true, false);
36948             }
36949         }
36950     },
36951
36952     // private
36953     delayedExpand : function(delay){
36954         if(!this.expandProcId){
36955             this.expandProcId = this.expand.defer(delay, this);
36956         }
36957     },
36958
36959     // private
36960     cancelExpand : function(){
36961         if(this.expandProcId){
36962             clearTimeout(this.expandProcId);
36963         }
36964         this.expandProcId = false;
36965     },
36966
36967     /**
36968      * Toggles expanded/collapsed state of the node
36969      */
36970     toggle : function(){
36971         if(this.expanded){
36972             this.collapse();
36973         }else{
36974             this.expand();
36975         }
36976     },
36977
36978     /**
36979      * Ensures all parent nodes are expanded
36980      */
36981     ensureVisible : function(callback){
36982         var tree = this.getOwnerTree();
36983         tree.expandPath(this.parentNode.getPath(), false, function(){
36984             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36985             Roo.callback(callback);
36986         }.createDelegate(this));
36987     },
36988
36989     /**
36990      * Expand all child nodes
36991      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36992      */
36993     expandChildNodes : function(deep){
36994         var cs = this.childNodes;
36995         for(var i = 0, len = cs.length; i < len; i++) {
36996                 cs[i].expand(deep);
36997         }
36998     },
36999
37000     /**
37001      * Collapse all child nodes
37002      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
37003      */
37004     collapseChildNodes : function(deep){
37005         var cs = this.childNodes;
37006         for(var i = 0, len = cs.length; i < len; i++) {
37007                 cs[i].collapse(deep);
37008         }
37009     },
37010
37011     /**
37012      * Disables this node
37013      */
37014     disable : function(){
37015         this.disabled = true;
37016         this.unselect();
37017         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
37018             this.ui.onDisableChange(this, true);
37019         }
37020         this.fireEvent("disabledchange", this, true);
37021     },
37022
37023     /**
37024      * Enables this node
37025      */
37026     enable : function(){
37027         this.disabled = false;
37028         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
37029             this.ui.onDisableChange(this, false);
37030         }
37031         this.fireEvent("disabledchange", this, false);
37032     },
37033
37034     // private
37035     renderChildren : function(suppressEvent){
37036         if(suppressEvent !== false){
37037             this.fireEvent("beforechildrenrendered", this);
37038         }
37039         var cs = this.childNodes;
37040         for(var i = 0, len = cs.length; i < len; i++){
37041             cs[i].render(true);
37042         }
37043         this.childrenRendered = true;
37044     },
37045
37046     // private
37047     sort : function(fn, scope){
37048         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
37049         if(this.childrenRendered){
37050             var cs = this.childNodes;
37051             for(var i = 0, len = cs.length; i < len; i++){
37052                 cs[i].render(true);
37053             }
37054         }
37055     },
37056
37057     // private
37058     render : function(bulkRender){
37059         this.ui.render(bulkRender);
37060         if(!this.rendered){
37061             this.rendered = true;
37062             if(this.expanded){
37063                 this.expanded = false;
37064                 this.expand(false, false);
37065             }
37066         }
37067     },
37068
37069     // private
37070     renderIndent : function(deep, refresh){
37071         if(refresh){
37072             this.ui.childIndent = null;
37073         }
37074         this.ui.renderIndent();
37075         if(deep === true && this.childrenRendered){
37076             var cs = this.childNodes;
37077             for(var i = 0, len = cs.length; i < len; i++){
37078                 cs[i].renderIndent(true, refresh);
37079             }
37080         }
37081     }
37082 });/*
37083  * Based on:
37084  * Ext JS Library 1.1.1
37085  * Copyright(c) 2006-2007, Ext JS, LLC.
37086  *
37087  * Originally Released Under LGPL - original licence link has changed is not relivant.
37088  *
37089  * Fork - LGPL
37090  * <script type="text/javascript">
37091  */
37092  
37093 /**
37094  * @class Roo.tree.AsyncTreeNode
37095  * @extends Roo.tree.TreeNode
37096  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
37097  * @constructor
37098  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
37099  */
37100  Roo.tree.AsyncTreeNode = function(config){
37101     this.loaded = false;
37102     this.loading = false;
37103     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
37104     /**
37105     * @event beforeload
37106     * Fires before this node is loaded, return false to cancel
37107     * @param {Node} this This node
37108     */
37109     this.addEvents({'beforeload':true, 'load': true});
37110     /**
37111     * @event load
37112     * Fires when this node is loaded
37113     * @param {Node} this This node
37114     */
37115     /**
37116      * The loader used by this node (defaults to using the tree's defined loader)
37117      * @type TreeLoader
37118      * @property loader
37119      */
37120 };
37121 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
37122     expand : function(deep, anim, callback){
37123         if(this.loading){ // if an async load is already running, waiting til it's done
37124             var timer;
37125             var f = function(){
37126                 if(!this.loading){ // done loading
37127                     clearInterval(timer);
37128                     this.expand(deep, anim, callback);
37129                 }
37130             }.createDelegate(this);
37131             timer = setInterval(f, 200);
37132             return;
37133         }
37134         if(!this.loaded){
37135             if(this.fireEvent("beforeload", this) === false){
37136                 return;
37137             }
37138             this.loading = true;
37139             this.ui.beforeLoad(this);
37140             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
37141             if(loader){
37142                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
37143                 return;
37144             }
37145         }
37146         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
37147     },
37148     
37149     /**
37150      * Returns true if this node is currently loading
37151      * @return {Boolean}
37152      */
37153     isLoading : function(){
37154         return this.loading;  
37155     },
37156     
37157     loadComplete : function(deep, anim, callback){
37158         this.loading = false;
37159         this.loaded = true;
37160         this.ui.afterLoad(this);
37161         this.fireEvent("load", this);
37162         this.expand(deep, anim, callback);
37163     },
37164     
37165     /**
37166      * Returns true if this node has been loaded
37167      * @return {Boolean}
37168      */
37169     isLoaded : function(){
37170         return this.loaded;
37171     },
37172     
37173     hasChildNodes : function(){
37174         if(!this.isLeaf() && !this.loaded){
37175             return true;
37176         }else{
37177             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
37178         }
37179     },
37180
37181     /**
37182      * Trigger a reload for this node
37183      * @param {Function} callback
37184      */
37185     reload : function(callback){
37186         this.collapse(false, false);
37187         while(this.firstChild){
37188             this.removeChild(this.firstChild);
37189         }
37190         this.childrenRendered = false;
37191         this.loaded = false;
37192         if(this.isHiddenRoot()){
37193             this.expanded = false;
37194         }
37195         this.expand(false, false, callback);
37196     }
37197 });/*
37198  * Based on:
37199  * Ext JS Library 1.1.1
37200  * Copyright(c) 2006-2007, Ext JS, LLC.
37201  *
37202  * Originally Released Under LGPL - original licence link has changed is not relivant.
37203  *
37204  * Fork - LGPL
37205  * <script type="text/javascript">
37206  */
37207  
37208 /**
37209  * @class Roo.tree.TreeNodeUI
37210  * @constructor
37211  * @param {Object} node The node to render
37212  * The TreeNode UI implementation is separate from the
37213  * tree implementation. Unless you are customizing the tree UI,
37214  * you should never have to use this directly.
37215  */
37216 Roo.tree.TreeNodeUI = function(node){
37217     this.node = node;
37218     this.rendered = false;
37219     this.animating = false;
37220     this.emptyIcon = Roo.BLANK_IMAGE_URL;
37221 };
37222
37223 Roo.tree.TreeNodeUI.prototype = {
37224     removeChild : function(node){
37225         if(this.rendered){
37226             this.ctNode.removeChild(node.ui.getEl());
37227         }
37228     },
37229
37230     beforeLoad : function(){
37231          this.addClass("x-tree-node-loading");
37232     },
37233
37234     afterLoad : function(){
37235          this.removeClass("x-tree-node-loading");
37236     },
37237
37238     onTextChange : function(node, text, oldText){
37239         if(this.rendered){
37240             this.textNode.innerHTML = text;
37241         }
37242     },
37243
37244     onDisableChange : function(node, state){
37245         this.disabled = state;
37246         if(state){
37247             this.addClass("x-tree-node-disabled");
37248         }else{
37249             this.removeClass("x-tree-node-disabled");
37250         }
37251     },
37252
37253     onSelectedChange : function(state){
37254         if(state){
37255             this.focus();
37256             this.addClass("x-tree-selected");
37257         }else{
37258             //this.blur();
37259             this.removeClass("x-tree-selected");
37260         }
37261     },
37262
37263     onMove : function(tree, node, oldParent, newParent, index, refNode){
37264         this.childIndent = null;
37265         if(this.rendered){
37266             var targetNode = newParent.ui.getContainer();
37267             if(!targetNode){//target not rendered
37268                 this.holder = document.createElement("div");
37269                 this.holder.appendChild(this.wrap);
37270                 return;
37271             }
37272             var insertBefore = refNode ? refNode.ui.getEl() : null;
37273             if(insertBefore){
37274                 targetNode.insertBefore(this.wrap, insertBefore);
37275             }else{
37276                 targetNode.appendChild(this.wrap);
37277             }
37278             this.node.renderIndent(true);
37279         }
37280     },
37281
37282     addClass : function(cls){
37283         if(this.elNode){
37284             Roo.fly(this.elNode).addClass(cls);
37285         }
37286     },
37287
37288     removeClass : function(cls){
37289         if(this.elNode){
37290             Roo.fly(this.elNode).removeClass(cls);
37291         }
37292     },
37293
37294     remove : function(){
37295         if(this.rendered){
37296             this.holder = document.createElement("div");
37297             this.holder.appendChild(this.wrap);
37298         }
37299     },
37300
37301     fireEvent : function(){
37302         return this.node.fireEvent.apply(this.node, arguments);
37303     },
37304
37305     initEvents : function(){
37306         this.node.on("move", this.onMove, this);
37307         var E = Roo.EventManager;
37308         var a = this.anchor;
37309
37310         var el = Roo.fly(a, '_treeui');
37311
37312         if(Roo.isOpera){ // opera render bug ignores the CSS
37313             el.setStyle("text-decoration", "none");
37314         }
37315
37316         el.on("click", this.onClick, this);
37317         el.on("dblclick", this.onDblClick, this);
37318
37319         if(this.checkbox){
37320             Roo.EventManager.on(this.checkbox,
37321                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37322         }
37323
37324         el.on("contextmenu", this.onContextMenu, this);
37325
37326         var icon = Roo.fly(this.iconNode);
37327         icon.on("click", this.onClick, this);
37328         icon.on("dblclick", this.onDblClick, this);
37329         icon.on("contextmenu", this.onContextMenu, this);
37330         E.on(this.ecNode, "click", this.ecClick, this, true);
37331
37332         if(this.node.disabled){
37333             this.addClass("x-tree-node-disabled");
37334         }
37335         if(this.node.hidden){
37336             this.addClass("x-tree-node-disabled");
37337         }
37338         var ot = this.node.getOwnerTree();
37339         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37340         if(dd && (!this.node.isRoot || ot.rootVisible)){
37341             Roo.dd.Registry.register(this.elNode, {
37342                 node: this.node,
37343                 handles: this.getDDHandles(),
37344                 isHandle: false
37345             });
37346         }
37347     },
37348
37349     getDDHandles : function(){
37350         return [this.iconNode, this.textNode];
37351     },
37352
37353     hide : function(){
37354         if(this.rendered){
37355             this.wrap.style.display = "none";
37356         }
37357     },
37358
37359     show : function(){
37360         if(this.rendered){
37361             this.wrap.style.display = "";
37362         }
37363     },
37364
37365     onContextMenu : function(e){
37366         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37367             e.preventDefault();
37368             this.focus();
37369             this.fireEvent("contextmenu", this.node, e);
37370         }
37371     },
37372
37373     onClick : function(e){
37374         if(this.dropping){
37375             e.stopEvent();
37376             return;
37377         }
37378         if(this.fireEvent("beforeclick", this.node, e) !== false){
37379             if(!this.disabled && this.node.attributes.href){
37380                 this.fireEvent("click", this.node, e);
37381                 return;
37382             }
37383             e.preventDefault();
37384             if(this.disabled){
37385                 return;
37386             }
37387
37388             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37389                 this.node.toggle();
37390             }
37391
37392             this.fireEvent("click", this.node, e);
37393         }else{
37394             e.stopEvent();
37395         }
37396     },
37397
37398     onDblClick : function(e){
37399         e.preventDefault();
37400         if(this.disabled){
37401             return;
37402         }
37403         if(this.checkbox){
37404             this.toggleCheck();
37405         }
37406         if(!this.animating && this.node.hasChildNodes()){
37407             this.node.toggle();
37408         }
37409         this.fireEvent("dblclick", this.node, e);
37410     },
37411
37412     onCheckChange : function(){
37413         var checked = this.checkbox.checked;
37414         this.node.attributes.checked = checked;
37415         this.fireEvent('checkchange', this.node, checked);
37416     },
37417
37418     ecClick : function(e){
37419         if(!this.animating && this.node.hasChildNodes()){
37420             this.node.toggle();
37421         }
37422     },
37423
37424     startDrop : function(){
37425         this.dropping = true;
37426     },
37427
37428     // delayed drop so the click event doesn't get fired on a drop
37429     endDrop : function(){
37430        setTimeout(function(){
37431            this.dropping = false;
37432        }.createDelegate(this), 50);
37433     },
37434
37435     expand : function(){
37436         this.updateExpandIcon();
37437         this.ctNode.style.display = "";
37438     },
37439
37440     focus : function(){
37441         if(!this.node.preventHScroll){
37442             try{this.anchor.focus();
37443             }catch(e){}
37444         }else if(!Roo.isIE){
37445             try{
37446                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37447                 var l = noscroll.scrollLeft;
37448                 this.anchor.focus();
37449                 noscroll.scrollLeft = l;
37450             }catch(e){}
37451         }
37452     },
37453
37454     toggleCheck : function(value){
37455         var cb = this.checkbox;
37456         if(cb){
37457             cb.checked = (value === undefined ? !cb.checked : value);
37458         }
37459     },
37460
37461     blur : function(){
37462         try{
37463             this.anchor.blur();
37464         }catch(e){}
37465     },
37466
37467     animExpand : function(callback){
37468         var ct = Roo.get(this.ctNode);
37469         ct.stopFx();
37470         if(!this.node.hasChildNodes()){
37471             this.updateExpandIcon();
37472             this.ctNode.style.display = "";
37473             Roo.callback(callback);
37474             return;
37475         }
37476         this.animating = true;
37477         this.updateExpandIcon();
37478
37479         ct.slideIn('t', {
37480            callback : function(){
37481                this.animating = false;
37482                Roo.callback(callback);
37483             },
37484             scope: this,
37485             duration: this.node.ownerTree.duration || .25
37486         });
37487     },
37488
37489     highlight : function(){
37490         var tree = this.node.getOwnerTree();
37491         Roo.fly(this.wrap).highlight(
37492             tree.hlColor || "C3DAF9",
37493             {endColor: tree.hlBaseColor}
37494         );
37495     },
37496
37497     collapse : function(){
37498         this.updateExpandIcon();
37499         this.ctNode.style.display = "none";
37500     },
37501
37502     animCollapse : function(callback){
37503         var ct = Roo.get(this.ctNode);
37504         ct.enableDisplayMode('block');
37505         ct.stopFx();
37506
37507         this.animating = true;
37508         this.updateExpandIcon();
37509
37510         ct.slideOut('t', {
37511             callback : function(){
37512                this.animating = false;
37513                Roo.callback(callback);
37514             },
37515             scope: this,
37516             duration: this.node.ownerTree.duration || .25
37517         });
37518     },
37519
37520     getContainer : function(){
37521         return this.ctNode;
37522     },
37523
37524     getEl : function(){
37525         return this.wrap;
37526     },
37527
37528     appendDDGhost : function(ghostNode){
37529         ghostNode.appendChild(this.elNode.cloneNode(true));
37530     },
37531
37532     getDDRepairXY : function(){
37533         return Roo.lib.Dom.getXY(this.iconNode);
37534     },
37535
37536     onRender : function(){
37537         this.render();
37538     },
37539
37540     render : function(bulkRender){
37541         var n = this.node, a = n.attributes;
37542         var targetNode = n.parentNode ?
37543               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37544
37545         if(!this.rendered){
37546             this.rendered = true;
37547
37548             this.renderElements(n, a, targetNode, bulkRender);
37549
37550             if(a.qtip){
37551                if(this.textNode.setAttributeNS){
37552                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37553                    if(a.qtipTitle){
37554                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37555                    }
37556                }else{
37557                    this.textNode.setAttribute("ext:qtip", a.qtip);
37558                    if(a.qtipTitle){
37559                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37560                    }
37561                }
37562             }else if(a.qtipCfg){
37563                 a.qtipCfg.target = Roo.id(this.textNode);
37564                 Roo.QuickTips.register(a.qtipCfg);
37565             }
37566             this.initEvents();
37567             if(!this.node.expanded){
37568                 this.updateExpandIcon();
37569             }
37570         }else{
37571             if(bulkRender === true) {
37572                 targetNode.appendChild(this.wrap);
37573             }
37574         }
37575     },
37576
37577     renderElements : function(n, a, targetNode, bulkRender)
37578     {
37579         // add some indent caching, this helps performance when rendering a large tree
37580         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37581         var t = n.getOwnerTree();
37582         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37583         if (typeof(n.attributes.html) != 'undefined') {
37584             txt = n.attributes.html;
37585         }
37586         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37587         var cb = typeof a.checked == 'boolean';
37588         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37589         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37590             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37591             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37592             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37593             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37594             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37595              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37596                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37597             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37598             "</li>"];
37599
37600         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37601             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37602                                 n.nextSibling.ui.getEl(), buf.join(""));
37603         }else{
37604             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37605         }
37606
37607         this.elNode = this.wrap.childNodes[0];
37608         this.ctNode = this.wrap.childNodes[1];
37609         var cs = this.elNode.childNodes;
37610         this.indentNode = cs[0];
37611         this.ecNode = cs[1];
37612         this.iconNode = cs[2];
37613         var index = 3;
37614         if(cb){
37615             this.checkbox = cs[3];
37616             index++;
37617         }
37618         this.anchor = cs[index];
37619         this.textNode = cs[index].firstChild;
37620     },
37621
37622     getAnchor : function(){
37623         return this.anchor;
37624     },
37625
37626     getTextEl : function(){
37627         return this.textNode;
37628     },
37629
37630     getIconEl : function(){
37631         return this.iconNode;
37632     },
37633
37634     isChecked : function(){
37635         return this.checkbox ? this.checkbox.checked : false;
37636     },
37637
37638     updateExpandIcon : function(){
37639         if(this.rendered){
37640             var n = this.node, c1, c2;
37641             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37642             var hasChild = n.hasChildNodes();
37643             if(hasChild){
37644                 if(n.expanded){
37645                     cls += "-minus";
37646                     c1 = "x-tree-node-collapsed";
37647                     c2 = "x-tree-node-expanded";
37648                 }else{
37649                     cls += "-plus";
37650                     c1 = "x-tree-node-expanded";
37651                     c2 = "x-tree-node-collapsed";
37652                 }
37653                 if(this.wasLeaf){
37654                     this.removeClass("x-tree-node-leaf");
37655                     this.wasLeaf = false;
37656                 }
37657                 if(this.c1 != c1 || this.c2 != c2){
37658                     Roo.fly(this.elNode).replaceClass(c1, c2);
37659                     this.c1 = c1; this.c2 = c2;
37660                 }
37661             }else{
37662                 // this changes non-leafs into leafs if they have no children.
37663                 // it's not very rational behaviour..
37664                 
37665                 if(!this.wasLeaf && this.node.leaf){
37666                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37667                     delete this.c1;
37668                     delete this.c2;
37669                     this.wasLeaf = true;
37670                 }
37671             }
37672             var ecc = "x-tree-ec-icon "+cls;
37673             if(this.ecc != ecc){
37674                 this.ecNode.className = ecc;
37675                 this.ecc = ecc;
37676             }
37677         }
37678     },
37679
37680     getChildIndent : function(){
37681         if(!this.childIndent){
37682             var buf = [];
37683             var p = this.node;
37684             while(p){
37685                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37686                     if(!p.isLast()) {
37687                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37688                     } else {
37689                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37690                     }
37691                 }
37692                 p = p.parentNode;
37693             }
37694             this.childIndent = buf.join("");
37695         }
37696         return this.childIndent;
37697     },
37698
37699     renderIndent : function(){
37700         if(this.rendered){
37701             var indent = "";
37702             var p = this.node.parentNode;
37703             if(p){
37704                 indent = p.ui.getChildIndent();
37705             }
37706             if(this.indentMarkup != indent){ // don't rerender if not required
37707                 this.indentNode.innerHTML = indent;
37708                 this.indentMarkup = indent;
37709             }
37710             this.updateExpandIcon();
37711         }
37712     }
37713 };
37714
37715 Roo.tree.RootTreeNodeUI = function(){
37716     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37717 };
37718 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37719     render : function(){
37720         if(!this.rendered){
37721             var targetNode = this.node.ownerTree.innerCt.dom;
37722             this.node.expanded = true;
37723             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37724             this.wrap = this.ctNode = targetNode.firstChild;
37725         }
37726     },
37727     collapse : function(){
37728     },
37729     expand : function(){
37730     }
37731 });/*
37732  * Based on:
37733  * Ext JS Library 1.1.1
37734  * Copyright(c) 2006-2007, Ext JS, LLC.
37735  *
37736  * Originally Released Under LGPL - original licence link has changed is not relivant.
37737  *
37738  * Fork - LGPL
37739  * <script type="text/javascript">
37740  */
37741 /**
37742  * @class Roo.tree.TreeLoader
37743  * @extends Roo.util.Observable
37744  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37745  * nodes from a specified URL. The response must be a javascript Array definition
37746  * who's elements are node definition objects. eg:
37747  * <pre><code>
37748 {  success : true,
37749    data :      [
37750    
37751     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37752     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37753     ]
37754 }
37755
37756
37757 </code></pre>
37758  * <br><br>
37759  * The old style respose with just an array is still supported, but not recommended.
37760  * <br><br>
37761  *
37762  * A server request is sent, and child nodes are loaded only when a node is expanded.
37763  * The loading node's id is passed to the server under the parameter name "node" to
37764  * enable the server to produce the correct child nodes.
37765  * <br><br>
37766  * To pass extra parameters, an event handler may be attached to the "beforeload"
37767  * event, and the parameters specified in the TreeLoader's baseParams property:
37768  * <pre><code>
37769     myTreeLoader.on("beforeload", function(treeLoader, node) {
37770         this.baseParams.category = node.attributes.category;
37771     }, this);
37772     
37773 </code></pre>
37774  *
37775  * This would pass an HTTP parameter called "category" to the server containing
37776  * the value of the Node's "category" attribute.
37777  * @constructor
37778  * Creates a new Treeloader.
37779  * @param {Object} config A config object containing config properties.
37780  */
37781 Roo.tree.TreeLoader = function(config){
37782     this.baseParams = {};
37783     this.requestMethod = "POST";
37784     Roo.apply(this, config);
37785
37786     this.addEvents({
37787     
37788         /**
37789          * @event beforeload
37790          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37791          * @param {Object} This TreeLoader object.
37792          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37793          * @param {Object} callback The callback function specified in the {@link #load} call.
37794          */
37795         beforeload : true,
37796         /**
37797          * @event load
37798          * Fires when the node has been successfuly loaded.
37799          * @param {Object} This TreeLoader object.
37800          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37801          * @param {Object} response The response object containing the data from the server.
37802          */
37803         load : true,
37804         /**
37805          * @event loadexception
37806          * Fires if the network request failed.
37807          * @param {Object} This TreeLoader object.
37808          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37809          * @param {Object} response The response object containing the data from the server.
37810          */
37811         loadexception : true,
37812         /**
37813          * @event create
37814          * Fires before a node is created, enabling you to return custom Node types 
37815          * @param {Object} This TreeLoader object.
37816          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37817          */
37818         create : true
37819     });
37820
37821     Roo.tree.TreeLoader.superclass.constructor.call(this);
37822 };
37823
37824 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37825     /**
37826     * @cfg {String} dataUrl The URL from which to request a Json string which
37827     * specifies an array of node definition object representing the child nodes
37828     * to be loaded.
37829     */
37830     /**
37831     * @cfg {String} requestMethod either GET or POST
37832     * defaults to POST (due to BC)
37833     * to be loaded.
37834     */
37835     /**
37836     * @cfg {Object} baseParams (optional) An object containing properties which
37837     * specify HTTP parameters to be passed to each request for child nodes.
37838     */
37839     /**
37840     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37841     * created by this loader. If the attributes sent by the server have an attribute in this object,
37842     * they take priority.
37843     */
37844     /**
37845     * @cfg {Object} uiProviders (optional) An object containing properties which
37846     * 
37847     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37848     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37849     * <i>uiProvider</i> attribute of a returned child node is a string rather
37850     * than a reference to a TreeNodeUI implementation, this that string value
37851     * is used as a property name in the uiProviders object. You can define the provider named
37852     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37853     */
37854     uiProviders : {},
37855
37856     /**
37857     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37858     * child nodes before loading.
37859     */
37860     clearOnLoad : true,
37861
37862     /**
37863     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37864     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37865     * Grid query { data : [ .....] }
37866     */
37867     
37868     root : false,
37869      /**
37870     * @cfg {String} queryParam (optional) 
37871     * Name of the query as it will be passed on the querystring (defaults to 'node')
37872     * eg. the request will be ?node=[id]
37873     */
37874     
37875     
37876     queryParam: false,
37877     
37878     /**
37879      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37880      * This is called automatically when a node is expanded, but may be used to reload
37881      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37882      * @param {Roo.tree.TreeNode} node
37883      * @param {Function} callback
37884      */
37885     load : function(node, callback){
37886         if(this.clearOnLoad){
37887             while(node.firstChild){
37888                 node.removeChild(node.firstChild);
37889             }
37890         }
37891         if(node.attributes.children){ // preloaded json children
37892             var cs = node.attributes.children;
37893             for(var i = 0, len = cs.length; i < len; i++){
37894                 node.appendChild(this.createNode(cs[i]));
37895             }
37896             if(typeof callback == "function"){
37897                 callback();
37898             }
37899         }else if(this.dataUrl){
37900             this.requestData(node, callback);
37901         }
37902     },
37903
37904     getParams: function(node){
37905         var buf = [], bp = this.baseParams;
37906         for(var key in bp){
37907             if(typeof bp[key] != "function"){
37908                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37909             }
37910         }
37911         var n = this.queryParam === false ? 'node' : this.queryParam;
37912         buf.push(n + "=", encodeURIComponent(node.id));
37913         return buf.join("");
37914     },
37915
37916     requestData : function(node, callback){
37917         if(this.fireEvent("beforeload", this, node, callback) !== false){
37918             this.transId = Roo.Ajax.request({
37919                 method:this.requestMethod,
37920                 url: this.dataUrl||this.url,
37921                 success: this.handleResponse,
37922                 failure: this.handleFailure,
37923                 scope: this,
37924                 argument: {callback: callback, node: node},
37925                 params: this.getParams(node)
37926             });
37927         }else{
37928             // if the load is cancelled, make sure we notify
37929             // the node that we are done
37930             if(typeof callback == "function"){
37931                 callback();
37932             }
37933         }
37934     },
37935
37936     isLoading : function(){
37937         return this.transId ? true : false;
37938     },
37939
37940     abort : function(){
37941         if(this.isLoading()){
37942             Roo.Ajax.abort(this.transId);
37943         }
37944     },
37945
37946     // private
37947     createNode : function(attr)
37948     {
37949         // apply baseAttrs, nice idea Corey!
37950         if(this.baseAttrs){
37951             Roo.applyIf(attr, this.baseAttrs);
37952         }
37953         if(this.applyLoader !== false){
37954             attr.loader = this;
37955         }
37956         // uiProvider = depreciated..
37957         
37958         if(typeof(attr.uiProvider) == 'string'){
37959            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37960                 /**  eval:var:attr */ eval(attr.uiProvider);
37961         }
37962         if(typeof(this.uiProviders['default']) != 'undefined') {
37963             attr.uiProvider = this.uiProviders['default'];
37964         }
37965         
37966         this.fireEvent('create', this, attr);
37967         
37968         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37969         return(attr.leaf ?
37970                         new Roo.tree.TreeNode(attr) :
37971                         new Roo.tree.AsyncTreeNode(attr));
37972     },
37973
37974     processResponse : function(response, node, callback)
37975     {
37976         var json = response.responseText;
37977         try {
37978             
37979             var o = Roo.decode(json);
37980             
37981             if (this.root === false && typeof(o.success) != undefined) {
37982                 this.root = 'data'; // the default behaviour for list like data..
37983                 }
37984                 
37985             if (this.root !== false &&  !o.success) {
37986                 // it's a failure condition.
37987                 var a = response.argument;
37988                 this.fireEvent("loadexception", this, a.node, response);
37989                 Roo.log("Load failed - should have a handler really");
37990                 return;
37991             }
37992             
37993             
37994             
37995             if (this.root !== false) {
37996                  o = o[this.root];
37997             }
37998             
37999             for(var i = 0, len = o.length; i < len; i++){
38000                 var n = this.createNode(o[i]);
38001                 if(n){
38002                     node.appendChild(n);
38003                 }
38004             }
38005             if(typeof callback == "function"){
38006                 callback(this, node);
38007             }
38008         }catch(e){
38009             this.handleFailure(response);
38010         }
38011     },
38012
38013     handleResponse : function(response){
38014         this.transId = false;
38015         var a = response.argument;
38016         this.processResponse(response, a.node, a.callback);
38017         this.fireEvent("load", this, a.node, response);
38018     },
38019
38020     handleFailure : function(response)
38021     {
38022         // should handle failure better..
38023         this.transId = false;
38024         var a = response.argument;
38025         this.fireEvent("loadexception", this, a.node, response);
38026         if(typeof a.callback == "function"){
38027             a.callback(this, a.node);
38028         }
38029     }
38030 });/*
38031  * Based on:
38032  * Ext JS Library 1.1.1
38033  * Copyright(c) 2006-2007, Ext JS, LLC.
38034  *
38035  * Originally Released Under LGPL - original licence link has changed is not relivant.
38036  *
38037  * Fork - LGPL
38038  * <script type="text/javascript">
38039  */
38040
38041 /**
38042 * @class Roo.tree.TreeFilter
38043 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
38044 * @param {TreePanel} tree
38045 * @param {Object} config (optional)
38046  */
38047 Roo.tree.TreeFilter = function(tree, config){
38048     this.tree = tree;
38049     this.filtered = {};
38050     Roo.apply(this, config);
38051 };
38052
38053 Roo.tree.TreeFilter.prototype = {
38054     clearBlank:false,
38055     reverse:false,
38056     autoClear:false,
38057     remove:false,
38058
38059      /**
38060      * Filter the data by a specific attribute.
38061      * @param {String/RegExp} value Either string that the attribute value
38062      * should start with or a RegExp to test against the attribute
38063      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
38064      * @param {TreeNode} startNode (optional) The node to start the filter at.
38065      */
38066     filter : function(value, attr, startNode){
38067         attr = attr || "text";
38068         var f;
38069         if(typeof value == "string"){
38070             var vlen = value.length;
38071             // auto clear empty filter
38072             if(vlen == 0 && this.clearBlank){
38073                 this.clear();
38074                 return;
38075             }
38076             value = value.toLowerCase();
38077             f = function(n){
38078                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
38079             };
38080         }else if(value.exec){ // regex?
38081             f = function(n){
38082                 return value.test(n.attributes[attr]);
38083             };
38084         }else{
38085             throw 'Illegal filter type, must be string or regex';
38086         }
38087         this.filterBy(f, null, startNode);
38088         },
38089
38090     /**
38091      * Filter by a function. The passed function will be called with each
38092      * node in the tree (or from the startNode). If the function returns true, the node is kept
38093      * otherwise it is filtered. If a node is filtered, its children are also filtered.
38094      * @param {Function} fn The filter function
38095      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
38096      */
38097     filterBy : function(fn, scope, startNode){
38098         startNode = startNode || this.tree.root;
38099         if(this.autoClear){
38100             this.clear();
38101         }
38102         var af = this.filtered, rv = this.reverse;
38103         var f = function(n){
38104             if(n == startNode){
38105                 return true;
38106             }
38107             if(af[n.id]){
38108                 return false;
38109             }
38110             var m = fn.call(scope || n, n);
38111             if(!m || rv){
38112                 af[n.id] = n;
38113                 n.ui.hide();
38114                 return false;
38115             }
38116             return true;
38117         };
38118         startNode.cascade(f);
38119         if(this.remove){
38120            for(var id in af){
38121                if(typeof id != "function"){
38122                    var n = af[id];
38123                    if(n && n.parentNode){
38124                        n.parentNode.removeChild(n);
38125                    }
38126                }
38127            }
38128         }
38129     },
38130
38131     /**
38132      * Clears the current filter. Note: with the "remove" option
38133      * set a filter cannot be cleared.
38134      */
38135     clear : function(){
38136         var t = this.tree;
38137         var af = this.filtered;
38138         for(var id in af){
38139             if(typeof id != "function"){
38140                 var n = af[id];
38141                 if(n){
38142                     n.ui.show();
38143                 }
38144             }
38145         }
38146         this.filtered = {};
38147     }
38148 };
38149 /*
38150  * Based on:
38151  * Ext JS Library 1.1.1
38152  * Copyright(c) 2006-2007, Ext JS, LLC.
38153  *
38154  * Originally Released Under LGPL - original licence link has changed is not relivant.
38155  *
38156  * Fork - LGPL
38157  * <script type="text/javascript">
38158  */
38159  
38160
38161 /**
38162  * @class Roo.tree.TreeSorter
38163  * Provides sorting of nodes in a TreePanel
38164  * 
38165  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
38166  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
38167  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
38168  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
38169  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
38170  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
38171  * @constructor
38172  * @param {TreePanel} tree
38173  * @param {Object} config
38174  */
38175 Roo.tree.TreeSorter = function(tree, config){
38176     Roo.apply(this, config);
38177     tree.on("beforechildrenrendered", this.doSort, this);
38178     tree.on("append", this.updateSort, this);
38179     tree.on("insert", this.updateSort, this);
38180     
38181     var dsc = this.dir && this.dir.toLowerCase() == "desc";
38182     var p = this.property || "text";
38183     var sortType = this.sortType;
38184     var fs = this.folderSort;
38185     var cs = this.caseSensitive === true;
38186     var leafAttr = this.leafAttr || 'leaf';
38187
38188     this.sortFn = function(n1, n2){
38189         if(fs){
38190             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
38191                 return 1;
38192             }
38193             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
38194                 return -1;
38195             }
38196         }
38197         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
38198         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
38199         if(v1 < v2){
38200                         return dsc ? +1 : -1;
38201                 }else if(v1 > v2){
38202                         return dsc ? -1 : +1;
38203         }else{
38204                 return 0;
38205         }
38206     };
38207 };
38208
38209 Roo.tree.TreeSorter.prototype = {
38210     doSort : function(node){
38211         node.sort(this.sortFn);
38212     },
38213     
38214     compareNodes : function(n1, n2){
38215         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
38216     },
38217     
38218     updateSort : function(tree, node){
38219         if(node.childrenRendered){
38220             this.doSort.defer(1, this, [node]);
38221         }
38222     }
38223 };/*
38224  * Based on:
38225  * Ext JS Library 1.1.1
38226  * Copyright(c) 2006-2007, Ext JS, LLC.
38227  *
38228  * Originally Released Under LGPL - original licence link has changed is not relivant.
38229  *
38230  * Fork - LGPL
38231  * <script type="text/javascript">
38232  */
38233
38234 if(Roo.dd.DropZone){
38235     
38236 Roo.tree.TreeDropZone = function(tree, config){
38237     this.allowParentInsert = false;
38238     this.allowContainerDrop = false;
38239     this.appendOnly = false;
38240     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38241     this.tree = tree;
38242     this.lastInsertClass = "x-tree-no-status";
38243     this.dragOverData = {};
38244 };
38245
38246 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38247     ddGroup : "TreeDD",
38248     scroll:  true,
38249     
38250     expandDelay : 1000,
38251     
38252     expandNode : function(node){
38253         if(node.hasChildNodes() && !node.isExpanded()){
38254             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38255         }
38256     },
38257     
38258     queueExpand : function(node){
38259         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38260     },
38261     
38262     cancelExpand : function(){
38263         if(this.expandProcId){
38264             clearTimeout(this.expandProcId);
38265             this.expandProcId = false;
38266         }
38267     },
38268     
38269     isValidDropPoint : function(n, pt, dd, e, data){
38270         if(!n || !data){ return false; }
38271         var targetNode = n.node;
38272         var dropNode = data.node;
38273         // default drop rules
38274         if(!(targetNode && targetNode.isTarget && pt)){
38275             return false;
38276         }
38277         if(pt == "append" && targetNode.allowChildren === false){
38278             return false;
38279         }
38280         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38281             return false;
38282         }
38283         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38284             return false;
38285         }
38286         // reuse the object
38287         var overEvent = this.dragOverData;
38288         overEvent.tree = this.tree;
38289         overEvent.target = targetNode;
38290         overEvent.data = data;
38291         overEvent.point = pt;
38292         overEvent.source = dd;
38293         overEvent.rawEvent = e;
38294         overEvent.dropNode = dropNode;
38295         overEvent.cancel = false;  
38296         var result = this.tree.fireEvent("nodedragover", overEvent);
38297         return overEvent.cancel === false && result !== false;
38298     },
38299     
38300     getDropPoint : function(e, n, dd)
38301     {
38302         var tn = n.node;
38303         if(tn.isRoot){
38304             return tn.allowChildren !== false ? "append" : false; // always append for root
38305         }
38306         var dragEl = n.ddel;
38307         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38308         var y = Roo.lib.Event.getPageY(e);
38309         //var noAppend = tn.allowChildren === false || tn.isLeaf();
38310         
38311         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38312         var noAppend = tn.allowChildren === false;
38313         if(this.appendOnly || tn.parentNode.allowChildren === false){
38314             return noAppend ? false : "append";
38315         }
38316         var noBelow = false;
38317         if(!this.allowParentInsert){
38318             noBelow = tn.hasChildNodes() && tn.isExpanded();
38319         }
38320         var q = (b - t) / (noAppend ? 2 : 3);
38321         if(y >= t && y < (t + q)){
38322             return "above";
38323         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38324             return "below";
38325         }else{
38326             return "append";
38327         }
38328     },
38329     
38330     onNodeEnter : function(n, dd, e, data)
38331     {
38332         this.cancelExpand();
38333     },
38334     
38335     onNodeOver : function(n, dd, e, data)
38336     {
38337        
38338         var pt = this.getDropPoint(e, n, dd);
38339         var node = n.node;
38340         
38341         // auto node expand check
38342         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38343             this.queueExpand(node);
38344         }else if(pt != "append"){
38345             this.cancelExpand();
38346         }
38347         
38348         // set the insert point style on the target node
38349         var returnCls = this.dropNotAllowed;
38350         if(this.isValidDropPoint(n, pt, dd, e, data)){
38351            if(pt){
38352                var el = n.ddel;
38353                var cls;
38354                if(pt == "above"){
38355                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38356                    cls = "x-tree-drag-insert-above";
38357                }else if(pt == "below"){
38358                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38359                    cls = "x-tree-drag-insert-below";
38360                }else{
38361                    returnCls = "x-tree-drop-ok-append";
38362                    cls = "x-tree-drag-append";
38363                }
38364                if(this.lastInsertClass != cls){
38365                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38366                    this.lastInsertClass = cls;
38367                }
38368            }
38369        }
38370        return returnCls;
38371     },
38372     
38373     onNodeOut : function(n, dd, e, data){
38374         
38375         this.cancelExpand();
38376         this.removeDropIndicators(n);
38377     },
38378     
38379     onNodeDrop : function(n, dd, e, data){
38380         var point = this.getDropPoint(e, n, dd);
38381         var targetNode = n.node;
38382         targetNode.ui.startDrop();
38383         if(!this.isValidDropPoint(n, point, dd, e, data)){
38384             targetNode.ui.endDrop();
38385             return false;
38386         }
38387         // first try to find the drop node
38388         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38389         var dropEvent = {
38390             tree : this.tree,
38391             target: targetNode,
38392             data: data,
38393             point: point,
38394             source: dd,
38395             rawEvent: e,
38396             dropNode: dropNode,
38397             cancel: !dropNode   
38398         };
38399         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38400         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38401             targetNode.ui.endDrop();
38402             return false;
38403         }
38404         // allow target changing
38405         targetNode = dropEvent.target;
38406         if(point == "append" && !targetNode.isExpanded()){
38407             targetNode.expand(false, null, function(){
38408                 this.completeDrop(dropEvent);
38409             }.createDelegate(this));
38410         }else{
38411             this.completeDrop(dropEvent);
38412         }
38413         return true;
38414     },
38415     
38416     completeDrop : function(de){
38417         var ns = de.dropNode, p = de.point, t = de.target;
38418         if(!(ns instanceof Array)){
38419             ns = [ns];
38420         }
38421         var n;
38422         for(var i = 0, len = ns.length; i < len; i++){
38423             n = ns[i];
38424             if(p == "above"){
38425                 t.parentNode.insertBefore(n, t);
38426             }else if(p == "below"){
38427                 t.parentNode.insertBefore(n, t.nextSibling);
38428             }else{
38429                 t.appendChild(n);
38430             }
38431         }
38432         n.ui.focus();
38433         if(this.tree.hlDrop){
38434             n.ui.highlight();
38435         }
38436         t.ui.endDrop();
38437         this.tree.fireEvent("nodedrop", de);
38438     },
38439     
38440     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38441         if(this.tree.hlDrop){
38442             dropNode.ui.focus();
38443             dropNode.ui.highlight();
38444         }
38445         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38446     },
38447     
38448     getTree : function(){
38449         return this.tree;
38450     },
38451     
38452     removeDropIndicators : function(n){
38453         if(n && n.ddel){
38454             var el = n.ddel;
38455             Roo.fly(el).removeClass([
38456                     "x-tree-drag-insert-above",
38457                     "x-tree-drag-insert-below",
38458                     "x-tree-drag-append"]);
38459             this.lastInsertClass = "_noclass";
38460         }
38461     },
38462     
38463     beforeDragDrop : function(target, e, id){
38464         this.cancelExpand();
38465         return true;
38466     },
38467     
38468     afterRepair : function(data){
38469         if(data && Roo.enableFx){
38470             data.node.ui.highlight();
38471         }
38472         this.hideProxy();
38473     } 
38474     
38475 });
38476
38477 }
38478 /*
38479  * Based on:
38480  * Ext JS Library 1.1.1
38481  * Copyright(c) 2006-2007, Ext JS, LLC.
38482  *
38483  * Originally Released Under LGPL - original licence link has changed is not relivant.
38484  *
38485  * Fork - LGPL
38486  * <script type="text/javascript">
38487  */
38488  
38489
38490 if(Roo.dd.DragZone){
38491 Roo.tree.TreeDragZone = function(tree, config){
38492     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38493     this.tree = tree;
38494 };
38495
38496 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38497     ddGroup : "TreeDD",
38498    
38499     onBeforeDrag : function(data, e){
38500         var n = data.node;
38501         return n && n.draggable && !n.disabled;
38502     },
38503      
38504     
38505     onInitDrag : function(e){
38506         var data = this.dragData;
38507         this.tree.getSelectionModel().select(data.node);
38508         this.proxy.update("");
38509         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38510         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38511     },
38512     
38513     getRepairXY : function(e, data){
38514         return data.node.ui.getDDRepairXY();
38515     },
38516     
38517     onEndDrag : function(data, e){
38518         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38519         
38520         
38521     },
38522     
38523     onValidDrop : function(dd, e, id){
38524         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38525         this.hideProxy();
38526     },
38527     
38528     beforeInvalidDrop : function(e, id){
38529         // this scrolls the original position back into view
38530         var sm = this.tree.getSelectionModel();
38531         sm.clearSelections();
38532         sm.select(this.dragData.node);
38533     }
38534 });
38535 }/*
38536  * Based on:
38537  * Ext JS Library 1.1.1
38538  * Copyright(c) 2006-2007, Ext JS, LLC.
38539  *
38540  * Originally Released Under LGPL - original licence link has changed is not relivant.
38541  *
38542  * Fork - LGPL
38543  * <script type="text/javascript">
38544  */
38545 /**
38546  * @class Roo.tree.TreeEditor
38547  * @extends Roo.Editor
38548  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38549  * as the editor field.
38550  * @constructor
38551  * @param {Object} config (used to be the tree panel.)
38552  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38553  * 
38554  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38555  * @cfg {Roo.form.TextField} field [required] The field configuration
38556  *
38557  * 
38558  */
38559 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38560     var tree = config;
38561     var field;
38562     if (oldconfig) { // old style..
38563         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38564     } else {
38565         // new style..
38566         tree = config.tree;
38567         config.field = config.field  || {};
38568         config.field.xtype = 'TextField';
38569         field = Roo.factory(config.field, Roo.form);
38570     }
38571     config = config || {};
38572     
38573     
38574     this.addEvents({
38575         /**
38576          * @event beforenodeedit
38577          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38578          * false from the handler of this event.
38579          * @param {Editor} this
38580          * @param {Roo.tree.Node} node 
38581          */
38582         "beforenodeedit" : true
38583     });
38584     
38585     //Roo.log(config);
38586     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38587
38588     this.tree = tree;
38589
38590     tree.on('beforeclick', this.beforeNodeClick, this);
38591     tree.getTreeEl().on('mousedown', this.hide, this);
38592     this.on('complete', this.updateNode, this);
38593     this.on('beforestartedit', this.fitToTree, this);
38594     this.on('startedit', this.bindScroll, this, {delay:10});
38595     this.on('specialkey', this.onSpecialKey, this);
38596 };
38597
38598 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38599     /**
38600      * @cfg {String} alignment
38601      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38602      */
38603     alignment: "l-l",
38604     // inherit
38605     autoSize: false,
38606     /**
38607      * @cfg {Boolean} hideEl
38608      * True to hide the bound element while the editor is displayed (defaults to false)
38609      */
38610     hideEl : false,
38611     /**
38612      * @cfg {String} cls
38613      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38614      */
38615     cls: "x-small-editor x-tree-editor",
38616     /**
38617      * @cfg {Boolean} shim
38618      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38619      */
38620     shim:false,
38621     // inherit
38622     shadow:"frame",
38623     /**
38624      * @cfg {Number} maxWidth
38625      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38626      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38627      * scroll and client offsets into account prior to each edit.
38628      */
38629     maxWidth: 250,
38630
38631     editDelay : 350,
38632
38633     // private
38634     fitToTree : function(ed, el){
38635         var td = this.tree.getTreeEl().dom, nd = el.dom;
38636         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38637             td.scrollLeft = nd.offsetLeft;
38638         }
38639         var w = Math.min(
38640                 this.maxWidth,
38641                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38642         this.setSize(w, '');
38643         
38644         return this.fireEvent('beforenodeedit', this, this.editNode);
38645         
38646     },
38647
38648     // private
38649     triggerEdit : function(node){
38650         this.completeEdit();
38651         this.editNode = node;
38652         this.startEdit(node.ui.textNode, node.text);
38653     },
38654
38655     // private
38656     bindScroll : function(){
38657         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38658     },
38659
38660     // private
38661     beforeNodeClick : function(node, e){
38662         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38663         this.lastClick = new Date();
38664         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38665             e.stopEvent();
38666             this.triggerEdit(node);
38667             return false;
38668         }
38669         return true;
38670     },
38671
38672     // private
38673     updateNode : function(ed, value){
38674         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38675         this.editNode.setText(value);
38676     },
38677
38678     // private
38679     onHide : function(){
38680         Roo.tree.TreeEditor.superclass.onHide.call(this);
38681         if(this.editNode){
38682             this.editNode.ui.focus();
38683         }
38684     },
38685
38686     // private
38687     onSpecialKey : function(field, e){
38688         var k = e.getKey();
38689         if(k == e.ESC){
38690             e.stopEvent();
38691             this.cancelEdit();
38692         }else if(k == e.ENTER && !e.hasModifier()){
38693             e.stopEvent();
38694             this.completeEdit();
38695         }
38696     }
38697 });//<Script type="text/javascript">
38698 /*
38699  * Based on:
38700  * Ext JS Library 1.1.1
38701  * Copyright(c) 2006-2007, Ext JS, LLC.
38702  *
38703  * Originally Released Under LGPL - original licence link has changed is not relivant.
38704  *
38705  * Fork - LGPL
38706  * <script type="text/javascript">
38707  */
38708  
38709 /**
38710  * Not documented??? - probably should be...
38711  */
38712
38713 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38714     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38715     
38716     renderElements : function(n, a, targetNode, bulkRender){
38717         //consel.log("renderElements?");
38718         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38719
38720         var t = n.getOwnerTree();
38721         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38722         
38723         var cols = t.columns;
38724         var bw = t.borderWidth;
38725         var c = cols[0];
38726         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38727          var cb = typeof a.checked == "boolean";
38728         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38729         var colcls = 'x-t-' + tid + '-c0';
38730         var buf = [
38731             '<li class="x-tree-node">',
38732             
38733                 
38734                 '<div class="x-tree-node-el ', a.cls,'">',
38735                     // extran...
38736                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38737                 
38738                 
38739                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38740                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38741                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38742                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38743                            (a.iconCls ? ' '+a.iconCls : ''),
38744                            '" unselectable="on" />',
38745                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38746                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38747                              
38748                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38749                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38750                             '<span unselectable="on" qtip="' + tx + '">',
38751                              tx,
38752                              '</span></a>' ,
38753                     '</div>',
38754                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38755                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38756                  ];
38757         for(var i = 1, len = cols.length; i < len; i++){
38758             c = cols[i];
38759             colcls = 'x-t-' + tid + '-c' +i;
38760             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38761             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38762                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38763                       "</div>");
38764          }
38765          
38766          buf.push(
38767             '</a>',
38768             '<div class="x-clear"></div></div>',
38769             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38770             "</li>");
38771         
38772         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38773             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38774                                 n.nextSibling.ui.getEl(), buf.join(""));
38775         }else{
38776             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38777         }
38778         var el = this.wrap.firstChild;
38779         this.elRow = el;
38780         this.elNode = el.firstChild;
38781         this.ranchor = el.childNodes[1];
38782         this.ctNode = this.wrap.childNodes[1];
38783         var cs = el.firstChild.childNodes;
38784         this.indentNode = cs[0];
38785         this.ecNode = cs[1];
38786         this.iconNode = cs[2];
38787         var index = 3;
38788         if(cb){
38789             this.checkbox = cs[3];
38790             index++;
38791         }
38792         this.anchor = cs[index];
38793         
38794         this.textNode = cs[index].firstChild;
38795         
38796         //el.on("click", this.onClick, this);
38797         //el.on("dblclick", this.onDblClick, this);
38798         
38799         
38800        // console.log(this);
38801     },
38802     initEvents : function(){
38803         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38804         
38805             
38806         var a = this.ranchor;
38807
38808         var el = Roo.get(a);
38809
38810         if(Roo.isOpera){ // opera render bug ignores the CSS
38811             el.setStyle("text-decoration", "none");
38812         }
38813
38814         el.on("click", this.onClick, this);
38815         el.on("dblclick", this.onDblClick, this);
38816         el.on("contextmenu", this.onContextMenu, this);
38817         
38818     },
38819     
38820     /*onSelectedChange : function(state){
38821         if(state){
38822             this.focus();
38823             this.addClass("x-tree-selected");
38824         }else{
38825             //this.blur();
38826             this.removeClass("x-tree-selected");
38827         }
38828     },*/
38829     addClass : function(cls){
38830         if(this.elRow){
38831             Roo.fly(this.elRow).addClass(cls);
38832         }
38833         
38834     },
38835     
38836     
38837     removeClass : function(cls){
38838         if(this.elRow){
38839             Roo.fly(this.elRow).removeClass(cls);
38840         }
38841     }
38842
38843     
38844     
38845 });//<Script type="text/javascript">
38846
38847 /*
38848  * Based on:
38849  * Ext JS Library 1.1.1
38850  * Copyright(c) 2006-2007, Ext JS, LLC.
38851  *
38852  * Originally Released Under LGPL - original licence link has changed is not relivant.
38853  *
38854  * Fork - LGPL
38855  * <script type="text/javascript">
38856  */
38857  
38858
38859 /**
38860  * @class Roo.tree.ColumnTree
38861  * @extends Roo.tree.TreePanel
38862  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38863  * @cfg {int} borderWidth  compined right/left border allowance
38864  * @constructor
38865  * @param {String/HTMLElement/Element} el The container element
38866  * @param {Object} config
38867  */
38868 Roo.tree.ColumnTree =  function(el, config)
38869 {
38870    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38871    this.addEvents({
38872         /**
38873         * @event resize
38874         * Fire this event on a container when it resizes
38875         * @param {int} w Width
38876         * @param {int} h Height
38877         */
38878        "resize" : true
38879     });
38880     this.on('resize', this.onResize, this);
38881 };
38882
38883 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38884     //lines:false,
38885     
38886     
38887     borderWidth: Roo.isBorderBox ? 0 : 2, 
38888     headEls : false,
38889     
38890     render : function(){
38891         // add the header.....
38892        
38893         Roo.tree.ColumnTree.superclass.render.apply(this);
38894         
38895         this.el.addClass('x-column-tree');
38896         
38897         this.headers = this.el.createChild(
38898             {cls:'x-tree-headers'},this.innerCt.dom);
38899    
38900         var cols = this.columns, c;
38901         var totalWidth = 0;
38902         this.headEls = [];
38903         var  len = cols.length;
38904         for(var i = 0; i < len; i++){
38905              c = cols[i];
38906              totalWidth += c.width;
38907             this.headEls.push(this.headers.createChild({
38908                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38909                  cn: {
38910                      cls:'x-tree-hd-text',
38911                      html: c.header
38912                  },
38913                  style:'width:'+(c.width-this.borderWidth)+'px;'
38914              }));
38915         }
38916         this.headers.createChild({cls:'x-clear'});
38917         // prevent floats from wrapping when clipped
38918         this.headers.setWidth(totalWidth);
38919         //this.innerCt.setWidth(totalWidth);
38920         this.innerCt.setStyle({ overflow: 'auto' });
38921         this.onResize(this.width, this.height);
38922              
38923         
38924     },
38925     onResize : function(w,h)
38926     {
38927         this.height = h;
38928         this.width = w;
38929         // resize cols..
38930         this.innerCt.setWidth(this.width);
38931         this.innerCt.setHeight(this.height-20);
38932         
38933         // headers...
38934         var cols = this.columns, c;
38935         var totalWidth = 0;
38936         var expEl = false;
38937         var len = cols.length;
38938         for(var i = 0; i < len; i++){
38939             c = cols[i];
38940             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38941                 // it's the expander..
38942                 expEl  = this.headEls[i];
38943                 continue;
38944             }
38945             totalWidth += c.width;
38946             
38947         }
38948         if (expEl) {
38949             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38950         }
38951         this.headers.setWidth(w-20);
38952
38953         
38954         
38955         
38956     }
38957 });
38958 /*
38959  * Based on:
38960  * Ext JS Library 1.1.1
38961  * Copyright(c) 2006-2007, Ext JS, LLC.
38962  *
38963  * Originally Released Under LGPL - original licence link has changed is not relivant.
38964  *
38965  * Fork - LGPL
38966  * <script type="text/javascript">
38967  */
38968  
38969 /**
38970  * @class Roo.menu.Menu
38971  * @extends Roo.util.Observable
38972  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38973  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38974  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38975  * @constructor
38976  * Creates a new Menu
38977  * @param {Object} config Configuration options
38978  */
38979 Roo.menu.Menu = function(config){
38980     
38981     Roo.menu.Menu.superclass.constructor.call(this, config);
38982     
38983     this.id = this.id || Roo.id();
38984     this.addEvents({
38985         /**
38986          * @event beforeshow
38987          * Fires before this menu is displayed
38988          * @param {Roo.menu.Menu} this
38989          */
38990         beforeshow : true,
38991         /**
38992          * @event beforehide
38993          * Fires before this menu is hidden
38994          * @param {Roo.menu.Menu} this
38995          */
38996         beforehide : true,
38997         /**
38998          * @event show
38999          * Fires after this menu is displayed
39000          * @param {Roo.menu.Menu} this
39001          */
39002         show : true,
39003         /**
39004          * @event hide
39005          * Fires after this menu is hidden
39006          * @param {Roo.menu.Menu} this
39007          */
39008         hide : true,
39009         /**
39010          * @event click
39011          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
39012          * @param {Roo.menu.Menu} this
39013          * @param {Roo.menu.Item} menuItem The menu item that was clicked
39014          * @param {Roo.EventObject} e
39015          */
39016         click : true,
39017         /**
39018          * @event mouseover
39019          * Fires when the mouse is hovering over this menu
39020          * @param {Roo.menu.Menu} this
39021          * @param {Roo.EventObject} e
39022          * @param {Roo.menu.Item} menuItem The menu item that was clicked
39023          */
39024         mouseover : true,
39025         /**
39026          * @event mouseout
39027          * Fires when the mouse exits this menu
39028          * @param {Roo.menu.Menu} this
39029          * @param {Roo.EventObject} e
39030          * @param {Roo.menu.Item} menuItem The menu item that was clicked
39031          */
39032         mouseout : true,
39033         /**
39034          * @event itemclick
39035          * Fires when a menu item contained in this menu is clicked
39036          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
39037          * @param {Roo.EventObject} e
39038          */
39039         itemclick: true
39040     });
39041     if (this.registerMenu) {
39042         Roo.menu.MenuMgr.register(this);
39043     }
39044     
39045     var mis = this.items;
39046     this.items = new Roo.util.MixedCollection();
39047     if(mis){
39048         this.add.apply(this, mis);
39049     }
39050 };
39051
39052 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
39053     /**
39054      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
39055      */
39056     minWidth : 120,
39057     /**
39058      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
39059      * for bottom-right shadow (defaults to "sides")
39060      */
39061     shadow : "sides",
39062     /**
39063      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
39064      * this menu (defaults to "tl-tr?")
39065      */
39066     subMenuAlign : "tl-tr?",
39067     /**
39068      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
39069      * relative to its element of origin (defaults to "tl-bl?")
39070      */
39071     defaultAlign : "tl-bl?",
39072     /**
39073      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
39074      */
39075     allowOtherMenus : false,
39076     /**
39077      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
39078      */
39079     registerMenu : true,
39080
39081     hidden:true,
39082
39083     // private
39084     render : function(){
39085         if(this.el){
39086             return;
39087         }
39088         var el = this.el = new Roo.Layer({
39089             cls: "x-menu",
39090             shadow:this.shadow,
39091             constrain: false,
39092             parentEl: this.parentEl || document.body,
39093             zindex:15000
39094         });
39095
39096         this.keyNav = new Roo.menu.MenuNav(this);
39097
39098         if(this.plain){
39099             el.addClass("x-menu-plain");
39100         }
39101         if(this.cls){
39102             el.addClass(this.cls);
39103         }
39104         // generic focus element
39105         this.focusEl = el.createChild({
39106             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
39107         });
39108         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
39109         //disabling touch- as it's causing issues ..
39110         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
39111         ul.on('click'   , this.onClick, this);
39112         
39113         
39114         ul.on("mouseover", this.onMouseOver, this);
39115         ul.on("mouseout", this.onMouseOut, this);
39116         this.items.each(function(item){
39117             if (item.hidden) {
39118                 return;
39119             }
39120             
39121             var li = document.createElement("li");
39122             li.className = "x-menu-list-item";
39123             ul.dom.appendChild(li);
39124             item.render(li, this);
39125         }, this);
39126         this.ul = ul;
39127         this.autoWidth();
39128     },
39129
39130     // private
39131     autoWidth : function(){
39132         var el = this.el, ul = this.ul;
39133         if(!el){
39134             return;
39135         }
39136         var w = this.width;
39137         if(w){
39138             el.setWidth(w);
39139         }else if(Roo.isIE){
39140             el.setWidth(this.minWidth);
39141             var t = el.dom.offsetWidth; // force recalc
39142             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
39143         }
39144     },
39145
39146     // private
39147     delayAutoWidth : function(){
39148         if(this.rendered){
39149             if(!this.awTask){
39150                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
39151             }
39152             this.awTask.delay(20);
39153         }
39154     },
39155
39156     // private
39157     findTargetItem : function(e){
39158         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
39159         if(t && t.menuItemId){
39160             return this.items.get(t.menuItemId);
39161         }
39162     },
39163
39164     // private
39165     onClick : function(e){
39166         Roo.log("menu.onClick");
39167         var t = this.findTargetItem(e);
39168         if(!t){
39169             return;
39170         }
39171         Roo.log(e);
39172         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
39173             if(t == this.activeItem && t.shouldDeactivate(e)){
39174                 this.activeItem.deactivate();
39175                 delete this.activeItem;
39176                 return;
39177             }
39178             if(t.canActivate){
39179                 this.setActiveItem(t, true);
39180             }
39181             return;
39182             
39183             
39184         }
39185         
39186         t.onClick(e);
39187         this.fireEvent("click", this, t, e);
39188     },
39189
39190     // private
39191     setActiveItem : function(item, autoExpand){
39192         if(item != this.activeItem){
39193             if(this.activeItem){
39194                 this.activeItem.deactivate();
39195             }
39196             this.activeItem = item;
39197             item.activate(autoExpand);
39198         }else if(autoExpand){
39199             item.expandMenu();
39200         }
39201     },
39202
39203     // private
39204     tryActivate : function(start, step){
39205         var items = this.items;
39206         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
39207             var item = items.get(i);
39208             if(!item.disabled && item.canActivate){
39209                 this.setActiveItem(item, false);
39210                 return item;
39211             }
39212         }
39213         return false;
39214     },
39215
39216     // private
39217     onMouseOver : function(e){
39218         var t;
39219         if(t = this.findTargetItem(e)){
39220             if(t.canActivate && !t.disabled){
39221                 this.setActiveItem(t, true);
39222             }
39223         }
39224         this.fireEvent("mouseover", this, e, t);
39225     },
39226
39227     // private
39228     onMouseOut : function(e){
39229         var t;
39230         if(t = this.findTargetItem(e)){
39231             if(t == this.activeItem && t.shouldDeactivate(e)){
39232                 this.activeItem.deactivate();
39233                 delete this.activeItem;
39234             }
39235         }
39236         this.fireEvent("mouseout", this, e, t);
39237     },
39238
39239     /**
39240      * Read-only.  Returns true if the menu is currently displayed, else false.
39241      * @type Boolean
39242      */
39243     isVisible : function(){
39244         return this.el && !this.hidden;
39245     },
39246
39247     /**
39248      * Displays this menu relative to another element
39249      * @param {String/HTMLElement/Roo.Element} element The element to align to
39250      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39251      * the element (defaults to this.defaultAlign)
39252      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39253      */
39254     show : function(el, pos, parentMenu){
39255         this.parentMenu = parentMenu;
39256         if(!this.el){
39257             this.render();
39258         }
39259         this.fireEvent("beforeshow", this);
39260         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39261     },
39262
39263     /**
39264      * Displays this menu at a specific xy position
39265      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39266      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39267      */
39268     showAt : function(xy, parentMenu, /* private: */_e){
39269         this.parentMenu = parentMenu;
39270         if(!this.el){
39271             this.render();
39272         }
39273         if(_e !== false){
39274             this.fireEvent("beforeshow", this);
39275             xy = this.el.adjustForConstraints(xy);
39276         }
39277         this.el.setXY(xy);
39278         this.el.show();
39279         this.hidden = false;
39280         this.focus();
39281         this.fireEvent("show", this);
39282     },
39283
39284     focus : function(){
39285         if(!this.hidden){
39286             this.doFocus.defer(50, this);
39287         }
39288     },
39289
39290     doFocus : function(){
39291         if(!this.hidden){
39292             this.focusEl.focus();
39293         }
39294     },
39295
39296     /**
39297      * Hides this menu and optionally all parent menus
39298      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39299      */
39300     hide : function(deep){
39301         if(this.el && this.isVisible()){
39302             this.fireEvent("beforehide", this);
39303             if(this.activeItem){
39304                 this.activeItem.deactivate();
39305                 this.activeItem = null;
39306             }
39307             this.el.hide();
39308             this.hidden = true;
39309             this.fireEvent("hide", this);
39310         }
39311         if(deep === true && this.parentMenu){
39312             this.parentMenu.hide(true);
39313         }
39314     },
39315
39316     /**
39317      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39318      * Any of the following are valid:
39319      * <ul>
39320      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39321      * <li>An HTMLElement object which will be converted to a menu item</li>
39322      * <li>A menu item config object that will be created as a new menu item</li>
39323      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39324      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39325      * </ul>
39326      * Usage:
39327      * <pre><code>
39328 // Create the menu
39329 var menu = new Roo.menu.Menu();
39330
39331 // Create a menu item to add by reference
39332 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39333
39334 // Add a bunch of items at once using different methods.
39335 // Only the last item added will be returned.
39336 var item = menu.add(
39337     menuItem,                // add existing item by ref
39338     'Dynamic Item',          // new TextItem
39339     '-',                     // new separator
39340     { text: 'Config Item' }  // new item by config
39341 );
39342 </code></pre>
39343      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39344      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39345      */
39346     add : function(){
39347         var a = arguments, l = a.length, item;
39348         for(var i = 0; i < l; i++){
39349             var el = a[i];
39350             if ((typeof(el) == "object") && el.xtype && el.xns) {
39351                 el = Roo.factory(el, Roo.menu);
39352             }
39353             
39354             if(el.render){ // some kind of Item
39355                 item = this.addItem(el);
39356             }else if(typeof el == "string"){ // string
39357                 if(el == "separator" || el == "-"){
39358                     item = this.addSeparator();
39359                 }else{
39360                     item = this.addText(el);
39361                 }
39362             }else if(el.tagName || el.el){ // element
39363                 item = this.addElement(el);
39364             }else if(typeof el == "object"){ // must be menu item config?
39365                 item = this.addMenuItem(el);
39366             }
39367         }
39368         return item;
39369     },
39370
39371     /**
39372      * Returns this menu's underlying {@link Roo.Element} object
39373      * @return {Roo.Element} The element
39374      */
39375     getEl : function(){
39376         if(!this.el){
39377             this.render();
39378         }
39379         return this.el;
39380     },
39381
39382     /**
39383      * Adds a separator bar to the menu
39384      * @return {Roo.menu.Item} The menu item that was added
39385      */
39386     addSeparator : function(){
39387         return this.addItem(new Roo.menu.Separator());
39388     },
39389
39390     /**
39391      * Adds an {@link Roo.Element} object to the menu
39392      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39393      * @return {Roo.menu.Item} The menu item that was added
39394      */
39395     addElement : function(el){
39396         return this.addItem(new Roo.menu.BaseItem(el));
39397     },
39398
39399     /**
39400      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39401      * @param {Roo.menu.Item} item The menu item to add
39402      * @return {Roo.menu.Item} The menu item that was added
39403      */
39404     addItem : function(item){
39405         this.items.add(item);
39406         if(this.ul){
39407             var li = document.createElement("li");
39408             li.className = "x-menu-list-item";
39409             this.ul.dom.appendChild(li);
39410             item.render(li, this);
39411             this.delayAutoWidth();
39412         }
39413         return item;
39414     },
39415
39416     /**
39417      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39418      * @param {Object} config A MenuItem config object
39419      * @return {Roo.menu.Item} The menu item that was added
39420      */
39421     addMenuItem : function(config){
39422         if(!(config instanceof Roo.menu.Item)){
39423             if(typeof config.checked == "boolean"){ // must be check menu item config?
39424                 config = new Roo.menu.CheckItem(config);
39425             }else{
39426                 config = new Roo.menu.Item(config);
39427             }
39428         }
39429         return this.addItem(config);
39430     },
39431
39432     /**
39433      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39434      * @param {String} text The text to display in the menu item
39435      * @return {Roo.menu.Item} The menu item that was added
39436      */
39437     addText : function(text){
39438         return this.addItem(new Roo.menu.TextItem({ text : text }));
39439     },
39440
39441     /**
39442      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39443      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39444      * @param {Roo.menu.Item} item The menu item to add
39445      * @return {Roo.menu.Item} The menu item that was added
39446      */
39447     insert : function(index, item){
39448         this.items.insert(index, item);
39449         if(this.ul){
39450             var li = document.createElement("li");
39451             li.className = "x-menu-list-item";
39452             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39453             item.render(li, this);
39454             this.delayAutoWidth();
39455         }
39456         return item;
39457     },
39458
39459     /**
39460      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39461      * @param {Roo.menu.Item} item The menu item to remove
39462      */
39463     remove : function(item){
39464         this.items.removeKey(item.id);
39465         item.destroy();
39466     },
39467
39468     /**
39469      * Removes and destroys all items in the menu
39470      */
39471     removeAll : function(){
39472         var f;
39473         while(f = this.items.first()){
39474             this.remove(f);
39475         }
39476     }
39477 });
39478
39479 // MenuNav is a private utility class used internally by the Menu
39480 Roo.menu.MenuNav = function(menu){
39481     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39482     this.scope = this.menu = menu;
39483 };
39484
39485 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39486     doRelay : function(e, h){
39487         var k = e.getKey();
39488         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39489             this.menu.tryActivate(0, 1);
39490             return false;
39491         }
39492         return h.call(this.scope || this, e, this.menu);
39493     },
39494
39495     up : function(e, m){
39496         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39497             m.tryActivate(m.items.length-1, -1);
39498         }
39499     },
39500
39501     down : function(e, m){
39502         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39503             m.tryActivate(0, 1);
39504         }
39505     },
39506
39507     right : function(e, m){
39508         if(m.activeItem){
39509             m.activeItem.expandMenu(true);
39510         }
39511     },
39512
39513     left : function(e, m){
39514         m.hide();
39515         if(m.parentMenu && m.parentMenu.activeItem){
39516             m.parentMenu.activeItem.activate();
39517         }
39518     },
39519
39520     enter : function(e, m){
39521         if(m.activeItem){
39522             e.stopPropagation();
39523             m.activeItem.onClick(e);
39524             m.fireEvent("click", this, m.activeItem);
39525             return true;
39526         }
39527     }
39528 });/*
39529  * Based on:
39530  * Ext JS Library 1.1.1
39531  * Copyright(c) 2006-2007, Ext JS, LLC.
39532  *
39533  * Originally Released Under LGPL - original licence link has changed is not relivant.
39534  *
39535  * Fork - LGPL
39536  * <script type="text/javascript">
39537  */
39538  
39539 /**
39540  * @class Roo.menu.MenuMgr
39541  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39542  * @static
39543  */
39544 Roo.menu.MenuMgr = function(){
39545    var menus, active, groups = {}, attached = false, lastShow = new Date();
39546
39547    // private - called when first menu is created
39548    function init(){
39549        menus = {};
39550        active = new Roo.util.MixedCollection();
39551        Roo.get(document).addKeyListener(27, function(){
39552            if(active.length > 0){
39553                hideAll();
39554            }
39555        });
39556    }
39557
39558    // private
39559    function hideAll(){
39560        if(active && active.length > 0){
39561            var c = active.clone();
39562            c.each(function(m){
39563                m.hide();
39564            });
39565        }
39566    }
39567
39568    // private
39569    function onHide(m){
39570        active.remove(m);
39571        if(active.length < 1){
39572            Roo.get(document).un("mousedown", onMouseDown);
39573            attached = false;
39574        }
39575    }
39576
39577    // private
39578    function onShow(m){
39579        var last = active.last();
39580        lastShow = new Date();
39581        active.add(m);
39582        if(!attached){
39583            Roo.get(document).on("mousedown", onMouseDown);
39584            attached = true;
39585        }
39586        if(m.parentMenu){
39587           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39588           m.parentMenu.activeChild = m;
39589        }else if(last && last.isVisible()){
39590           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39591        }
39592    }
39593
39594    // private
39595    function onBeforeHide(m){
39596        if(m.activeChild){
39597            m.activeChild.hide();
39598        }
39599        if(m.autoHideTimer){
39600            clearTimeout(m.autoHideTimer);
39601            delete m.autoHideTimer;
39602        }
39603    }
39604
39605    // private
39606    function onBeforeShow(m){
39607        var pm = m.parentMenu;
39608        if(!pm && !m.allowOtherMenus){
39609            hideAll();
39610        }else if(pm && pm.activeChild && active != m){
39611            pm.activeChild.hide();
39612        }
39613    }
39614
39615    // private
39616    function onMouseDown(e){
39617        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39618            hideAll();
39619        }
39620    }
39621
39622    // private
39623    function onBeforeCheck(mi, state){
39624        if(state){
39625            var g = groups[mi.group];
39626            for(var i = 0, l = g.length; i < l; i++){
39627                if(g[i] != mi){
39628                    g[i].setChecked(false);
39629                }
39630            }
39631        }
39632    }
39633
39634    return {
39635
39636        /**
39637         * Hides all menus that are currently visible
39638         */
39639        hideAll : function(){
39640             hideAll();  
39641        },
39642
39643        // private
39644        register : function(menu){
39645            if(!menus){
39646                init();
39647            }
39648            menus[menu.id] = menu;
39649            menu.on("beforehide", onBeforeHide);
39650            menu.on("hide", onHide);
39651            menu.on("beforeshow", onBeforeShow);
39652            menu.on("show", onShow);
39653            var g = menu.group;
39654            if(g && menu.events["checkchange"]){
39655                if(!groups[g]){
39656                    groups[g] = [];
39657                }
39658                groups[g].push(menu);
39659                menu.on("checkchange", onCheck);
39660            }
39661        },
39662
39663         /**
39664          * Returns a {@link Roo.menu.Menu} object
39665          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39666          * be used to generate and return a new Menu instance.
39667          */
39668        get : function(menu){
39669            if(typeof menu == "string"){ // menu id
39670                return menus[menu];
39671            }else if(menu.events){  // menu instance
39672                return menu;
39673            }else if(typeof menu.length == 'number'){ // array of menu items?
39674                return new Roo.menu.Menu({items:menu});
39675            }else{ // otherwise, must be a config
39676                return new Roo.menu.Menu(menu);
39677            }
39678        },
39679
39680        // private
39681        unregister : function(menu){
39682            delete menus[menu.id];
39683            menu.un("beforehide", onBeforeHide);
39684            menu.un("hide", onHide);
39685            menu.un("beforeshow", onBeforeShow);
39686            menu.un("show", onShow);
39687            var g = menu.group;
39688            if(g && menu.events["checkchange"]){
39689                groups[g].remove(menu);
39690                menu.un("checkchange", onCheck);
39691            }
39692        },
39693
39694        // private
39695        registerCheckable : function(menuItem){
39696            var g = menuItem.group;
39697            if(g){
39698                if(!groups[g]){
39699                    groups[g] = [];
39700                }
39701                groups[g].push(menuItem);
39702                menuItem.on("beforecheckchange", onBeforeCheck);
39703            }
39704        },
39705
39706        // private
39707        unregisterCheckable : function(menuItem){
39708            var g = menuItem.group;
39709            if(g){
39710                groups[g].remove(menuItem);
39711                menuItem.un("beforecheckchange", onBeforeCheck);
39712            }
39713        }
39714    };
39715 }();/*
39716  * Based on:
39717  * Ext JS Library 1.1.1
39718  * Copyright(c) 2006-2007, Ext JS, LLC.
39719  *
39720  * Originally Released Under LGPL - original licence link has changed is not relivant.
39721  *
39722  * Fork - LGPL
39723  * <script type="text/javascript">
39724  */
39725  
39726
39727 /**
39728  * @class Roo.menu.BaseItem
39729  * @extends Roo.Component
39730  * @abstract
39731  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39732  * management and base configuration options shared by all menu components.
39733  * @constructor
39734  * Creates a new BaseItem
39735  * @param {Object} config Configuration options
39736  */
39737 Roo.menu.BaseItem = function(config){
39738     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39739
39740     this.addEvents({
39741         /**
39742          * @event click
39743          * Fires when this item is clicked
39744          * @param {Roo.menu.BaseItem} this
39745          * @param {Roo.EventObject} e
39746          */
39747         click: true,
39748         /**
39749          * @event activate
39750          * Fires when this item is activated
39751          * @param {Roo.menu.BaseItem} this
39752          */
39753         activate : true,
39754         /**
39755          * @event deactivate
39756          * Fires when this item is deactivated
39757          * @param {Roo.menu.BaseItem} this
39758          */
39759         deactivate : true
39760     });
39761
39762     if(this.handler){
39763         this.on("click", this.handler, this.scope, true);
39764     }
39765 };
39766
39767 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39768     /**
39769      * @cfg {Function} handler
39770      * A function that will handle the click event of this menu item (defaults to undefined)
39771      */
39772     /**
39773      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39774      */
39775     canActivate : false,
39776     
39777      /**
39778      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39779      */
39780     hidden: false,
39781     
39782     /**
39783      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39784      */
39785     activeClass : "x-menu-item-active",
39786     /**
39787      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39788      */
39789     hideOnClick : true,
39790     /**
39791      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39792      */
39793     hideDelay : 100,
39794
39795     // private
39796     ctype: "Roo.menu.BaseItem",
39797
39798     // private
39799     actionMode : "container",
39800
39801     // private
39802     render : function(container, parentMenu){
39803         this.parentMenu = parentMenu;
39804         Roo.menu.BaseItem.superclass.render.call(this, container);
39805         this.container.menuItemId = this.id;
39806     },
39807
39808     // private
39809     onRender : function(container, position){
39810         this.el = Roo.get(this.el);
39811         container.dom.appendChild(this.el.dom);
39812     },
39813
39814     // private
39815     onClick : function(e){
39816         if(!this.disabled && this.fireEvent("click", this, e) !== false
39817                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39818             this.handleClick(e);
39819         }else{
39820             e.stopEvent();
39821         }
39822     },
39823
39824     // private
39825     activate : function(){
39826         if(this.disabled){
39827             return false;
39828         }
39829         var li = this.container;
39830         li.addClass(this.activeClass);
39831         this.region = li.getRegion().adjust(2, 2, -2, -2);
39832         this.fireEvent("activate", this);
39833         return true;
39834     },
39835
39836     // private
39837     deactivate : function(){
39838         this.container.removeClass(this.activeClass);
39839         this.fireEvent("deactivate", this);
39840     },
39841
39842     // private
39843     shouldDeactivate : function(e){
39844         return !this.region || !this.region.contains(e.getPoint());
39845     },
39846
39847     // private
39848     handleClick : function(e){
39849         if(this.hideOnClick){
39850             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39851         }
39852     },
39853
39854     // private
39855     expandMenu : function(autoActivate){
39856         // do nothing
39857     },
39858
39859     // private
39860     hideMenu : function(){
39861         // do nothing
39862     }
39863 });/*
39864  * Based on:
39865  * Ext JS Library 1.1.1
39866  * Copyright(c) 2006-2007, Ext JS, LLC.
39867  *
39868  * Originally Released Under LGPL - original licence link has changed is not relivant.
39869  *
39870  * Fork - LGPL
39871  * <script type="text/javascript">
39872  */
39873  
39874 /**
39875  * @class Roo.menu.Adapter
39876  * @extends Roo.menu.BaseItem
39877  * @abstract
39878  * 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.
39879  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39880  * @constructor
39881  * Creates a new Adapter
39882  * @param {Object} config Configuration options
39883  */
39884 Roo.menu.Adapter = function(component, config){
39885     Roo.menu.Adapter.superclass.constructor.call(this, config);
39886     this.component = component;
39887 };
39888 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39889     // private
39890     canActivate : true,
39891
39892     // private
39893     onRender : function(container, position){
39894         this.component.render(container);
39895         this.el = this.component.getEl();
39896     },
39897
39898     // private
39899     activate : function(){
39900         if(this.disabled){
39901             return false;
39902         }
39903         this.component.focus();
39904         this.fireEvent("activate", this);
39905         return true;
39906     },
39907
39908     // private
39909     deactivate : function(){
39910         this.fireEvent("deactivate", this);
39911     },
39912
39913     // private
39914     disable : function(){
39915         this.component.disable();
39916         Roo.menu.Adapter.superclass.disable.call(this);
39917     },
39918
39919     // private
39920     enable : function(){
39921         this.component.enable();
39922         Roo.menu.Adapter.superclass.enable.call(this);
39923     }
39924 });/*
39925  * Based on:
39926  * Ext JS Library 1.1.1
39927  * Copyright(c) 2006-2007, Ext JS, LLC.
39928  *
39929  * Originally Released Under LGPL - original licence link has changed is not relivant.
39930  *
39931  * Fork - LGPL
39932  * <script type="text/javascript">
39933  */
39934
39935 /**
39936  * @class Roo.menu.TextItem
39937  * @extends Roo.menu.BaseItem
39938  * Adds a static text string to a menu, usually used as either a heading or group separator.
39939  * Note: old style constructor with text is still supported.
39940  * 
39941  * @constructor
39942  * Creates a new TextItem
39943  * @param {Object} cfg Configuration
39944  */
39945 Roo.menu.TextItem = function(cfg){
39946     if (typeof(cfg) == 'string') {
39947         this.text = cfg;
39948     } else {
39949         Roo.apply(this,cfg);
39950     }
39951     
39952     Roo.menu.TextItem.superclass.constructor.call(this);
39953 };
39954
39955 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39956     /**
39957      * @cfg {String} text Text to show on item.
39958      */
39959     text : '',
39960     
39961     /**
39962      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39963      */
39964     hideOnClick : false,
39965     /**
39966      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39967      */
39968     itemCls : "x-menu-text",
39969
39970     // private
39971     onRender : function(){
39972         var s = document.createElement("span");
39973         s.className = this.itemCls;
39974         s.innerHTML = this.text;
39975         this.el = s;
39976         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39977     }
39978 });/*
39979  * Based on:
39980  * Ext JS Library 1.1.1
39981  * Copyright(c) 2006-2007, Ext JS, LLC.
39982  *
39983  * Originally Released Under LGPL - original licence link has changed is not relivant.
39984  *
39985  * Fork - LGPL
39986  * <script type="text/javascript">
39987  */
39988
39989 /**
39990  * @class Roo.menu.Separator
39991  * @extends Roo.menu.BaseItem
39992  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39993  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39994  * @constructor
39995  * @param {Object} config Configuration options
39996  */
39997 Roo.menu.Separator = function(config){
39998     Roo.menu.Separator.superclass.constructor.call(this, config);
39999 };
40000
40001 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
40002     /**
40003      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
40004      */
40005     itemCls : "x-menu-sep",
40006     /**
40007      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
40008      */
40009     hideOnClick : false,
40010
40011     // private
40012     onRender : function(li){
40013         var s = document.createElement("span");
40014         s.className = this.itemCls;
40015         s.innerHTML = "&#160;";
40016         this.el = s;
40017         li.addClass("x-menu-sep-li");
40018         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
40019     }
40020 });/*
40021  * Based on:
40022  * Ext JS Library 1.1.1
40023  * Copyright(c) 2006-2007, Ext JS, LLC.
40024  *
40025  * Originally Released Under LGPL - original licence link has changed is not relivant.
40026  *
40027  * Fork - LGPL
40028  * <script type="text/javascript">
40029  */
40030 /**
40031  * @class Roo.menu.Item
40032  * @extends Roo.menu.BaseItem
40033  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
40034  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
40035  * activation and click handling.
40036  * @constructor
40037  * Creates a new Item
40038  * @param {Object} config Configuration options
40039  */
40040 Roo.menu.Item = function(config){
40041     Roo.menu.Item.superclass.constructor.call(this, config);
40042     if(this.menu){
40043         this.menu = Roo.menu.MenuMgr.get(this.menu);
40044     }
40045 };
40046 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
40047     /**
40048      * @cfg {Roo.menu.Menu} menu
40049      * A Sub menu
40050      */
40051     /**
40052      * @cfg {String} text
40053      * The text to show on the menu item.
40054      */
40055     text: '',
40056      /**
40057      * @cfg {String} html to render in menu
40058      * The text to show on the menu item (HTML version).
40059      */
40060     html: '',
40061     /**
40062      * @cfg {String} icon
40063      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
40064      */
40065     icon: undefined,
40066     /**
40067      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
40068      */
40069     itemCls : "x-menu-item",
40070     /**
40071      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
40072      */
40073     canActivate : true,
40074     /**
40075      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
40076      */
40077     showDelay: 200,
40078     // doc'd in BaseItem
40079     hideDelay: 200,
40080
40081     // private
40082     ctype: "Roo.menu.Item",
40083     
40084     // private
40085     onRender : function(container, position){
40086         var el = document.createElement("a");
40087         el.hideFocus = true;
40088         el.unselectable = "on";
40089         el.href = this.href || "#";
40090         if(this.hrefTarget){
40091             el.target = this.hrefTarget;
40092         }
40093         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
40094         
40095         var html = this.html.length ? this.html  : String.format('{0}',this.text);
40096         
40097         el.innerHTML = String.format(
40098                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
40099                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
40100         this.el = el;
40101         Roo.menu.Item.superclass.onRender.call(this, container, position);
40102     },
40103
40104     /**
40105      * Sets the text to display in this menu item
40106      * @param {String} text The text to display
40107      * @param {Boolean} isHTML true to indicate text is pure html.
40108      */
40109     setText : function(text, isHTML){
40110         if (isHTML) {
40111             this.html = text;
40112         } else {
40113             this.text = text;
40114             this.html = '';
40115         }
40116         if(this.rendered){
40117             var html = this.html.length ? this.html  : String.format('{0}',this.text);
40118      
40119             this.el.update(String.format(
40120                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
40121                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
40122             this.parentMenu.autoWidth();
40123         }
40124     },
40125
40126     // private
40127     handleClick : function(e){
40128         if(!this.href){ // if no link defined, stop the event automatically
40129             e.stopEvent();
40130         }
40131         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
40132     },
40133
40134     // private
40135     activate : function(autoExpand){
40136         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
40137             this.focus();
40138             if(autoExpand){
40139                 this.expandMenu();
40140             }
40141         }
40142         return true;
40143     },
40144
40145     // private
40146     shouldDeactivate : function(e){
40147         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
40148             if(this.menu && this.menu.isVisible()){
40149                 return !this.menu.getEl().getRegion().contains(e.getPoint());
40150             }
40151             return true;
40152         }
40153         return false;
40154     },
40155
40156     // private
40157     deactivate : function(){
40158         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
40159         this.hideMenu();
40160     },
40161
40162     // private
40163     expandMenu : function(autoActivate){
40164         if(!this.disabled && this.menu){
40165             clearTimeout(this.hideTimer);
40166             delete this.hideTimer;
40167             if(!this.menu.isVisible() && !this.showTimer){
40168                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
40169             }else if (this.menu.isVisible() && autoActivate){
40170                 this.menu.tryActivate(0, 1);
40171             }
40172         }
40173     },
40174
40175     // private
40176     deferExpand : function(autoActivate){
40177         delete this.showTimer;
40178         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
40179         if(autoActivate){
40180             this.menu.tryActivate(0, 1);
40181         }
40182     },
40183
40184     // private
40185     hideMenu : function(){
40186         clearTimeout(this.showTimer);
40187         delete this.showTimer;
40188         if(!this.hideTimer && this.menu && this.menu.isVisible()){
40189             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
40190         }
40191     },
40192
40193     // private
40194     deferHide : function(){
40195         delete this.hideTimer;
40196         this.menu.hide();
40197     }
40198 });/*
40199  * Based on:
40200  * Ext JS Library 1.1.1
40201  * Copyright(c) 2006-2007, Ext JS, LLC.
40202  *
40203  * Originally Released Under LGPL - original licence link has changed is not relivant.
40204  *
40205  * Fork - LGPL
40206  * <script type="text/javascript">
40207  */
40208  
40209 /**
40210  * @class Roo.menu.CheckItem
40211  * @extends Roo.menu.Item
40212  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
40213  * @constructor
40214  * Creates a new CheckItem
40215  * @param {Object} config Configuration options
40216  */
40217 Roo.menu.CheckItem = function(config){
40218     Roo.menu.CheckItem.superclass.constructor.call(this, config);
40219     this.addEvents({
40220         /**
40221          * @event beforecheckchange
40222          * Fires before the checked value is set, providing an opportunity to cancel if needed
40223          * @param {Roo.menu.CheckItem} this
40224          * @param {Boolean} checked The new checked value that will be set
40225          */
40226         "beforecheckchange" : true,
40227         /**
40228          * @event checkchange
40229          * Fires after the checked value has been set
40230          * @param {Roo.menu.CheckItem} this
40231          * @param {Boolean} checked The checked value that was set
40232          */
40233         "checkchange" : true
40234     });
40235     if(this.checkHandler){
40236         this.on('checkchange', this.checkHandler, this.scope);
40237     }
40238 };
40239 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
40240     /**
40241      * @cfg {String} group
40242      * All check items with the same group name will automatically be grouped into a single-select
40243      * radio button group (defaults to '')
40244      */
40245     /**
40246      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40247      */
40248     itemCls : "x-menu-item x-menu-check-item",
40249     /**
40250      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40251      */
40252     groupClass : "x-menu-group-item",
40253
40254     /**
40255      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
40256      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40257      * initialized with checked = true will be rendered as checked.
40258      */
40259     checked: false,
40260
40261     // private
40262     ctype: "Roo.menu.CheckItem",
40263
40264     // private
40265     onRender : function(c){
40266         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40267         if(this.group){
40268             this.el.addClass(this.groupClass);
40269         }
40270         Roo.menu.MenuMgr.registerCheckable(this);
40271         if(this.checked){
40272             this.checked = false;
40273             this.setChecked(true, true);
40274         }
40275     },
40276
40277     // private
40278     destroy : function(){
40279         if(this.rendered){
40280             Roo.menu.MenuMgr.unregisterCheckable(this);
40281         }
40282         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40283     },
40284
40285     /**
40286      * Set the checked state of this item
40287      * @param {Boolean} checked The new checked value
40288      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40289      */
40290     setChecked : function(state, suppressEvent){
40291         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40292             if(this.container){
40293                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40294             }
40295             this.checked = state;
40296             if(suppressEvent !== true){
40297                 this.fireEvent("checkchange", this, state);
40298             }
40299         }
40300     },
40301
40302     // private
40303     handleClick : function(e){
40304        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40305            this.setChecked(!this.checked);
40306        }
40307        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40308     }
40309 });/*
40310  * Based on:
40311  * Ext JS Library 1.1.1
40312  * Copyright(c) 2006-2007, Ext JS, LLC.
40313  *
40314  * Originally Released Under LGPL - original licence link has changed is not relivant.
40315  *
40316  * Fork - LGPL
40317  * <script type="text/javascript">
40318  */
40319  
40320 /**
40321  * @class Roo.menu.DateItem
40322  * @extends Roo.menu.Adapter
40323  * A menu item that wraps the {@link Roo.DatPicker} component.
40324  * @constructor
40325  * Creates a new DateItem
40326  * @param {Object} config Configuration options
40327  */
40328 Roo.menu.DateItem = function(config){
40329     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40330     /** The Roo.DatePicker object @type Roo.DatePicker */
40331     this.picker = this.component;
40332     this.addEvents({select: true});
40333     
40334     this.picker.on("render", function(picker){
40335         picker.getEl().swallowEvent("click");
40336         picker.container.addClass("x-menu-date-item");
40337     });
40338
40339     this.picker.on("select", this.onSelect, this);
40340 };
40341
40342 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40343     // private
40344     onSelect : function(picker, date){
40345         this.fireEvent("select", this, date, picker);
40346         Roo.menu.DateItem.superclass.handleClick.call(this);
40347     }
40348 });/*
40349  * Based on:
40350  * Ext JS Library 1.1.1
40351  * Copyright(c) 2006-2007, Ext JS, LLC.
40352  *
40353  * Originally Released Under LGPL - original licence link has changed is not relivant.
40354  *
40355  * Fork - LGPL
40356  * <script type="text/javascript">
40357  */
40358  
40359 /**
40360  * @class Roo.menu.ColorItem
40361  * @extends Roo.menu.Adapter
40362  * A menu item that wraps the {@link Roo.ColorPalette} component.
40363  * @constructor
40364  * Creates a new ColorItem
40365  * @param {Object} config Configuration options
40366  */
40367 Roo.menu.ColorItem = function(config){
40368     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40369     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40370     this.palette = this.component;
40371     this.relayEvents(this.palette, ["select"]);
40372     if(this.selectHandler){
40373         this.on('select', this.selectHandler, this.scope);
40374     }
40375 };
40376 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40377  * Based on:
40378  * Ext JS Library 1.1.1
40379  * Copyright(c) 2006-2007, Ext JS, LLC.
40380  *
40381  * Originally Released Under LGPL - original licence link has changed is not relivant.
40382  *
40383  * Fork - LGPL
40384  * <script type="text/javascript">
40385  */
40386  
40387
40388 /**
40389  * @class Roo.menu.DateMenu
40390  * @extends Roo.menu.Menu
40391  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40392  * @constructor
40393  * Creates a new DateMenu
40394  * @param {Object} config Configuration options
40395  */
40396 Roo.menu.DateMenu = function(config){
40397     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40398     this.plain = true;
40399     var di = new Roo.menu.DateItem(config);
40400     this.add(di);
40401     /**
40402      * The {@link Roo.DatePicker} instance for this DateMenu
40403      * @type DatePicker
40404      */
40405     this.picker = di.picker;
40406     /**
40407      * @event select
40408      * @param {DatePicker} picker
40409      * @param {Date} date
40410      */
40411     this.relayEvents(di, ["select"]);
40412     this.on('beforeshow', function(){
40413         if(this.picker){
40414             this.picker.hideMonthPicker(false);
40415         }
40416     }, this);
40417 };
40418 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40419     cls:'x-date-menu'
40420 });/*
40421  * Based on:
40422  * Ext JS Library 1.1.1
40423  * Copyright(c) 2006-2007, Ext JS, LLC.
40424  *
40425  * Originally Released Under LGPL - original licence link has changed is not relivant.
40426  *
40427  * Fork - LGPL
40428  * <script type="text/javascript">
40429  */
40430  
40431
40432 /**
40433  * @class Roo.menu.ColorMenu
40434  * @extends Roo.menu.Menu
40435  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40436  * @constructor
40437  * Creates a new ColorMenu
40438  * @param {Object} config Configuration options
40439  */
40440 Roo.menu.ColorMenu = function(config){
40441     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40442     this.plain = true;
40443     var ci = new Roo.menu.ColorItem(config);
40444     this.add(ci);
40445     /**
40446      * The {@link Roo.ColorPalette} instance for this ColorMenu
40447      * @type ColorPalette
40448      */
40449     this.palette = ci.palette;
40450     /**
40451      * @event select
40452      * @param {ColorPalette} palette
40453      * @param {String} color
40454      */
40455     this.relayEvents(ci, ["select"]);
40456 };
40457 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40458  * Based on:
40459  * Ext JS Library 1.1.1
40460  * Copyright(c) 2006-2007, Ext JS, LLC.
40461  *
40462  * Originally Released Under LGPL - original licence link has changed is not relivant.
40463  *
40464  * Fork - LGPL
40465  * <script type="text/javascript">
40466  */
40467  
40468 /**
40469  * @class Roo.form.TextItem
40470  * @extends Roo.BoxComponent
40471  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40472  * @constructor
40473  * Creates a new TextItem
40474  * @param {Object} config Configuration options
40475  */
40476 Roo.form.TextItem = function(config){
40477     Roo.form.TextItem.superclass.constructor.call(this, config);
40478 };
40479
40480 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40481     
40482     /**
40483      * @cfg {String} tag the tag for this item (default div)
40484      */
40485     tag : 'div',
40486     /**
40487      * @cfg {String} html the content for this item
40488      */
40489     html : '',
40490     
40491     getAutoCreate : function()
40492     {
40493         var cfg = {
40494             id: this.id,
40495             tag: this.tag,
40496             html: this.html,
40497             cls: 'x-form-item'
40498         };
40499         
40500         return cfg;
40501         
40502     },
40503     
40504     onRender : function(ct, position)
40505     {
40506         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40507         
40508         if(!this.el){
40509             var cfg = this.getAutoCreate();
40510             if(!cfg.name){
40511                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40512             }
40513             if (!cfg.name.length) {
40514                 delete cfg.name;
40515             }
40516             this.el = ct.createChild(cfg, position);
40517         }
40518     },
40519     /*
40520      * setHTML
40521      * @param {String} html update the Contents of the element.
40522      */
40523     setHTML : function(html)
40524     {
40525         this.fieldEl.dom.innerHTML = html;
40526     }
40527     
40528 });/*
40529  * Based on:
40530  * Ext JS Library 1.1.1
40531  * Copyright(c) 2006-2007, Ext JS, LLC.
40532  *
40533  * Originally Released Under LGPL - original licence link has changed is not relivant.
40534  *
40535  * Fork - LGPL
40536  * <script type="text/javascript">
40537  */
40538  
40539 /**
40540  * @class Roo.form.Field
40541  * @extends Roo.BoxComponent
40542  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40543  * @constructor
40544  * Creates a new Field
40545  * @param {Object} config Configuration options
40546  */
40547 Roo.form.Field = function(config){
40548     Roo.form.Field.superclass.constructor.call(this, config);
40549 };
40550
40551 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40552     /**
40553      * @cfg {String} fieldLabel Label to use when rendering a form.
40554      */
40555        /**
40556      * @cfg {String} qtip Mouse over tip
40557      */
40558      
40559     /**
40560      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40561      */
40562     invalidClass : "x-form-invalid",
40563     /**
40564      * @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")
40565      */
40566     invalidText : "The value in this field is invalid",
40567     /**
40568      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40569      */
40570     focusClass : "x-form-focus",
40571     /**
40572      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40573       automatic validation (defaults to "keyup").
40574      */
40575     validationEvent : "keyup",
40576     /**
40577      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40578      */
40579     validateOnBlur : true,
40580     /**
40581      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40582      */
40583     validationDelay : 250,
40584     /**
40585      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40586      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40587      */
40588     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40589     /**
40590      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40591      */
40592     fieldClass : "x-form-field",
40593     /**
40594      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40595      *<pre>
40596 Value         Description
40597 -----------   ----------------------------------------------------------------------
40598 qtip          Display a quick tip when the user hovers over the field
40599 title         Display a default browser title attribute popup
40600 under         Add a block div beneath the field containing the error text
40601 side          Add an error icon to the right of the field with a popup on hover
40602 [element id]  Add the error text directly to the innerHTML of the specified element
40603 </pre>
40604      */
40605     msgTarget : 'qtip',
40606     /**
40607      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40608      */
40609     msgFx : 'normal',
40610
40611     /**
40612      * @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.
40613      */
40614     readOnly : false,
40615
40616     /**
40617      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40618      */
40619     disabled : false,
40620
40621     /**
40622      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40623      */
40624     inputType : undefined,
40625     
40626     /**
40627      * @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).
40628          */
40629         tabIndex : undefined,
40630         
40631     // private
40632     isFormField : true,
40633
40634     // private
40635     hasFocus : false,
40636     /**
40637      * @property {Roo.Element} fieldEl
40638      * Element Containing the rendered Field (with label etc.)
40639      */
40640     /**
40641      * @cfg {Mixed} value A value to initialize this field with.
40642      */
40643     value : undefined,
40644
40645     /**
40646      * @cfg {String} name The field's HTML name attribute.
40647      */
40648     /**
40649      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40650      */
40651     // private
40652     loadedValue : false,
40653      
40654      
40655         // private ??
40656         initComponent : function(){
40657         Roo.form.Field.superclass.initComponent.call(this);
40658         this.addEvents({
40659             /**
40660              * @event focus
40661              * Fires when this field receives input focus.
40662              * @param {Roo.form.Field} this
40663              */
40664             focus : true,
40665             /**
40666              * @event blur
40667              * Fires when this field loses input focus.
40668              * @param {Roo.form.Field} this
40669              */
40670             blur : true,
40671             /**
40672              * @event specialkey
40673              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40674              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40675              * @param {Roo.form.Field} this
40676              * @param {Roo.EventObject} e The event object
40677              */
40678             specialkey : true,
40679             /**
40680              * @event change
40681              * Fires just before the field blurs if the field value has changed.
40682              * @param {Roo.form.Field} this
40683              * @param {Mixed} newValue The new value
40684              * @param {Mixed} oldValue The original value
40685              */
40686             change : true,
40687             /**
40688              * @event invalid
40689              * Fires after the field has been marked as invalid.
40690              * @param {Roo.form.Field} this
40691              * @param {String} msg The validation message
40692              */
40693             invalid : true,
40694             /**
40695              * @event valid
40696              * Fires after the field has been validated with no errors.
40697              * @param {Roo.form.Field} this
40698              */
40699             valid : true,
40700              /**
40701              * @event keyup
40702              * Fires after the key up
40703              * @param {Roo.form.Field} this
40704              * @param {Roo.EventObject}  e The event Object
40705              */
40706             keyup : true
40707         });
40708     },
40709
40710     /**
40711      * Returns the name attribute of the field if available
40712      * @return {String} name The field name
40713      */
40714     getName: function(){
40715          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40716     },
40717
40718     // private
40719     onRender : function(ct, position){
40720         Roo.form.Field.superclass.onRender.call(this, ct, position);
40721         if(!this.el){
40722             var cfg = this.getAutoCreate();
40723             if(!cfg.name){
40724                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40725             }
40726             if (!cfg.name.length) {
40727                 delete cfg.name;
40728             }
40729             if(this.inputType){
40730                 cfg.type = this.inputType;
40731             }
40732             this.el = ct.createChild(cfg, position);
40733         }
40734         var type = this.el.dom.type;
40735         if(type){
40736             if(type == 'password'){
40737                 type = 'text';
40738             }
40739             this.el.addClass('x-form-'+type);
40740         }
40741         if(this.readOnly){
40742             this.el.dom.readOnly = true;
40743         }
40744         if(this.tabIndex !== undefined){
40745             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40746         }
40747
40748         this.el.addClass([this.fieldClass, this.cls]);
40749         this.initValue();
40750     },
40751
40752     /**
40753      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40754      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40755      * @return {Roo.form.Field} this
40756      */
40757     applyTo : function(target){
40758         this.allowDomMove = false;
40759         this.el = Roo.get(target);
40760         this.render(this.el.dom.parentNode);
40761         return this;
40762     },
40763
40764     // private
40765     initValue : function(){
40766         if(this.value !== undefined){
40767             this.setValue(this.value);
40768         }else if(this.el.dom.value.length > 0){
40769             this.setValue(this.el.dom.value);
40770         }
40771     },
40772
40773     /**
40774      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40775      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40776      */
40777     isDirty : function() {
40778         if(this.disabled) {
40779             return false;
40780         }
40781         return String(this.getValue()) !== String(this.originalValue);
40782     },
40783
40784     /**
40785      * stores the current value in loadedValue
40786      */
40787     resetHasChanged : function()
40788     {
40789         this.loadedValue = String(this.getValue());
40790     },
40791     /**
40792      * checks the current value against the 'loaded' value.
40793      * Note - will return false if 'resetHasChanged' has not been called first.
40794      */
40795     hasChanged : function()
40796     {
40797         if(this.disabled || this.readOnly) {
40798             return false;
40799         }
40800         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40801     },
40802     
40803     
40804     
40805     // private
40806     afterRender : function(){
40807         Roo.form.Field.superclass.afterRender.call(this);
40808         this.initEvents();
40809     },
40810
40811     // private
40812     fireKey : function(e){
40813         //Roo.log('field ' + e.getKey());
40814         if(e.isNavKeyPress()){
40815             this.fireEvent("specialkey", this, e);
40816         }
40817     },
40818
40819     /**
40820      * Resets the current field value to the originally loaded value and clears any validation messages
40821      */
40822     reset : function(){
40823         this.setValue(this.resetValue);
40824         this.originalValue = this.getValue();
40825         this.clearInvalid();
40826     },
40827
40828     // private
40829     initEvents : function(){
40830         // safari killled keypress - so keydown is now used..
40831         this.el.on("keydown" , this.fireKey,  this);
40832         this.el.on("focus", this.onFocus,  this);
40833         this.el.on("blur", this.onBlur,  this);
40834         this.el.relayEvent('keyup', this);
40835
40836         // reference to original value for reset
40837         this.originalValue = this.getValue();
40838         this.resetValue =  this.getValue();
40839     },
40840
40841     // private
40842     onFocus : function(){
40843         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40844             this.el.addClass(this.focusClass);
40845         }
40846         if(!this.hasFocus){
40847             this.hasFocus = true;
40848             this.startValue = this.getValue();
40849             this.fireEvent("focus", this);
40850         }
40851     },
40852
40853     beforeBlur : Roo.emptyFn,
40854
40855     // private
40856     onBlur : function(){
40857         this.beforeBlur();
40858         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40859             this.el.removeClass(this.focusClass);
40860         }
40861         this.hasFocus = false;
40862         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40863             this.validate();
40864         }
40865         var v = this.getValue();
40866         if(String(v) !== String(this.startValue)){
40867             this.fireEvent('change', this, v, this.startValue);
40868         }
40869         this.fireEvent("blur", this);
40870     },
40871
40872     /**
40873      * Returns whether or not the field value is currently valid
40874      * @param {Boolean} preventMark True to disable marking the field invalid
40875      * @return {Boolean} True if the value is valid, else false
40876      */
40877     isValid : function(preventMark){
40878         if(this.disabled){
40879             return true;
40880         }
40881         var restore = this.preventMark;
40882         this.preventMark = preventMark === true;
40883         var v = this.validateValue(this.processValue(this.getRawValue()));
40884         this.preventMark = restore;
40885         return v;
40886     },
40887
40888     /**
40889      * Validates the field value
40890      * @return {Boolean} True if the value is valid, else false
40891      */
40892     validate : function(){
40893         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40894             this.clearInvalid();
40895             return true;
40896         }
40897         return false;
40898     },
40899
40900     processValue : function(value){
40901         return value;
40902     },
40903
40904     // private
40905     // Subclasses should provide the validation implementation by overriding this
40906     validateValue : function(value){
40907         return true;
40908     },
40909
40910     /**
40911      * Mark this field as invalid
40912      * @param {String} msg The validation message
40913      */
40914     markInvalid : function(msg){
40915         if(!this.rendered || this.preventMark){ // not rendered
40916             return;
40917         }
40918         
40919         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40920         
40921         obj.el.addClass(this.invalidClass);
40922         msg = msg || this.invalidText;
40923         switch(this.msgTarget){
40924             case 'qtip':
40925                 obj.el.dom.qtip = msg;
40926                 obj.el.dom.qclass = 'x-form-invalid-tip';
40927                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40928                     Roo.QuickTips.enable();
40929                 }
40930                 break;
40931             case 'title':
40932                 this.el.dom.title = msg;
40933                 break;
40934             case 'under':
40935                 if(!this.errorEl){
40936                     var elp = this.el.findParent('.x-form-element', 5, true);
40937                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40938                     this.errorEl.setWidth(elp.getWidth(true)-20);
40939                 }
40940                 this.errorEl.update(msg);
40941                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40942                 break;
40943             case 'side':
40944                 if(!this.errorIcon){
40945                     var elp = this.el.findParent('.x-form-element', 5, true);
40946                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40947                 }
40948                 this.alignErrorIcon();
40949                 this.errorIcon.dom.qtip = msg;
40950                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40951                 this.errorIcon.show();
40952                 this.on('resize', this.alignErrorIcon, this);
40953                 break;
40954             default:
40955                 var t = Roo.getDom(this.msgTarget);
40956                 t.innerHTML = msg;
40957                 t.style.display = this.msgDisplay;
40958                 break;
40959         }
40960         this.fireEvent('invalid', this, msg);
40961     },
40962
40963     // private
40964     alignErrorIcon : function(){
40965         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40966     },
40967
40968     /**
40969      * Clear any invalid styles/messages for this field
40970      */
40971     clearInvalid : function(){
40972         if(!this.rendered || this.preventMark){ // not rendered
40973             return;
40974         }
40975         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40976         
40977         obj.el.removeClass(this.invalidClass);
40978         switch(this.msgTarget){
40979             case 'qtip':
40980                 obj.el.dom.qtip = '';
40981                 break;
40982             case 'title':
40983                 this.el.dom.title = '';
40984                 break;
40985             case 'under':
40986                 if(this.errorEl){
40987                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40988                 }
40989                 break;
40990             case 'side':
40991                 if(this.errorIcon){
40992                     this.errorIcon.dom.qtip = '';
40993                     this.errorIcon.hide();
40994                     this.un('resize', this.alignErrorIcon, this);
40995                 }
40996                 break;
40997             default:
40998                 var t = Roo.getDom(this.msgTarget);
40999                 t.innerHTML = '';
41000                 t.style.display = 'none';
41001                 break;
41002         }
41003         this.fireEvent('valid', this);
41004     },
41005
41006     /**
41007      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41008      * @return {Mixed} value The field value
41009      */
41010     getRawValue : function(){
41011         var v = this.el.getValue();
41012         
41013         return v;
41014     },
41015
41016     /**
41017      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41018      * @return {Mixed} value The field value
41019      */
41020     getValue : function(){
41021         var v = this.el.getValue();
41022          
41023         return v;
41024     },
41025
41026     /**
41027      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
41028      * @param {Mixed} value The value to set
41029      */
41030     setRawValue : function(v){
41031         return this.el.dom.value = (v === null || v === undefined ? '' : v);
41032     },
41033
41034     /**
41035      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41036      * @param {Mixed} value The value to set
41037      */
41038     setValue : function(v){
41039         this.value = v;
41040         if(this.rendered){
41041             this.el.dom.value = (v === null || v === undefined ? '' : v);
41042              this.validate();
41043         }
41044     },
41045
41046     adjustSize : function(w, h){
41047         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
41048         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
41049         return s;
41050     },
41051
41052     adjustWidth : function(tag, w){
41053         tag = tag.toLowerCase();
41054         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
41055             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
41056                 if(tag == 'input'){
41057                     return w + 2;
41058                 }
41059                 if(tag == 'textarea'){
41060                     return w-2;
41061                 }
41062             }else if(Roo.isOpera){
41063                 if(tag == 'input'){
41064                     return w + 2;
41065                 }
41066                 if(tag == 'textarea'){
41067                     return w-2;
41068                 }
41069             }
41070         }
41071         return w;
41072     }
41073 });
41074
41075
41076 // anything other than normal should be considered experimental
41077 Roo.form.Field.msgFx = {
41078     normal : {
41079         show: function(msgEl, f){
41080             msgEl.setDisplayed('block');
41081         },
41082
41083         hide : function(msgEl, f){
41084             msgEl.setDisplayed(false).update('');
41085         }
41086     },
41087
41088     slide : {
41089         show: function(msgEl, f){
41090             msgEl.slideIn('t', {stopFx:true});
41091         },
41092
41093         hide : function(msgEl, f){
41094             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
41095         }
41096     },
41097
41098     slideRight : {
41099         show: function(msgEl, f){
41100             msgEl.fixDisplay();
41101             msgEl.alignTo(f.el, 'tl-tr');
41102             msgEl.slideIn('l', {stopFx:true});
41103         },
41104
41105         hide : function(msgEl, f){
41106             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
41107         }
41108     }
41109 };/*
41110  * Based on:
41111  * Ext JS Library 1.1.1
41112  * Copyright(c) 2006-2007, Ext JS, LLC.
41113  *
41114  * Originally Released Under LGPL - original licence link has changed is not relivant.
41115  *
41116  * Fork - LGPL
41117  * <script type="text/javascript">
41118  */
41119  
41120
41121 /**
41122  * @class Roo.form.TextField
41123  * @extends Roo.form.Field
41124  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
41125  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
41126  * @constructor
41127  * Creates a new TextField
41128  * @param {Object} config Configuration options
41129  */
41130 Roo.form.TextField = function(config){
41131     Roo.form.TextField.superclass.constructor.call(this, config);
41132     this.addEvents({
41133         /**
41134          * @event autosize
41135          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
41136          * according to the default logic, but this event provides a hook for the developer to apply additional
41137          * logic at runtime to resize the field if needed.
41138              * @param {Roo.form.Field} this This text field
41139              * @param {Number} width The new field width
41140              */
41141         autosize : true
41142     });
41143 };
41144
41145 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
41146     /**
41147      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
41148      */
41149     grow : false,
41150     /**
41151      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
41152      */
41153     growMin : 30,
41154     /**
41155      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
41156      */
41157     growMax : 800,
41158     /**
41159      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
41160      */
41161     vtype : null,
41162     /**
41163      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
41164      */
41165     maskRe : null,
41166     /**
41167      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
41168      */
41169     disableKeyFilter : false,
41170     /**
41171      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
41172      */
41173     allowBlank : true,
41174     /**
41175      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
41176      */
41177     minLength : 0,
41178     /**
41179      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
41180      */
41181     maxLength : Number.MAX_VALUE,
41182     /**
41183      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
41184      */
41185     minLengthText : "The minimum length for this field is {0}",
41186     /**
41187      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
41188      */
41189     maxLengthText : "The maximum length for this field is {0}",
41190     /**
41191      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
41192      */
41193     selectOnFocus : false,
41194     /**
41195      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
41196      */    
41197     allowLeadingSpace : false,
41198     /**
41199      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
41200      */
41201     blankText : "This field is required",
41202     /**
41203      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
41204      * If available, this function will be called only after the basic validators all return true, and will be passed the
41205      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
41206      */
41207     validator : null,
41208     /**
41209      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
41210      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
41211      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
41212      */
41213     regex : null,
41214     /**
41215      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
41216      */
41217     regexText : "",
41218     /**
41219      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
41220      */
41221     emptyText : null,
41222    
41223
41224     // private
41225     initEvents : function()
41226     {
41227         if (this.emptyText) {
41228             this.el.attr('placeholder', this.emptyText);
41229         }
41230         
41231         Roo.form.TextField.superclass.initEvents.call(this);
41232         if(this.validationEvent == 'keyup'){
41233             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41234             this.el.on('keyup', this.filterValidation, this);
41235         }
41236         else if(this.validationEvent !== false){
41237             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41238         }
41239         
41240         if(this.selectOnFocus){
41241             this.on("focus", this.preFocus, this);
41242         }
41243         if (!this.allowLeadingSpace) {
41244             this.on('blur', this.cleanLeadingSpace, this);
41245         }
41246         
41247         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41248             this.el.on("keypress", this.filterKeys, this);
41249         }
41250         if(this.grow){
41251             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
41252             this.el.on("click", this.autoSize,  this);
41253         }
41254         if(this.el.is('input[type=password]') && Roo.isSafari){
41255             this.el.on('keydown', this.SafariOnKeyDown, this);
41256         }
41257     },
41258
41259     processValue : function(value){
41260         if(this.stripCharsRe){
41261             var newValue = value.replace(this.stripCharsRe, '');
41262             if(newValue !== value){
41263                 this.setRawValue(newValue);
41264                 return newValue;
41265             }
41266         }
41267         return value;
41268     },
41269
41270     filterValidation : function(e){
41271         if(!e.isNavKeyPress()){
41272             this.validationTask.delay(this.validationDelay);
41273         }
41274     },
41275
41276     // private
41277     onKeyUp : function(e){
41278         if(!e.isNavKeyPress()){
41279             this.autoSize();
41280         }
41281     },
41282     // private - clean the leading white space
41283     cleanLeadingSpace : function(e)
41284     {
41285         if ( this.inputType == 'file') {
41286             return;
41287         }
41288         
41289         this.setValue((this.getValue() + '').replace(/^\s+/,''));
41290     },
41291     /**
41292      * Resets the current field value to the originally-loaded value and clears any validation messages.
41293      *  
41294      */
41295     reset : function(){
41296         Roo.form.TextField.superclass.reset.call(this);
41297        
41298     }, 
41299     // private
41300     preFocus : function(){
41301         
41302         if(this.selectOnFocus){
41303             this.el.dom.select();
41304         }
41305     },
41306
41307     
41308     // private
41309     filterKeys : function(e){
41310         var k = e.getKey();
41311         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41312             return;
41313         }
41314         var c = e.getCharCode(), cc = String.fromCharCode(c);
41315         if(Roo.isIE && (e.isSpecialKey() || !cc)){
41316             return;
41317         }
41318         if(!this.maskRe.test(cc)){
41319             e.stopEvent();
41320         }
41321     },
41322
41323     setValue : function(v){
41324         
41325         Roo.form.TextField.superclass.setValue.apply(this, arguments);
41326         
41327         this.autoSize();
41328     },
41329
41330     /**
41331      * Validates a value according to the field's validation rules and marks the field as invalid
41332      * if the validation fails
41333      * @param {Mixed} value The value to validate
41334      * @return {Boolean} True if the value is valid, else false
41335      */
41336     validateValue : function(value){
41337         if(value.length < 1)  { // if it's blank
41338              if(this.allowBlank){
41339                 this.clearInvalid();
41340                 return true;
41341              }else{
41342                 this.markInvalid(this.blankText);
41343                 return false;
41344              }
41345         }
41346         if(value.length < this.minLength){
41347             this.markInvalid(String.format(this.minLengthText, this.minLength));
41348             return false;
41349         }
41350         if(value.length > this.maxLength){
41351             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41352             return false;
41353         }
41354         if(this.vtype){
41355             var vt = Roo.form.VTypes;
41356             if(!vt[this.vtype](value, this)){
41357                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41358                 return false;
41359             }
41360         }
41361         if(typeof this.validator == "function"){
41362             var msg = this.validator(value);
41363             if(msg !== true){
41364                 this.markInvalid(msg);
41365                 return false;
41366             }
41367         }
41368         if(this.regex && !this.regex.test(value)){
41369             this.markInvalid(this.regexText);
41370             return false;
41371         }
41372         return true;
41373     },
41374
41375     /**
41376      * Selects text in this field
41377      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41378      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41379      */
41380     selectText : function(start, end){
41381         var v = this.getRawValue();
41382         if(v.length > 0){
41383             start = start === undefined ? 0 : start;
41384             end = end === undefined ? v.length : end;
41385             var d = this.el.dom;
41386             if(d.setSelectionRange){
41387                 d.setSelectionRange(start, end);
41388             }else if(d.createTextRange){
41389                 var range = d.createTextRange();
41390                 range.moveStart("character", start);
41391                 range.moveEnd("character", v.length-end);
41392                 range.select();
41393             }
41394         }
41395     },
41396
41397     /**
41398      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41399      * This only takes effect if grow = true, and fires the autosize event.
41400      */
41401     autoSize : function(){
41402         if(!this.grow || !this.rendered){
41403             return;
41404         }
41405         if(!this.metrics){
41406             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41407         }
41408         var el = this.el;
41409         var v = el.dom.value;
41410         var d = document.createElement('div');
41411         d.appendChild(document.createTextNode(v));
41412         v = d.innerHTML;
41413         d = null;
41414         v += "&#160;";
41415         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41416         this.el.setWidth(w);
41417         this.fireEvent("autosize", this, w);
41418     },
41419     
41420     // private
41421     SafariOnKeyDown : function(event)
41422     {
41423         // this is a workaround for a password hang bug on chrome/ webkit.
41424         
41425         var isSelectAll = false;
41426         
41427         if(this.el.dom.selectionEnd > 0){
41428             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41429         }
41430         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41431             event.preventDefault();
41432             this.setValue('');
41433             return;
41434         }
41435         
41436         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41437             
41438             event.preventDefault();
41439             // this is very hacky as keydown always get's upper case.
41440             
41441             var cc = String.fromCharCode(event.getCharCode());
41442             
41443             
41444             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41445             
41446         }
41447         
41448         
41449     }
41450 });/*
41451  * Based on:
41452  * Ext JS Library 1.1.1
41453  * Copyright(c) 2006-2007, Ext JS, LLC.
41454  *
41455  * Originally Released Under LGPL - original licence link has changed is not relivant.
41456  *
41457  * Fork - LGPL
41458  * <script type="text/javascript">
41459  */
41460  
41461 /**
41462  * @class Roo.form.Hidden
41463  * @extends Roo.form.TextField
41464  * Simple Hidden element used on forms 
41465  * 
41466  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41467  * 
41468  * @constructor
41469  * Creates a new Hidden form element.
41470  * @param {Object} config Configuration options
41471  */
41472
41473
41474
41475 // easy hidden field...
41476 Roo.form.Hidden = function(config){
41477     Roo.form.Hidden.superclass.constructor.call(this, config);
41478 };
41479   
41480 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41481     fieldLabel:      '',
41482     inputType:      'hidden',
41483     width:          50,
41484     allowBlank:     true,
41485     labelSeparator: '',
41486     hidden:         true,
41487     itemCls :       'x-form-item-display-none'
41488
41489
41490 });
41491
41492
41493 /*
41494  * Based on:
41495  * Ext JS Library 1.1.1
41496  * Copyright(c) 2006-2007, Ext JS, LLC.
41497  *
41498  * Originally Released Under LGPL - original licence link has changed is not relivant.
41499  *
41500  * Fork - LGPL
41501  * <script type="text/javascript">
41502  */
41503  
41504 /**
41505  * @class Roo.form.TriggerField
41506  * @extends Roo.form.TextField
41507  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41508  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41509  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41510  * for which you can provide a custom implementation.  For example:
41511  * <pre><code>
41512 var trigger = new Roo.form.TriggerField();
41513 trigger.onTriggerClick = myTriggerFn;
41514 trigger.applyTo('my-field');
41515 </code></pre>
41516  *
41517  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41518  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41519  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41520  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41521  * @constructor
41522  * Create a new TriggerField.
41523  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41524  * to the base TextField)
41525  */
41526 Roo.form.TriggerField = function(config){
41527     this.mimicing = false;
41528     Roo.form.TriggerField.superclass.constructor.call(this, config);
41529 };
41530
41531 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41532     /**
41533      * @cfg {String} triggerClass A CSS class to apply to the trigger
41534      */
41535     /**
41536      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41537      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41538      */
41539     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41540     /**
41541      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41542      */
41543     hideTrigger:false,
41544
41545     /** @cfg {Boolean} grow @hide */
41546     /** @cfg {Number} growMin @hide */
41547     /** @cfg {Number} growMax @hide */
41548
41549     /**
41550      * @hide 
41551      * @method
41552      */
41553     autoSize: Roo.emptyFn,
41554     // private
41555     monitorTab : true,
41556     // private
41557     deferHeight : true,
41558
41559     
41560     actionMode : 'wrap',
41561     // private
41562     onResize : function(w, h){
41563         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41564         if(typeof w == 'number'){
41565             var x = w - this.trigger.getWidth();
41566             this.el.setWidth(this.adjustWidth('input', x));
41567             this.trigger.setStyle('left', x+'px');
41568         }
41569     },
41570
41571     // private
41572     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41573
41574     // private
41575     getResizeEl : function(){
41576         return this.wrap;
41577     },
41578
41579     // private
41580     getPositionEl : function(){
41581         return this.wrap;
41582     },
41583
41584     // private
41585     alignErrorIcon : function(){
41586         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41587     },
41588
41589     // private
41590     onRender : function(ct, position){
41591         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41592         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41593         this.trigger = this.wrap.createChild(this.triggerConfig ||
41594                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41595         if(this.hideTrigger){
41596             this.trigger.setDisplayed(false);
41597         }
41598         this.initTrigger();
41599         if(!this.width){
41600             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41601         }
41602     },
41603
41604     // private
41605     initTrigger : function(){
41606         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41607         this.trigger.addClassOnOver('x-form-trigger-over');
41608         this.trigger.addClassOnClick('x-form-trigger-click');
41609     },
41610
41611     // private
41612     onDestroy : function(){
41613         if(this.trigger){
41614             this.trigger.removeAllListeners();
41615             this.trigger.remove();
41616         }
41617         if(this.wrap){
41618             this.wrap.remove();
41619         }
41620         Roo.form.TriggerField.superclass.onDestroy.call(this);
41621     },
41622
41623     // private
41624     onFocus : function(){
41625         Roo.form.TriggerField.superclass.onFocus.call(this);
41626         if(!this.mimicing){
41627             this.wrap.addClass('x-trigger-wrap-focus');
41628             this.mimicing = true;
41629             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41630             if(this.monitorTab){
41631                 this.el.on("keydown", this.checkTab, this);
41632             }
41633         }
41634     },
41635
41636     // private
41637     checkTab : function(e){
41638         if(e.getKey() == e.TAB){
41639             this.triggerBlur();
41640         }
41641     },
41642
41643     // private
41644     onBlur : function(){
41645         // do nothing
41646     },
41647
41648     // private
41649     mimicBlur : function(e, t){
41650         if(!this.wrap.contains(t) && this.validateBlur()){
41651             this.triggerBlur();
41652         }
41653     },
41654
41655     // private
41656     triggerBlur : function(){
41657         this.mimicing = false;
41658         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41659         if(this.monitorTab){
41660             this.el.un("keydown", this.checkTab, this);
41661         }
41662         this.wrap.removeClass('x-trigger-wrap-focus');
41663         Roo.form.TriggerField.superclass.onBlur.call(this);
41664     },
41665
41666     // private
41667     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41668     validateBlur : function(e, t){
41669         return true;
41670     },
41671
41672     // private
41673     onDisable : function(){
41674         Roo.form.TriggerField.superclass.onDisable.call(this);
41675         if(this.wrap){
41676             this.wrap.addClass('x-item-disabled');
41677         }
41678     },
41679
41680     // private
41681     onEnable : function(){
41682         Roo.form.TriggerField.superclass.onEnable.call(this);
41683         if(this.wrap){
41684             this.wrap.removeClass('x-item-disabled');
41685         }
41686     },
41687
41688     // private
41689     onShow : function(){
41690         var ae = this.getActionEl();
41691         
41692         if(ae){
41693             ae.dom.style.display = '';
41694             ae.dom.style.visibility = 'visible';
41695         }
41696     },
41697
41698     // private
41699     
41700     onHide : function(){
41701         var ae = this.getActionEl();
41702         ae.dom.style.display = 'none';
41703     },
41704
41705     /**
41706      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41707      * by an implementing function.
41708      * @method
41709      * @param {EventObject} e
41710      */
41711     onTriggerClick : Roo.emptyFn
41712 });
41713
41714 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41715 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41716 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41717 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41718     initComponent : function(){
41719         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41720
41721         this.triggerConfig = {
41722             tag:'span', cls:'x-form-twin-triggers', cn:[
41723             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41724             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41725         ]};
41726     },
41727
41728     getTrigger : function(index){
41729         return this.triggers[index];
41730     },
41731
41732     initTrigger : function(){
41733         var ts = this.trigger.select('.x-form-trigger', true);
41734         this.wrap.setStyle('overflow', 'hidden');
41735         var triggerField = this;
41736         ts.each(function(t, all, index){
41737             t.hide = function(){
41738                 var w = triggerField.wrap.getWidth();
41739                 this.dom.style.display = 'none';
41740                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41741             };
41742             t.show = function(){
41743                 var w = triggerField.wrap.getWidth();
41744                 this.dom.style.display = '';
41745                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41746             };
41747             var triggerIndex = 'Trigger'+(index+1);
41748
41749             if(this['hide'+triggerIndex]){
41750                 t.dom.style.display = 'none';
41751             }
41752             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41753             t.addClassOnOver('x-form-trigger-over');
41754             t.addClassOnClick('x-form-trigger-click');
41755         }, this);
41756         this.triggers = ts.elements;
41757     },
41758
41759     onTrigger1Click : Roo.emptyFn,
41760     onTrigger2Click : Roo.emptyFn
41761 });/*
41762  * Based on:
41763  * Ext JS Library 1.1.1
41764  * Copyright(c) 2006-2007, Ext JS, LLC.
41765  *
41766  * Originally Released Under LGPL - original licence link has changed is not relivant.
41767  *
41768  * Fork - LGPL
41769  * <script type="text/javascript">
41770  */
41771  
41772 /**
41773  * @class Roo.form.TextArea
41774  * @extends Roo.form.TextField
41775  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41776  * support for auto-sizing.
41777  * @constructor
41778  * Creates a new TextArea
41779  * @param {Object} config Configuration options
41780  */
41781 Roo.form.TextArea = function(config){
41782     Roo.form.TextArea.superclass.constructor.call(this, config);
41783     // these are provided exchanges for backwards compat
41784     // minHeight/maxHeight were replaced by growMin/growMax to be
41785     // compatible with TextField growing config values
41786     if(this.minHeight !== undefined){
41787         this.growMin = this.minHeight;
41788     }
41789     if(this.maxHeight !== undefined){
41790         this.growMax = this.maxHeight;
41791     }
41792 };
41793
41794 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41795     /**
41796      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41797      */
41798     growMin : 60,
41799     /**
41800      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41801      */
41802     growMax: 1000,
41803     /**
41804      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41805      * in the field (equivalent to setting overflow: hidden, defaults to false)
41806      */
41807     preventScrollbars: false,
41808     /**
41809      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41810      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41811      */
41812
41813     // private
41814     onRender : function(ct, position){
41815         if(!this.el){
41816             this.defaultAutoCreate = {
41817                 tag: "textarea",
41818                 style:"width:300px;height:60px;",
41819                 autocomplete: "new-password"
41820             };
41821         }
41822         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41823         if(this.grow){
41824             this.textSizeEl = Roo.DomHelper.append(document.body, {
41825                 tag: "pre", cls: "x-form-grow-sizer"
41826             });
41827             if(this.preventScrollbars){
41828                 this.el.setStyle("overflow", "hidden");
41829             }
41830             this.el.setHeight(this.growMin);
41831         }
41832     },
41833
41834     onDestroy : function(){
41835         if(this.textSizeEl){
41836             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41837         }
41838         Roo.form.TextArea.superclass.onDestroy.call(this);
41839     },
41840
41841     // private
41842     onKeyUp : function(e){
41843         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41844             this.autoSize();
41845         }
41846     },
41847
41848     /**
41849      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41850      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41851      */
41852     autoSize : function(){
41853         if(!this.grow || !this.textSizeEl){
41854             return;
41855         }
41856         var el = this.el;
41857         var v = el.dom.value;
41858         var ts = this.textSizeEl;
41859
41860         ts.innerHTML = '';
41861         ts.appendChild(document.createTextNode(v));
41862         v = ts.innerHTML;
41863
41864         Roo.fly(ts).setWidth(this.el.getWidth());
41865         if(v.length < 1){
41866             v = "&#160;&#160;";
41867         }else{
41868             if(Roo.isIE){
41869                 v = v.replace(/\n/g, '<p>&#160;</p>');
41870             }
41871             v += "&#160;\n&#160;";
41872         }
41873         ts.innerHTML = v;
41874         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41875         if(h != this.lastHeight){
41876             this.lastHeight = h;
41877             this.el.setHeight(h);
41878             this.fireEvent("autosize", this, h);
41879         }
41880     }
41881 });/*
41882  * Based on:
41883  * Ext JS Library 1.1.1
41884  * Copyright(c) 2006-2007, Ext JS, LLC.
41885  *
41886  * Originally Released Under LGPL - original licence link has changed is not relivant.
41887  *
41888  * Fork - LGPL
41889  * <script type="text/javascript">
41890  */
41891  
41892
41893 /**
41894  * @class Roo.form.NumberField
41895  * @extends Roo.form.TextField
41896  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41897  * @constructor
41898  * Creates a new NumberField
41899  * @param {Object} config Configuration options
41900  */
41901 Roo.form.NumberField = function(config){
41902     Roo.form.NumberField.superclass.constructor.call(this, config);
41903 };
41904
41905 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41906     /**
41907      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41908      */
41909     fieldClass: "x-form-field x-form-num-field",
41910     /**
41911      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41912      */
41913     allowDecimals : true,
41914     /**
41915      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41916      */
41917     decimalSeparator : ".",
41918     /**
41919      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41920      */
41921     decimalPrecision : 2,
41922     /**
41923      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41924      */
41925     allowNegative : true,
41926     /**
41927      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41928      */
41929     minValue : Number.NEGATIVE_INFINITY,
41930     /**
41931      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41932      */
41933     maxValue : Number.MAX_VALUE,
41934     /**
41935      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41936      */
41937     minText : "The minimum value for this field is {0}",
41938     /**
41939      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41940      */
41941     maxText : "The maximum value for this field is {0}",
41942     /**
41943      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41944      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41945      */
41946     nanText : "{0} is not a valid number",
41947
41948     // private
41949     initEvents : function(){
41950         Roo.form.NumberField.superclass.initEvents.call(this);
41951         var allowed = "0123456789";
41952         if(this.allowDecimals){
41953             allowed += this.decimalSeparator;
41954         }
41955         if(this.allowNegative){
41956             allowed += "-";
41957         }
41958         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41959         var keyPress = function(e){
41960             var k = e.getKey();
41961             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41962                 return;
41963             }
41964             var c = e.getCharCode();
41965             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41966                 e.stopEvent();
41967             }
41968         };
41969         this.el.on("keypress", keyPress, this);
41970     },
41971
41972     // private
41973     validateValue : function(value){
41974         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41975             return false;
41976         }
41977         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41978              return true;
41979         }
41980         var num = this.parseValue(value);
41981         if(isNaN(num)){
41982             this.markInvalid(String.format(this.nanText, value));
41983             return false;
41984         }
41985         if(num < this.minValue){
41986             this.markInvalid(String.format(this.minText, this.minValue));
41987             return false;
41988         }
41989         if(num > this.maxValue){
41990             this.markInvalid(String.format(this.maxText, this.maxValue));
41991             return false;
41992         }
41993         return true;
41994     },
41995
41996     getValue : function(){
41997         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41998     },
41999
42000     // private
42001     parseValue : function(value){
42002         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
42003         return isNaN(value) ? '' : value;
42004     },
42005
42006     // private
42007     fixPrecision : function(value){
42008         var nan = isNaN(value);
42009         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
42010             return nan ? '' : value;
42011         }
42012         return parseFloat(value).toFixed(this.decimalPrecision);
42013     },
42014
42015     setValue : function(v){
42016         v = this.fixPrecision(v);
42017         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
42018     },
42019
42020     // private
42021     decimalPrecisionFcn : function(v){
42022         return Math.floor(v);
42023     },
42024
42025     beforeBlur : function(){
42026         var v = this.parseValue(this.getRawValue());
42027         if(v){
42028             this.setValue(v);
42029         }
42030     }
42031 });/*
42032  * Based on:
42033  * Ext JS Library 1.1.1
42034  * Copyright(c) 2006-2007, Ext JS, LLC.
42035  *
42036  * Originally Released Under LGPL - original licence link has changed is not relivant.
42037  *
42038  * Fork - LGPL
42039  * <script type="text/javascript">
42040  */
42041  
42042 /**
42043  * @class Roo.form.DateField
42044  * @extends Roo.form.TriggerField
42045  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42046 * @constructor
42047 * Create a new DateField
42048 * @param {Object} config
42049  */
42050 Roo.form.DateField = function(config)
42051 {
42052     Roo.form.DateField.superclass.constructor.call(this, config);
42053     
42054       this.addEvents({
42055          
42056         /**
42057          * @event select
42058          * Fires when a date is selected
42059              * @param {Roo.form.DateField} combo This combo box
42060              * @param {Date} date The date selected
42061              */
42062         'select' : true
42063          
42064     });
42065     
42066     
42067     if(typeof this.minValue == "string") {
42068         this.minValue = this.parseDate(this.minValue);
42069     }
42070     if(typeof this.maxValue == "string") {
42071         this.maxValue = this.parseDate(this.maxValue);
42072     }
42073     this.ddMatch = null;
42074     if(this.disabledDates){
42075         var dd = this.disabledDates;
42076         var re = "(?:";
42077         for(var i = 0; i < dd.length; i++){
42078             re += dd[i];
42079             if(i != dd.length-1) {
42080                 re += "|";
42081             }
42082         }
42083         this.ddMatch = new RegExp(re + ")");
42084     }
42085 };
42086
42087 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
42088     /**
42089      * @cfg {String} format
42090      * The default date format string which can be overriden for localization support.  The format must be
42091      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42092      */
42093     format : "m/d/y",
42094     /**
42095      * @cfg {String} altFormats
42096      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42097      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42098      */
42099     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
42100     /**
42101      * @cfg {Array} disabledDays
42102      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42103      */
42104     disabledDays : null,
42105     /**
42106      * @cfg {String} disabledDaysText
42107      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42108      */
42109     disabledDaysText : "Disabled",
42110     /**
42111      * @cfg {Array} disabledDates
42112      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42113      * expression so they are very powerful. Some examples:
42114      * <ul>
42115      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42116      * <li>["03/08", "09/16"] would disable those days for every year</li>
42117      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42118      * <li>["03/../2006"] would disable every day in March 2006</li>
42119      * <li>["^03"] would disable every day in every March</li>
42120      * </ul>
42121      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42122      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42123      */
42124     disabledDates : null,
42125     /**
42126      * @cfg {String} disabledDatesText
42127      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42128      */
42129     disabledDatesText : "Disabled",
42130         
42131         
42132         /**
42133      * @cfg {Date/String} zeroValue
42134      * if the date is less that this number, then the field is rendered as empty
42135      * default is 1800
42136      */
42137         zeroValue : '1800-01-01',
42138         
42139         
42140     /**
42141      * @cfg {Date/String} minValue
42142      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42143      * valid format (defaults to null).
42144      */
42145     minValue : null,
42146     /**
42147      * @cfg {Date/String} maxValue
42148      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42149      * valid format (defaults to null).
42150      */
42151     maxValue : null,
42152     /**
42153      * @cfg {String} minText
42154      * The error text to display when the date in the cell is before minValue (defaults to
42155      * 'The date in this field must be after {minValue}').
42156      */
42157     minText : "The date in this field must be equal to or after {0}",
42158     /**
42159      * @cfg {String} maxText
42160      * The error text to display when the date in the cell is after maxValue (defaults to
42161      * 'The date in this field must be before {maxValue}').
42162      */
42163     maxText : "The date in this field must be equal to or before {0}",
42164     /**
42165      * @cfg {String} invalidText
42166      * The error text to display when the date in the field is invalid (defaults to
42167      * '{value} is not a valid date - it must be in the format {format}').
42168      */
42169     invalidText : "{0} is not a valid date - it must be in the format {1}",
42170     /**
42171      * @cfg {String} triggerClass
42172      * An additional CSS class used to style the trigger button.  The trigger will always get the
42173      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42174      * which displays a calendar icon).
42175      */
42176     triggerClass : 'x-form-date-trigger',
42177     
42178
42179     /**
42180      * @cfg {Boolean} useIso
42181      * if enabled, then the date field will use a hidden field to store the 
42182      * real value as iso formated date. default (false)
42183      */ 
42184     useIso : false,
42185     /**
42186      * @cfg {String/Object} autoCreate
42187      * A DomHelper element spec, or true for a default element spec (defaults to
42188      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42189      */ 
42190     // private
42191     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
42192     
42193     // private
42194     hiddenField: false,
42195     
42196     onRender : function(ct, position)
42197     {
42198         Roo.form.DateField.superclass.onRender.call(this, ct, position);
42199         if (this.useIso) {
42200             //this.el.dom.removeAttribute('name'); 
42201             Roo.log("Changing name?");
42202             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
42203             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42204                     'before', true);
42205             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42206             // prevent input submission
42207             this.hiddenName = this.name;
42208         }
42209             
42210             
42211     },
42212     
42213     // private
42214     validateValue : function(value)
42215     {
42216         value = this.formatDate(value);
42217         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
42218             Roo.log('super failed');
42219             return false;
42220         }
42221         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42222              return true;
42223         }
42224         var svalue = value;
42225         value = this.parseDate(value);
42226         if(!value){
42227             Roo.log('parse date failed' + svalue);
42228             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42229             return false;
42230         }
42231         var time = value.getTime();
42232         if(this.minValue && time < this.minValue.getTime()){
42233             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42234             return false;
42235         }
42236         if(this.maxValue && time > this.maxValue.getTime()){
42237             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42238             return false;
42239         }
42240         if(this.disabledDays){
42241             var day = value.getDay();
42242             for(var i = 0; i < this.disabledDays.length; i++) {
42243                 if(day === this.disabledDays[i]){
42244                     this.markInvalid(this.disabledDaysText);
42245                     return false;
42246                 }
42247             }
42248         }
42249         var fvalue = this.formatDate(value);
42250         if(this.ddMatch && this.ddMatch.test(fvalue)){
42251             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42252             return false;
42253         }
42254         return true;
42255     },
42256
42257     // private
42258     // Provides logic to override the default TriggerField.validateBlur which just returns true
42259     validateBlur : function(){
42260         return !this.menu || !this.menu.isVisible();
42261     },
42262     
42263     getName: function()
42264     {
42265         // returns hidden if it's set..
42266         if (!this.rendered) {return ''};
42267         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42268         
42269     },
42270
42271     /**
42272      * Returns the current date value of the date field.
42273      * @return {Date} The date value
42274      */
42275     getValue : function(){
42276         
42277         return  this.hiddenField ?
42278                 this.hiddenField.value :
42279                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42280     },
42281
42282     /**
42283      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42284      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42285      * (the default format used is "m/d/y").
42286      * <br />Usage:
42287      * <pre><code>
42288 //All of these calls set the same date value (May 4, 2006)
42289
42290 //Pass a date object:
42291 var dt = new Date('5/4/06');
42292 dateField.setValue(dt);
42293
42294 //Pass a date string (default format):
42295 dateField.setValue('5/4/06');
42296
42297 //Pass a date string (custom format):
42298 dateField.format = 'Y-m-d';
42299 dateField.setValue('2006-5-4');
42300 </code></pre>
42301      * @param {String/Date} date The date or valid date string
42302      */
42303     setValue : function(date){
42304         if (this.hiddenField) {
42305             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42306         }
42307         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42308         // make sure the value field is always stored as a date..
42309         this.value = this.parseDate(date);
42310         
42311         
42312     },
42313
42314     // private
42315     parseDate : function(value){
42316                 
42317                 if (value instanceof Date) {
42318                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42319                                 return  '';
42320                         }
42321                         return value;
42322                 }
42323                 
42324                 
42325         if(!value || value instanceof Date){
42326             return value;
42327         }
42328         var v = Date.parseDate(value, this.format);
42329          if (!v && this.useIso) {
42330             v = Date.parseDate(value, 'Y-m-d');
42331         }
42332         if(!v && this.altFormats){
42333             if(!this.altFormatsArray){
42334                 this.altFormatsArray = this.altFormats.split("|");
42335             }
42336             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42337                 v = Date.parseDate(value, this.altFormatsArray[i]);
42338             }
42339         }
42340                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42341                         v = '';
42342                 }
42343         return v;
42344     },
42345
42346     // private
42347     formatDate : function(date, fmt){
42348         return (!date || !(date instanceof Date)) ?
42349                date : date.dateFormat(fmt || this.format);
42350     },
42351
42352     // private
42353     menuListeners : {
42354         select: function(m, d){
42355             
42356             this.setValue(d);
42357             this.fireEvent('select', this, d);
42358         },
42359         show : function(){ // retain focus styling
42360             this.onFocus();
42361         },
42362         hide : function(){
42363             this.focus.defer(10, this);
42364             var ml = this.menuListeners;
42365             this.menu.un("select", ml.select,  this);
42366             this.menu.un("show", ml.show,  this);
42367             this.menu.un("hide", ml.hide,  this);
42368         }
42369     },
42370
42371     // private
42372     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42373     onTriggerClick : function(){
42374         if(this.disabled){
42375             return;
42376         }
42377         if(this.menu == null){
42378             this.menu = new Roo.menu.DateMenu();
42379         }
42380         Roo.apply(this.menu.picker,  {
42381             showClear: this.allowBlank,
42382             minDate : this.minValue,
42383             maxDate : this.maxValue,
42384             disabledDatesRE : this.ddMatch,
42385             disabledDatesText : this.disabledDatesText,
42386             disabledDays : this.disabledDays,
42387             disabledDaysText : this.disabledDaysText,
42388             format : this.useIso ? 'Y-m-d' : this.format,
42389             minText : String.format(this.minText, this.formatDate(this.minValue)),
42390             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42391         });
42392         this.menu.on(Roo.apply({}, this.menuListeners, {
42393             scope:this
42394         }));
42395         this.menu.picker.setValue(this.getValue() || new Date());
42396         this.menu.show(this.el, "tl-bl?");
42397     },
42398
42399     beforeBlur : function(){
42400         var v = this.parseDate(this.getRawValue());
42401         if(v){
42402             this.setValue(v);
42403         }
42404     },
42405
42406     /*@
42407      * overide
42408      * 
42409      */
42410     isDirty : function() {
42411         if(this.disabled) {
42412             return false;
42413         }
42414         
42415         if(typeof(this.startValue) === 'undefined'){
42416             return false;
42417         }
42418         
42419         return String(this.getValue()) !== String(this.startValue);
42420         
42421     },
42422     // @overide
42423     cleanLeadingSpace : function(e)
42424     {
42425        return;
42426     }
42427     
42428 });/*
42429  * Based on:
42430  * Ext JS Library 1.1.1
42431  * Copyright(c) 2006-2007, Ext JS, LLC.
42432  *
42433  * Originally Released Under LGPL - original licence link has changed is not relivant.
42434  *
42435  * Fork - LGPL
42436  * <script type="text/javascript">
42437  */
42438  
42439 /**
42440  * @class Roo.form.MonthField
42441  * @extends Roo.form.TriggerField
42442  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42443 * @constructor
42444 * Create a new MonthField
42445 * @param {Object} config
42446  */
42447 Roo.form.MonthField = function(config){
42448     
42449     Roo.form.MonthField.superclass.constructor.call(this, config);
42450     
42451       this.addEvents({
42452          
42453         /**
42454          * @event select
42455          * Fires when a date is selected
42456              * @param {Roo.form.MonthFieeld} combo This combo box
42457              * @param {Date} date The date selected
42458              */
42459         'select' : true
42460          
42461     });
42462     
42463     
42464     if(typeof this.minValue == "string") {
42465         this.minValue = this.parseDate(this.minValue);
42466     }
42467     if(typeof this.maxValue == "string") {
42468         this.maxValue = this.parseDate(this.maxValue);
42469     }
42470     this.ddMatch = null;
42471     if(this.disabledDates){
42472         var dd = this.disabledDates;
42473         var re = "(?:";
42474         for(var i = 0; i < dd.length; i++){
42475             re += dd[i];
42476             if(i != dd.length-1) {
42477                 re += "|";
42478             }
42479         }
42480         this.ddMatch = new RegExp(re + ")");
42481     }
42482 };
42483
42484 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42485     /**
42486      * @cfg {String} format
42487      * The default date format string which can be overriden for localization support.  The format must be
42488      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42489      */
42490     format : "M Y",
42491     /**
42492      * @cfg {String} altFormats
42493      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42494      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42495      */
42496     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42497     /**
42498      * @cfg {Array} disabledDays
42499      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42500      */
42501     disabledDays : [0,1,2,3,4,5,6],
42502     /**
42503      * @cfg {String} disabledDaysText
42504      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42505      */
42506     disabledDaysText : "Disabled",
42507     /**
42508      * @cfg {Array} disabledDates
42509      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42510      * expression so they are very powerful. Some examples:
42511      * <ul>
42512      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42513      * <li>["03/08", "09/16"] would disable those days for every year</li>
42514      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42515      * <li>["03/../2006"] would disable every day in March 2006</li>
42516      * <li>["^03"] would disable every day in every March</li>
42517      * </ul>
42518      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42519      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42520      */
42521     disabledDates : null,
42522     /**
42523      * @cfg {String} disabledDatesText
42524      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42525      */
42526     disabledDatesText : "Disabled",
42527     /**
42528      * @cfg {Date/String} minValue
42529      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42530      * valid format (defaults to null).
42531      */
42532     minValue : null,
42533     /**
42534      * @cfg {Date/String} maxValue
42535      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42536      * valid format (defaults to null).
42537      */
42538     maxValue : null,
42539     /**
42540      * @cfg {String} minText
42541      * The error text to display when the date in the cell is before minValue (defaults to
42542      * 'The date in this field must be after {minValue}').
42543      */
42544     minText : "The date in this field must be equal to or after {0}",
42545     /**
42546      * @cfg {String} maxTextf
42547      * The error text to display when the date in the cell is after maxValue (defaults to
42548      * 'The date in this field must be before {maxValue}').
42549      */
42550     maxText : "The date in this field must be equal to or before {0}",
42551     /**
42552      * @cfg {String} invalidText
42553      * The error text to display when the date in the field is invalid (defaults to
42554      * '{value} is not a valid date - it must be in the format {format}').
42555      */
42556     invalidText : "{0} is not a valid date - it must be in the format {1}",
42557     /**
42558      * @cfg {String} triggerClass
42559      * An additional CSS class used to style the trigger button.  The trigger will always get the
42560      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42561      * which displays a calendar icon).
42562      */
42563     triggerClass : 'x-form-date-trigger',
42564     
42565
42566     /**
42567      * @cfg {Boolean} useIso
42568      * if enabled, then the date field will use a hidden field to store the 
42569      * real value as iso formated date. default (true)
42570      */ 
42571     useIso : true,
42572     /**
42573      * @cfg {String/Object} autoCreate
42574      * A DomHelper element spec, or true for a default element spec (defaults to
42575      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42576      */ 
42577     // private
42578     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42579     
42580     // private
42581     hiddenField: false,
42582     
42583     hideMonthPicker : false,
42584     
42585     onRender : function(ct, position)
42586     {
42587         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42588         if (this.useIso) {
42589             this.el.dom.removeAttribute('name'); 
42590             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42591                     'before', true);
42592             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42593             // prevent input submission
42594             this.hiddenName = this.name;
42595         }
42596             
42597             
42598     },
42599     
42600     // private
42601     validateValue : function(value)
42602     {
42603         value = this.formatDate(value);
42604         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42605             return false;
42606         }
42607         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42608              return true;
42609         }
42610         var svalue = value;
42611         value = this.parseDate(value);
42612         if(!value){
42613             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42614             return false;
42615         }
42616         var time = value.getTime();
42617         if(this.minValue && time < this.minValue.getTime()){
42618             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42619             return false;
42620         }
42621         if(this.maxValue && time > this.maxValue.getTime()){
42622             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42623             return false;
42624         }
42625         /*if(this.disabledDays){
42626             var day = value.getDay();
42627             for(var i = 0; i < this.disabledDays.length; i++) {
42628                 if(day === this.disabledDays[i]){
42629                     this.markInvalid(this.disabledDaysText);
42630                     return false;
42631                 }
42632             }
42633         }
42634         */
42635         var fvalue = this.formatDate(value);
42636         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42637             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42638             return false;
42639         }
42640         */
42641         return true;
42642     },
42643
42644     // private
42645     // Provides logic to override the default TriggerField.validateBlur which just returns true
42646     validateBlur : function(){
42647         return !this.menu || !this.menu.isVisible();
42648     },
42649
42650     /**
42651      * Returns the current date value of the date field.
42652      * @return {Date} The date value
42653      */
42654     getValue : function(){
42655         
42656         
42657         
42658         return  this.hiddenField ?
42659                 this.hiddenField.value :
42660                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42661     },
42662
42663     /**
42664      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42665      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42666      * (the default format used is "m/d/y").
42667      * <br />Usage:
42668      * <pre><code>
42669 //All of these calls set the same date value (May 4, 2006)
42670
42671 //Pass a date object:
42672 var dt = new Date('5/4/06');
42673 monthField.setValue(dt);
42674
42675 //Pass a date string (default format):
42676 monthField.setValue('5/4/06');
42677
42678 //Pass a date string (custom format):
42679 monthField.format = 'Y-m-d';
42680 monthField.setValue('2006-5-4');
42681 </code></pre>
42682      * @param {String/Date} date The date or valid date string
42683      */
42684     setValue : function(date){
42685         Roo.log('month setValue' + date);
42686         // can only be first of month..
42687         
42688         var val = this.parseDate(date);
42689         
42690         if (this.hiddenField) {
42691             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42692         }
42693         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42694         this.value = this.parseDate(date);
42695     },
42696
42697     // private
42698     parseDate : function(value){
42699         if(!value || value instanceof Date){
42700             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42701             return value;
42702         }
42703         var v = Date.parseDate(value, this.format);
42704         if (!v && this.useIso) {
42705             v = Date.parseDate(value, 'Y-m-d');
42706         }
42707         if (v) {
42708             // 
42709             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42710         }
42711         
42712         
42713         if(!v && this.altFormats){
42714             if(!this.altFormatsArray){
42715                 this.altFormatsArray = this.altFormats.split("|");
42716             }
42717             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42718                 v = Date.parseDate(value, this.altFormatsArray[i]);
42719             }
42720         }
42721         return v;
42722     },
42723
42724     // private
42725     formatDate : function(date, fmt){
42726         return (!date || !(date instanceof Date)) ?
42727                date : date.dateFormat(fmt || this.format);
42728     },
42729
42730     // private
42731     menuListeners : {
42732         select: function(m, d){
42733             this.setValue(d);
42734             this.fireEvent('select', this, d);
42735         },
42736         show : function(){ // retain focus styling
42737             this.onFocus();
42738         },
42739         hide : function(){
42740             this.focus.defer(10, this);
42741             var ml = this.menuListeners;
42742             this.menu.un("select", ml.select,  this);
42743             this.menu.un("show", ml.show,  this);
42744             this.menu.un("hide", ml.hide,  this);
42745         }
42746     },
42747     // private
42748     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42749     onTriggerClick : function(){
42750         if(this.disabled){
42751             return;
42752         }
42753         if(this.menu == null){
42754             this.menu = new Roo.menu.DateMenu();
42755            
42756         }
42757         
42758         Roo.apply(this.menu.picker,  {
42759             
42760             showClear: this.allowBlank,
42761             minDate : this.minValue,
42762             maxDate : this.maxValue,
42763             disabledDatesRE : this.ddMatch,
42764             disabledDatesText : this.disabledDatesText,
42765             
42766             format : this.useIso ? 'Y-m-d' : this.format,
42767             minText : String.format(this.minText, this.formatDate(this.minValue)),
42768             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42769             
42770         });
42771          this.menu.on(Roo.apply({}, this.menuListeners, {
42772             scope:this
42773         }));
42774        
42775         
42776         var m = this.menu;
42777         var p = m.picker;
42778         
42779         // hide month picker get's called when we called by 'before hide';
42780         
42781         var ignorehide = true;
42782         p.hideMonthPicker  = function(disableAnim){
42783             if (ignorehide) {
42784                 return;
42785             }
42786              if(this.monthPicker){
42787                 Roo.log("hideMonthPicker called");
42788                 if(disableAnim === true){
42789                     this.monthPicker.hide();
42790                 }else{
42791                     this.monthPicker.slideOut('t', {duration:.2});
42792                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42793                     p.fireEvent("select", this, this.value);
42794                     m.hide();
42795                 }
42796             }
42797         }
42798         
42799         Roo.log('picker set value');
42800         Roo.log(this.getValue());
42801         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42802         m.show(this.el, 'tl-bl?');
42803         ignorehide  = false;
42804         // this will trigger hideMonthPicker..
42805         
42806         
42807         // hidden the day picker
42808         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42809         
42810         
42811         
42812       
42813         
42814         p.showMonthPicker.defer(100, p);
42815     
42816         
42817        
42818     },
42819
42820     beforeBlur : function(){
42821         var v = this.parseDate(this.getRawValue());
42822         if(v){
42823             this.setValue(v);
42824         }
42825     }
42826
42827     /** @cfg {Boolean} grow @hide */
42828     /** @cfg {Number} growMin @hide */
42829     /** @cfg {Number} growMax @hide */
42830     /**
42831      * @hide
42832      * @method autoSize
42833      */
42834 });/*
42835  * Based on:
42836  * Ext JS Library 1.1.1
42837  * Copyright(c) 2006-2007, Ext JS, LLC.
42838  *
42839  * Originally Released Under LGPL - original licence link has changed is not relivant.
42840  *
42841  * Fork - LGPL
42842  * <script type="text/javascript">
42843  */
42844  
42845
42846 /**
42847  * @class Roo.form.ComboBox
42848  * @extends Roo.form.TriggerField
42849  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42850  * @constructor
42851  * Create a new ComboBox.
42852  * @param {Object} config Configuration options
42853  */
42854 Roo.form.ComboBox = function(config){
42855     Roo.form.ComboBox.superclass.constructor.call(this, config);
42856     this.addEvents({
42857         /**
42858          * @event expand
42859          * Fires when the dropdown list is expanded
42860              * @param {Roo.form.ComboBox} combo This combo box
42861              */
42862         'expand' : true,
42863         /**
42864          * @event collapse
42865          * Fires when the dropdown list is collapsed
42866              * @param {Roo.form.ComboBox} combo This combo box
42867              */
42868         'collapse' : true,
42869         /**
42870          * @event beforeselect
42871          * Fires before a list item is selected. Return false to cancel the selection.
42872              * @param {Roo.form.ComboBox} combo This combo box
42873              * @param {Roo.data.Record} record The data record returned from the underlying store
42874              * @param {Number} index The index of the selected item in the dropdown list
42875              */
42876         'beforeselect' : true,
42877         /**
42878          * @event select
42879          * Fires when a list item is selected
42880              * @param {Roo.form.ComboBox} combo This combo box
42881              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42882              * @param {Number} index The index of the selected item in the dropdown list
42883              */
42884         'select' : true,
42885         /**
42886          * @event beforequery
42887          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42888          * The event object passed has these properties:
42889              * @param {Roo.form.ComboBox} combo This combo box
42890              * @param {String} query The query
42891              * @param {Boolean} forceAll true to force "all" query
42892              * @param {Boolean} cancel true to cancel the query
42893              * @param {Object} e The query event object
42894              */
42895         'beforequery': true,
42896          /**
42897          * @event add
42898          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42899              * @param {Roo.form.ComboBox} combo This combo box
42900              */
42901         'add' : true,
42902         /**
42903          * @event edit
42904          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42905              * @param {Roo.form.ComboBox} combo This combo box
42906              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42907              */
42908         'edit' : true
42909         
42910         
42911     });
42912     if(this.transform){
42913         this.allowDomMove = false;
42914         var s = Roo.getDom(this.transform);
42915         if(!this.hiddenName){
42916             this.hiddenName = s.name;
42917         }
42918         if(!this.store){
42919             this.mode = 'local';
42920             var d = [], opts = s.options;
42921             for(var i = 0, len = opts.length;i < len; i++){
42922                 var o = opts[i];
42923                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42924                 if(o.selected) {
42925                     this.value = value;
42926                 }
42927                 d.push([value, o.text]);
42928             }
42929             this.store = new Roo.data.SimpleStore({
42930                 'id': 0,
42931                 fields: ['value', 'text'],
42932                 data : d
42933             });
42934             this.valueField = 'value';
42935             this.displayField = 'text';
42936         }
42937         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42938         if(!this.lazyRender){
42939             this.target = true;
42940             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42941             s.parentNode.removeChild(s); // remove it
42942             this.render(this.el.parentNode);
42943         }else{
42944             s.parentNode.removeChild(s); // remove it
42945         }
42946
42947     }
42948     if (this.store) {
42949         this.store = Roo.factory(this.store, Roo.data);
42950     }
42951     
42952     this.selectedIndex = -1;
42953     if(this.mode == 'local'){
42954         if(config.queryDelay === undefined){
42955             this.queryDelay = 10;
42956         }
42957         if(config.minChars === undefined){
42958             this.minChars = 0;
42959         }
42960     }
42961 };
42962
42963 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42964     /**
42965      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42966      */
42967     /**
42968      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42969      * rendering into an Roo.Editor, defaults to false)
42970      */
42971     /**
42972      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42973      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42974      */
42975     /**
42976      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42977      */
42978     /**
42979      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42980      * the dropdown list (defaults to undefined, with no header element)
42981      */
42982
42983      /**
42984      * @cfg {String/Roo.Template} tpl The template to use to render the output
42985      */
42986      
42987     // private
42988     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42989     /**
42990      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42991      */
42992     listWidth: undefined,
42993     /**
42994      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42995      * mode = 'remote' or 'text' if mode = 'local')
42996      */
42997     displayField: undefined,
42998     /**
42999      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
43000      * mode = 'remote' or 'value' if mode = 'local'). 
43001      * Note: use of a valueField requires the user make a selection
43002      * in order for a value to be mapped.
43003      */
43004     valueField: undefined,
43005     
43006     
43007     /**
43008      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
43009      * field's data value (defaults to the underlying DOM element's name)
43010      */
43011     hiddenName: undefined,
43012     /**
43013      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
43014      */
43015     listClass: '',
43016     /**
43017      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
43018      */
43019     selectedClass: 'x-combo-selected',
43020     /**
43021      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
43022      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
43023      * which displays a downward arrow icon).
43024      */
43025     triggerClass : 'x-form-arrow-trigger',
43026     /**
43027      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
43028      */
43029     shadow:'sides',
43030     /**
43031      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
43032      * anchor positions (defaults to 'tl-bl')
43033      */
43034     listAlign: 'tl-bl?',
43035     /**
43036      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
43037      */
43038     maxHeight: 300,
43039     /**
43040      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
43041      * query specified by the allQuery config option (defaults to 'query')
43042      */
43043     triggerAction: 'query',
43044     /**
43045      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
43046      * (defaults to 4, does not apply if editable = false)
43047      */
43048     minChars : 4,
43049     /**
43050      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
43051      * delay (typeAheadDelay) if it matches a known value (defaults to false)
43052      */
43053     typeAhead: false,
43054     /**
43055      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
43056      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
43057      */
43058     queryDelay: 500,
43059     /**
43060      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
43061      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
43062      */
43063     pageSize: 0,
43064     /**
43065      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
43066      * when editable = true (defaults to false)
43067      */
43068     selectOnFocus:false,
43069     /**
43070      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
43071      */
43072     queryParam: 'query',
43073     /**
43074      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
43075      * when mode = 'remote' (defaults to 'Loading...')
43076      */
43077     loadingText: 'Loading...',
43078     /**
43079      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
43080      */
43081     resizable: false,
43082     /**
43083      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
43084      */
43085     handleHeight : 8,
43086     /**
43087      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
43088      * traditional select (defaults to true)
43089      */
43090     editable: true,
43091     /**
43092      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
43093      */
43094     allQuery: '',
43095     /**
43096      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
43097      */
43098     mode: 'remote',
43099     /**
43100      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
43101      * listWidth has a higher value)
43102      */
43103     minListWidth : 70,
43104     /**
43105      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
43106      * allow the user to set arbitrary text into the field (defaults to false)
43107      */
43108     forceSelection:false,
43109     /**
43110      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
43111      * if typeAhead = true (defaults to 250)
43112      */
43113     typeAheadDelay : 250,
43114     /**
43115      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
43116      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
43117      */
43118     valueNotFoundText : undefined,
43119     /**
43120      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
43121      */
43122     blockFocus : false,
43123     
43124     /**
43125      * @cfg {Boolean} disableClear Disable showing of clear button.
43126      */
43127     disableClear : false,
43128     /**
43129      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
43130      */
43131     alwaysQuery : false,
43132     
43133     //private
43134     addicon : false,
43135     editicon: false,
43136     
43137     // element that contains real text value.. (when hidden is used..)
43138      
43139     // private
43140     onRender : function(ct, position)
43141     {
43142         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
43143         
43144         if(this.hiddenName){
43145             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43146                     'before', true);
43147             this.hiddenField.value =
43148                 this.hiddenValue !== undefined ? this.hiddenValue :
43149                 this.value !== undefined ? this.value : '';
43150
43151             // prevent input submission
43152             this.el.dom.removeAttribute('name');
43153              
43154              
43155         }
43156         
43157         if(Roo.isGecko){
43158             this.el.dom.setAttribute('autocomplete', 'off');
43159         }
43160
43161         var cls = 'x-combo-list';
43162
43163         this.list = new Roo.Layer({
43164             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43165         });
43166
43167         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43168         this.list.setWidth(lw);
43169         this.list.swallowEvent('mousewheel');
43170         this.assetHeight = 0;
43171
43172         if(this.title){
43173             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43174             this.assetHeight += this.header.getHeight();
43175         }
43176
43177         this.innerList = this.list.createChild({cls:cls+'-inner'});
43178         this.innerList.on('mouseover', this.onViewOver, this);
43179         this.innerList.on('mousemove', this.onViewMove, this);
43180         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43181         
43182         if(this.allowBlank && !this.pageSize && !this.disableClear){
43183             this.footer = this.list.createChild({cls:cls+'-ft'});
43184             this.pageTb = new Roo.Toolbar(this.footer);
43185            
43186         }
43187         if(this.pageSize){
43188             this.footer = this.list.createChild({cls:cls+'-ft'});
43189             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
43190                     {pageSize: this.pageSize});
43191             
43192         }
43193         
43194         if (this.pageTb && this.allowBlank && !this.disableClear) {
43195             var _this = this;
43196             this.pageTb.add(new Roo.Toolbar.Fill(), {
43197                 cls: 'x-btn-icon x-btn-clear',
43198                 text: '&#160;',
43199                 handler: function()
43200                 {
43201                     _this.collapse();
43202                     _this.clearValue();
43203                     _this.onSelect(false, -1);
43204                 }
43205             });
43206         }
43207         if (this.footer) {
43208             this.assetHeight += this.footer.getHeight();
43209         }
43210         
43211
43212         if(!this.tpl){
43213             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
43214         }
43215
43216         this.view = new Roo.View(this.innerList, this.tpl, {
43217             singleSelect:true,
43218             store: this.store,
43219             selectedClass: this.selectedClass
43220         });
43221
43222         this.view.on('click', this.onViewClick, this);
43223
43224         this.store.on('beforeload', this.onBeforeLoad, this);
43225         this.store.on('load', this.onLoad, this);
43226         this.store.on('loadexception', this.onLoadException, this);
43227
43228         if(this.resizable){
43229             this.resizer = new Roo.Resizable(this.list,  {
43230                pinned:true, handles:'se'
43231             });
43232             this.resizer.on('resize', function(r, w, h){
43233                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
43234                 this.listWidth = w;
43235                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
43236                 this.restrictHeight();
43237             }, this);
43238             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
43239         }
43240         if(!this.editable){
43241             this.editable = true;
43242             this.setEditable(false);
43243         }  
43244         
43245         
43246         if (typeof(this.events.add.listeners) != 'undefined') {
43247             
43248             this.addicon = this.wrap.createChild(
43249                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
43250        
43251             this.addicon.on('click', function(e) {
43252                 this.fireEvent('add', this);
43253             }, this);
43254         }
43255         if (typeof(this.events.edit.listeners) != 'undefined') {
43256             
43257             this.editicon = this.wrap.createChild(
43258                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
43259             if (this.addicon) {
43260                 this.editicon.setStyle('margin-left', '40px');
43261             }
43262             this.editicon.on('click', function(e) {
43263                 
43264                 // we fire even  if inothing is selected..
43265                 this.fireEvent('edit', this, this.lastData );
43266                 
43267             }, this);
43268         }
43269         
43270         
43271         
43272     },
43273
43274     // private
43275     initEvents : function(){
43276         Roo.form.ComboBox.superclass.initEvents.call(this);
43277
43278         this.keyNav = new Roo.KeyNav(this.el, {
43279             "up" : function(e){
43280                 this.inKeyMode = true;
43281                 this.selectPrev();
43282             },
43283
43284             "down" : function(e){
43285                 if(!this.isExpanded()){
43286                     this.onTriggerClick();
43287                 }else{
43288                     this.inKeyMode = true;
43289                     this.selectNext();
43290                 }
43291             },
43292
43293             "enter" : function(e){
43294                 this.onViewClick();
43295                 //return true;
43296             },
43297
43298             "esc" : function(e){
43299                 this.collapse();
43300             },
43301
43302             "tab" : function(e){
43303                 this.onViewClick(false);
43304                 this.fireEvent("specialkey", this, e);
43305                 return true;
43306             },
43307
43308             scope : this,
43309
43310             doRelay : function(foo, bar, hname){
43311                 if(hname == 'down' || this.scope.isExpanded()){
43312                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43313                 }
43314                 return true;
43315             },
43316
43317             forceKeyDown: true
43318         });
43319         this.queryDelay = Math.max(this.queryDelay || 10,
43320                 this.mode == 'local' ? 10 : 250);
43321         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43322         if(this.typeAhead){
43323             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43324         }
43325         if(this.editable !== false){
43326             this.el.on("keyup", this.onKeyUp, this);
43327         }
43328         if(this.forceSelection){
43329             this.on('blur', this.doForce, this);
43330         }
43331     },
43332
43333     onDestroy : function(){
43334         if(this.view){
43335             this.view.setStore(null);
43336             this.view.el.removeAllListeners();
43337             this.view.el.remove();
43338             this.view.purgeListeners();
43339         }
43340         if(this.list){
43341             this.list.destroy();
43342         }
43343         if(this.store){
43344             this.store.un('beforeload', this.onBeforeLoad, this);
43345             this.store.un('load', this.onLoad, this);
43346             this.store.un('loadexception', this.onLoadException, this);
43347         }
43348         Roo.form.ComboBox.superclass.onDestroy.call(this);
43349     },
43350
43351     // private
43352     fireKey : function(e){
43353         if(e.isNavKeyPress() && !this.list.isVisible()){
43354             this.fireEvent("specialkey", this, e);
43355         }
43356     },
43357
43358     // private
43359     onResize: function(w, h){
43360         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43361         
43362         if(typeof w != 'number'){
43363             // we do not handle it!?!?
43364             return;
43365         }
43366         var tw = this.trigger.getWidth();
43367         tw += this.addicon ? this.addicon.getWidth() : 0;
43368         tw += this.editicon ? this.editicon.getWidth() : 0;
43369         var x = w - tw;
43370         this.el.setWidth( this.adjustWidth('input', x));
43371             
43372         this.trigger.setStyle('left', x+'px');
43373         
43374         if(this.list && this.listWidth === undefined){
43375             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43376             this.list.setWidth(lw);
43377             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43378         }
43379         
43380     
43381         
43382     },
43383
43384     /**
43385      * Allow or prevent the user from directly editing the field text.  If false is passed,
43386      * the user will only be able to select from the items defined in the dropdown list.  This method
43387      * is the runtime equivalent of setting the 'editable' config option at config time.
43388      * @param {Boolean} value True to allow the user to directly edit the field text
43389      */
43390     setEditable : function(value){
43391         if(value == this.editable){
43392             return;
43393         }
43394         this.editable = value;
43395         if(!value){
43396             this.el.dom.setAttribute('readOnly', true);
43397             this.el.on('mousedown', this.onTriggerClick,  this);
43398             this.el.addClass('x-combo-noedit');
43399         }else{
43400             this.el.dom.setAttribute('readOnly', false);
43401             this.el.un('mousedown', this.onTriggerClick,  this);
43402             this.el.removeClass('x-combo-noedit');
43403         }
43404     },
43405
43406     // private
43407     onBeforeLoad : function(){
43408         if(!this.hasFocus){
43409             return;
43410         }
43411         this.innerList.update(this.loadingText ?
43412                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43413         this.restrictHeight();
43414         this.selectedIndex = -1;
43415     },
43416
43417     // private
43418     onLoad : function(){
43419         if(!this.hasFocus){
43420             return;
43421         }
43422         if(this.store.getCount() > 0){
43423             this.expand();
43424             this.restrictHeight();
43425             if(this.lastQuery == this.allQuery){
43426                 if(this.editable){
43427                     this.el.dom.select();
43428                 }
43429                 if(!this.selectByValue(this.value, true)){
43430                     this.select(0, true);
43431                 }
43432             }else{
43433                 this.selectNext();
43434                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43435                     this.taTask.delay(this.typeAheadDelay);
43436                 }
43437             }
43438         }else{
43439             this.onEmptyResults();
43440         }
43441         //this.el.focus();
43442     },
43443     // private
43444     onLoadException : function()
43445     {
43446         this.collapse();
43447         Roo.log(this.store.reader.jsonData);
43448         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43449             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43450         }
43451         
43452         
43453     },
43454     // private
43455     onTypeAhead : function(){
43456         if(this.store.getCount() > 0){
43457             var r = this.store.getAt(0);
43458             var newValue = r.data[this.displayField];
43459             var len = newValue.length;
43460             var selStart = this.getRawValue().length;
43461             if(selStart != len){
43462                 this.setRawValue(newValue);
43463                 this.selectText(selStart, newValue.length);
43464             }
43465         }
43466     },
43467
43468     // private
43469     onSelect : function(record, index){
43470         if(this.fireEvent('beforeselect', this, record, index) !== false){
43471             this.setFromData(index > -1 ? record.data : false);
43472             this.collapse();
43473             this.fireEvent('select', this, record, index);
43474         }
43475     },
43476
43477     /**
43478      * Returns the currently selected field value or empty string if no value is set.
43479      * @return {String} value The selected value
43480      */
43481     getValue : function(){
43482         if(this.valueField){
43483             return typeof this.value != 'undefined' ? this.value : '';
43484         }
43485         return Roo.form.ComboBox.superclass.getValue.call(this);
43486     },
43487
43488     /**
43489      * Clears any text/value currently set in the field
43490      */
43491     clearValue : function(){
43492         if(this.hiddenField){
43493             this.hiddenField.value = '';
43494         }
43495         this.value = '';
43496         this.setRawValue('');
43497         this.lastSelectionText = '';
43498         
43499     },
43500
43501     /**
43502      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43503      * will be displayed in the field.  If the value does not match the data value of an existing item,
43504      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43505      * Otherwise the field will be blank (although the value will still be set).
43506      * @param {String} value The value to match
43507      */
43508     setValue : function(v){
43509         var text = v;
43510         if(this.valueField){
43511             var r = this.findRecord(this.valueField, v);
43512             if(r){
43513                 text = r.data[this.displayField];
43514             }else if(this.valueNotFoundText !== undefined){
43515                 text = this.valueNotFoundText;
43516             }
43517         }
43518         this.lastSelectionText = text;
43519         if(this.hiddenField){
43520             this.hiddenField.value = v;
43521         }
43522         Roo.form.ComboBox.superclass.setValue.call(this, text);
43523         this.value = v;
43524     },
43525     /**
43526      * @property {Object} the last set data for the element
43527      */
43528     
43529     lastData : false,
43530     /**
43531      * Sets the value of the field based on a object which is related to the record format for the store.
43532      * @param {Object} value the value to set as. or false on reset?
43533      */
43534     setFromData : function(o){
43535         var dv = ''; // display value
43536         var vv = ''; // value value..
43537         this.lastData = o;
43538         if (this.displayField) {
43539             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43540         } else {
43541             // this is an error condition!!!
43542             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43543         }
43544         
43545         if(this.valueField){
43546             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43547         }
43548         if(this.hiddenField){
43549             this.hiddenField.value = vv;
43550             
43551             this.lastSelectionText = dv;
43552             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43553             this.value = vv;
43554             return;
43555         }
43556         // no hidden field.. - we store the value in 'value', but still display
43557         // display field!!!!
43558         this.lastSelectionText = dv;
43559         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43560         this.value = vv;
43561         
43562         
43563     },
43564     // private
43565     reset : function(){
43566         // overridden so that last data is reset..
43567         this.setValue(this.resetValue);
43568         this.originalValue = this.getValue();
43569         this.clearInvalid();
43570         this.lastData = false;
43571         if (this.view) {
43572             this.view.clearSelections();
43573         }
43574     },
43575     // private
43576     findRecord : function(prop, value){
43577         var record;
43578         if(this.store.getCount() > 0){
43579             this.store.each(function(r){
43580                 if(r.data[prop] == value){
43581                     record = r;
43582                     return false;
43583                 }
43584                 return true;
43585             });
43586         }
43587         return record;
43588     },
43589     
43590     getName: function()
43591     {
43592         // returns hidden if it's set..
43593         if (!this.rendered) {return ''};
43594         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43595         
43596     },
43597     // private
43598     onViewMove : function(e, t){
43599         this.inKeyMode = false;
43600     },
43601
43602     // private
43603     onViewOver : function(e, t){
43604         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43605             return;
43606         }
43607         var item = this.view.findItemFromChild(t);
43608         if(item){
43609             var index = this.view.indexOf(item);
43610             this.select(index, false);
43611         }
43612     },
43613
43614     // private
43615     onViewClick : function(doFocus)
43616     {
43617         var index = this.view.getSelectedIndexes()[0];
43618         var r = this.store.getAt(index);
43619         if(r){
43620             this.onSelect(r, index);
43621         }
43622         if(doFocus !== false && !this.blockFocus){
43623             this.el.focus();
43624         }
43625     },
43626
43627     // private
43628     restrictHeight : function(){
43629         this.innerList.dom.style.height = '';
43630         var inner = this.innerList.dom;
43631         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43632         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43633         this.list.beginUpdate();
43634         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43635         this.list.alignTo(this.el, this.listAlign);
43636         this.list.endUpdate();
43637     },
43638
43639     // private
43640     onEmptyResults : function(){
43641         this.collapse();
43642     },
43643
43644     /**
43645      * Returns true if the dropdown list is expanded, else false.
43646      */
43647     isExpanded : function(){
43648         return this.list.isVisible();
43649     },
43650
43651     /**
43652      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43653      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43654      * @param {String} value The data value of the item to select
43655      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43656      * selected item if it is not currently in view (defaults to true)
43657      * @return {Boolean} True if the value matched an item in the list, else false
43658      */
43659     selectByValue : function(v, scrollIntoView){
43660         if(v !== undefined && v !== null){
43661             var r = this.findRecord(this.valueField || this.displayField, v);
43662             if(r){
43663                 this.select(this.store.indexOf(r), scrollIntoView);
43664                 return true;
43665             }
43666         }
43667         return false;
43668     },
43669
43670     /**
43671      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43672      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43673      * @param {Number} index The zero-based index of the list item to select
43674      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43675      * selected item if it is not currently in view (defaults to true)
43676      */
43677     select : function(index, scrollIntoView){
43678         this.selectedIndex = index;
43679         this.view.select(index);
43680         if(scrollIntoView !== false){
43681             var el = this.view.getNode(index);
43682             if(el){
43683                 this.innerList.scrollChildIntoView(el, false);
43684             }
43685         }
43686     },
43687
43688     // private
43689     selectNext : function(){
43690         var ct = this.store.getCount();
43691         if(ct > 0){
43692             if(this.selectedIndex == -1){
43693                 this.select(0);
43694             }else if(this.selectedIndex < ct-1){
43695                 this.select(this.selectedIndex+1);
43696             }
43697         }
43698     },
43699
43700     // private
43701     selectPrev : function(){
43702         var ct = this.store.getCount();
43703         if(ct > 0){
43704             if(this.selectedIndex == -1){
43705                 this.select(0);
43706             }else if(this.selectedIndex != 0){
43707                 this.select(this.selectedIndex-1);
43708             }
43709         }
43710     },
43711
43712     // private
43713     onKeyUp : function(e){
43714         if(this.editable !== false && !e.isSpecialKey()){
43715             this.lastKey = e.getKey();
43716             this.dqTask.delay(this.queryDelay);
43717         }
43718     },
43719
43720     // private
43721     validateBlur : function(){
43722         return !this.list || !this.list.isVisible();   
43723     },
43724
43725     // private
43726     initQuery : function(){
43727         this.doQuery(this.getRawValue());
43728     },
43729
43730     // private
43731     doForce : function(){
43732         if(this.el.dom.value.length > 0){
43733             this.el.dom.value =
43734                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43735              
43736         }
43737     },
43738
43739     /**
43740      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43741      * query allowing the query action to be canceled if needed.
43742      * @param {String} query The SQL query to execute
43743      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43744      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43745      * saved in the current store (defaults to false)
43746      */
43747     doQuery : function(q, forceAll){
43748         if(q === undefined || q === null){
43749             q = '';
43750         }
43751         var qe = {
43752             query: q,
43753             forceAll: forceAll,
43754             combo: this,
43755             cancel:false
43756         };
43757         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43758             return false;
43759         }
43760         q = qe.query;
43761         forceAll = qe.forceAll;
43762         if(forceAll === true || (q.length >= this.minChars)){
43763             if(this.lastQuery != q || this.alwaysQuery){
43764                 this.lastQuery = q;
43765                 if(this.mode == 'local'){
43766                     this.selectedIndex = -1;
43767                     if(forceAll){
43768                         this.store.clearFilter();
43769                     }else{
43770                         this.store.filter(this.displayField, q);
43771                     }
43772                     this.onLoad();
43773                 }else{
43774                     this.store.baseParams[this.queryParam] = q;
43775                     this.store.load({
43776                         params: this.getParams(q)
43777                     });
43778                     this.expand();
43779                 }
43780             }else{
43781                 this.selectedIndex = -1;
43782                 this.onLoad();   
43783             }
43784         }
43785     },
43786
43787     // private
43788     getParams : function(q){
43789         var p = {};
43790         //p[this.queryParam] = q;
43791         if(this.pageSize){
43792             p.start = 0;
43793             p.limit = this.pageSize;
43794         }
43795         return p;
43796     },
43797
43798     /**
43799      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43800      */
43801     collapse : function(){
43802         if(!this.isExpanded()){
43803             return;
43804         }
43805         this.list.hide();
43806         Roo.get(document).un('mousedown', this.collapseIf, this);
43807         Roo.get(document).un('mousewheel', this.collapseIf, this);
43808         if (!this.editable) {
43809             Roo.get(document).un('keydown', this.listKeyPress, this);
43810         }
43811         this.fireEvent('collapse', this);
43812     },
43813
43814     // private
43815     collapseIf : function(e){
43816         if(!e.within(this.wrap) && !e.within(this.list)){
43817             this.collapse();
43818         }
43819     },
43820
43821     /**
43822      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43823      */
43824     expand : function(){
43825         if(this.isExpanded() || !this.hasFocus){
43826             return;
43827         }
43828         this.list.alignTo(this.el, this.listAlign);
43829         this.list.show();
43830         Roo.get(document).on('mousedown', this.collapseIf, this);
43831         Roo.get(document).on('mousewheel', this.collapseIf, this);
43832         if (!this.editable) {
43833             Roo.get(document).on('keydown', this.listKeyPress, this);
43834         }
43835         
43836         this.fireEvent('expand', this);
43837     },
43838
43839     // private
43840     // Implements the default empty TriggerField.onTriggerClick function
43841     onTriggerClick : function(){
43842         if(this.disabled){
43843             return;
43844         }
43845         if(this.isExpanded()){
43846             this.collapse();
43847             if (!this.blockFocus) {
43848                 this.el.focus();
43849             }
43850             
43851         }else {
43852             this.hasFocus = true;
43853             if(this.triggerAction == 'all') {
43854                 this.doQuery(this.allQuery, true);
43855             } else {
43856                 this.doQuery(this.getRawValue());
43857             }
43858             if (!this.blockFocus) {
43859                 this.el.focus();
43860             }
43861         }
43862     },
43863     listKeyPress : function(e)
43864     {
43865         //Roo.log('listkeypress');
43866         // scroll to first matching element based on key pres..
43867         if (e.isSpecialKey()) {
43868             return false;
43869         }
43870         var k = String.fromCharCode(e.getKey()).toUpperCase();
43871         //Roo.log(k);
43872         var match  = false;
43873         var csel = this.view.getSelectedNodes();
43874         var cselitem = false;
43875         if (csel.length) {
43876             var ix = this.view.indexOf(csel[0]);
43877             cselitem  = this.store.getAt(ix);
43878             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43879                 cselitem = false;
43880             }
43881             
43882         }
43883         
43884         this.store.each(function(v) { 
43885             if (cselitem) {
43886                 // start at existing selection.
43887                 if (cselitem.id == v.id) {
43888                     cselitem = false;
43889                 }
43890                 return;
43891             }
43892                 
43893             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43894                 match = this.store.indexOf(v);
43895                 return false;
43896             }
43897         }, this);
43898         
43899         if (match === false) {
43900             return true; // no more action?
43901         }
43902         // scroll to?
43903         this.view.select(match);
43904         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43905         sn.scrollIntoView(sn.dom.parentNode, false);
43906     } 
43907
43908     /** 
43909     * @cfg {Boolean} grow 
43910     * @hide 
43911     */
43912     /** 
43913     * @cfg {Number} growMin 
43914     * @hide 
43915     */
43916     /** 
43917     * @cfg {Number} growMax 
43918     * @hide 
43919     */
43920     /**
43921      * @hide
43922      * @method autoSize
43923      */
43924 });/*
43925  * Copyright(c) 2010-2012, Roo J Solutions Limited
43926  *
43927  * Licence LGPL
43928  *
43929  */
43930
43931 /**
43932  * @class Roo.form.ComboBoxArray
43933  * @extends Roo.form.TextField
43934  * A facebook style adder... for lists of email / people / countries  etc...
43935  * pick multiple items from a combo box, and shows each one.
43936  *
43937  *  Fred [x]  Brian [x]  [Pick another |v]
43938  *
43939  *
43940  *  For this to work: it needs various extra information
43941  *    - normal combo problay has
43942  *      name, hiddenName
43943  *    + displayField, valueField
43944  *
43945  *    For our purpose...
43946  *
43947  *
43948  *   If we change from 'extends' to wrapping...
43949  *   
43950  *  
43951  *
43952  
43953  
43954  * @constructor
43955  * Create a new ComboBoxArray.
43956  * @param {Object} config Configuration options
43957  */
43958  
43959
43960 Roo.form.ComboBoxArray = function(config)
43961 {
43962     this.addEvents({
43963         /**
43964          * @event beforeremove
43965          * Fires before remove the value from the list
43966              * @param {Roo.form.ComboBoxArray} _self This combo box array
43967              * @param {Roo.form.ComboBoxArray.Item} item removed item
43968              */
43969         'beforeremove' : true,
43970         /**
43971          * @event remove
43972          * Fires when remove the value from the list
43973              * @param {Roo.form.ComboBoxArray} _self This combo box array
43974              * @param {Roo.form.ComboBoxArray.Item} item removed item
43975              */
43976         'remove' : true
43977         
43978         
43979     });
43980     
43981     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43982     
43983     this.items = new Roo.util.MixedCollection(false);
43984     
43985     // construct the child combo...
43986     
43987     
43988     
43989     
43990    
43991     
43992 }
43993
43994  
43995 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43996
43997     /**
43998      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43999      */
44000     
44001     lastData : false,
44002     
44003     // behavies liek a hiddne field
44004     inputType:      'hidden',
44005     /**
44006      * @cfg {Number} width The width of the box that displays the selected element
44007      */ 
44008     width:          300,
44009
44010     
44011     
44012     /**
44013      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
44014      */
44015     name : false,
44016     /**
44017      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
44018      */
44019     hiddenName : false,
44020       /**
44021      * @cfg {String} seperator    The value seperator normally ',' 
44022      */
44023     seperator : ',',
44024     
44025     // private the array of items that are displayed..
44026     items  : false,
44027     // private - the hidden field el.
44028     hiddenEl : false,
44029     // private - the filed el..
44030     el : false,
44031     
44032     //validateValue : function() { return true; }, // all values are ok!
44033     //onAddClick: function() { },
44034     
44035     onRender : function(ct, position) 
44036     {
44037         
44038         // create the standard hidden element
44039         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
44040         
44041         
44042         // give fake names to child combo;
44043         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
44044         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
44045         
44046         this.combo = Roo.factory(this.combo, Roo.form);
44047         this.combo.onRender(ct, position);
44048         if (typeof(this.combo.width) != 'undefined') {
44049             this.combo.onResize(this.combo.width,0);
44050         }
44051         
44052         this.combo.initEvents();
44053         
44054         // assigned so form know we need to do this..
44055         this.store          = this.combo.store;
44056         this.valueField     = this.combo.valueField;
44057         this.displayField   = this.combo.displayField ;
44058         
44059         
44060         this.combo.wrap.addClass('x-cbarray-grp');
44061         
44062         var cbwrap = this.combo.wrap.createChild(
44063             {tag: 'div', cls: 'x-cbarray-cb'},
44064             this.combo.el.dom
44065         );
44066         
44067              
44068         this.hiddenEl = this.combo.wrap.createChild({
44069             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
44070         });
44071         this.el = this.combo.wrap.createChild({
44072             tag: 'input',  type:'hidden' , name: this.name, value : ''
44073         });
44074          //   this.el.dom.removeAttribute("name");
44075         
44076         
44077         this.outerWrap = this.combo.wrap;
44078         this.wrap = cbwrap;
44079         
44080         this.outerWrap.setWidth(this.width);
44081         this.outerWrap.dom.removeChild(this.el.dom);
44082         
44083         this.wrap.dom.appendChild(this.el.dom);
44084         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
44085         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
44086         
44087         this.combo.trigger.setStyle('position','relative');
44088         this.combo.trigger.setStyle('left', '0px');
44089         this.combo.trigger.setStyle('top', '2px');
44090         
44091         this.combo.el.setStyle('vertical-align', 'text-bottom');
44092         
44093         //this.trigger.setStyle('vertical-align', 'top');
44094         
44095         // this should use the code from combo really... on('add' ....)
44096         if (this.adder) {
44097             
44098         
44099             this.adder = this.outerWrap.createChild(
44100                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
44101             var _t = this;
44102             this.adder.on('click', function(e) {
44103                 _t.fireEvent('adderclick', this, e);
44104             }, _t);
44105         }
44106         //var _t = this;
44107         //this.adder.on('click', this.onAddClick, _t);
44108         
44109         
44110         this.combo.on('select', function(cb, rec, ix) {
44111             this.addItem(rec.data);
44112             
44113             cb.setValue('');
44114             cb.el.dom.value = '';
44115             //cb.lastData = rec.data;
44116             // add to list
44117             
44118         }, this);
44119         
44120         
44121     },
44122     
44123     
44124     getName: function()
44125     {
44126         // returns hidden if it's set..
44127         if (!this.rendered) {return ''};
44128         return  this.hiddenName ? this.hiddenName : this.name;
44129         
44130     },
44131     
44132     
44133     onResize: function(w, h){
44134         
44135         return;
44136         // not sure if this is needed..
44137         //this.combo.onResize(w,h);
44138         
44139         if(typeof w != 'number'){
44140             // we do not handle it!?!?
44141             return;
44142         }
44143         var tw = this.combo.trigger.getWidth();
44144         tw += this.addicon ? this.addicon.getWidth() : 0;
44145         tw += this.editicon ? this.editicon.getWidth() : 0;
44146         var x = w - tw;
44147         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
44148             
44149         this.combo.trigger.setStyle('left', '0px');
44150         
44151         if(this.list && this.listWidth === undefined){
44152             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
44153             this.list.setWidth(lw);
44154             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
44155         }
44156         
44157     
44158         
44159     },
44160     
44161     addItem: function(rec)
44162     {
44163         var valueField = this.combo.valueField;
44164         var displayField = this.combo.displayField;
44165         
44166         if (this.items.indexOfKey(rec[valueField]) > -1) {
44167             //console.log("GOT " + rec.data.id);
44168             return;
44169         }
44170         
44171         var x = new Roo.form.ComboBoxArray.Item({
44172             //id : rec[this.idField],
44173             data : rec,
44174             displayField : displayField ,
44175             tipField : displayField ,
44176             cb : this
44177         });
44178         // use the 
44179         this.items.add(rec[valueField],x);
44180         // add it before the element..
44181         this.updateHiddenEl();
44182         x.render(this.outerWrap, this.wrap.dom);
44183         // add the image handler..
44184     },
44185     
44186     updateHiddenEl : function()
44187     {
44188         this.validate();
44189         if (!this.hiddenEl) {
44190             return;
44191         }
44192         var ar = [];
44193         var idField = this.combo.valueField;
44194         
44195         this.items.each(function(f) {
44196             ar.push(f.data[idField]);
44197         });
44198         this.hiddenEl.dom.value = ar.join(this.seperator);
44199         this.validate();
44200     },
44201     
44202     reset : function()
44203     {
44204         this.items.clear();
44205         
44206         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
44207            el.remove();
44208         });
44209         
44210         this.el.dom.value = '';
44211         if (this.hiddenEl) {
44212             this.hiddenEl.dom.value = '';
44213         }
44214         
44215     },
44216     getValue: function()
44217     {
44218         return this.hiddenEl ? this.hiddenEl.dom.value : '';
44219     },
44220     setValue: function(v) // not a valid action - must use addItems..
44221     {
44222         
44223         this.reset();
44224          
44225         if (this.store.isLocal && (typeof(v) == 'string')) {
44226             // then we can use the store to find the values..
44227             // comma seperated at present.. this needs to allow JSON based encoding..
44228             this.hiddenEl.value  = v;
44229             var v_ar = [];
44230             Roo.each(v.split(this.seperator), function(k) {
44231                 Roo.log("CHECK " + this.valueField + ',' + k);
44232                 var li = this.store.query(this.valueField, k);
44233                 if (!li.length) {
44234                     return;
44235                 }
44236                 var add = {};
44237                 add[this.valueField] = k;
44238                 add[this.displayField] = li.item(0).data[this.displayField];
44239                 
44240                 this.addItem(add);
44241             }, this) 
44242              
44243         }
44244         if (typeof(v) == 'object' ) {
44245             // then let's assume it's an array of objects..
44246             Roo.each(v, function(l) {
44247                 var add = l;
44248                 if (typeof(l) == 'string') {
44249                     add = {};
44250                     add[this.valueField] = l;
44251                     add[this.displayField] = l
44252                 }
44253                 this.addItem(add);
44254             }, this);
44255              
44256         }
44257         
44258         
44259     },
44260     setFromData: function(v)
44261     {
44262         // this recieves an object, if setValues is called.
44263         this.reset();
44264         this.el.dom.value = v[this.displayField];
44265         this.hiddenEl.dom.value = v[this.valueField];
44266         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44267             return;
44268         }
44269         var kv = v[this.valueField];
44270         var dv = v[this.displayField];
44271         kv = typeof(kv) != 'string' ? '' : kv;
44272         dv = typeof(dv) != 'string' ? '' : dv;
44273         
44274         
44275         var keys = kv.split(this.seperator);
44276         var display = dv.split(this.seperator);
44277         for (var i = 0 ; i < keys.length; i++) {
44278             add = {};
44279             add[this.valueField] = keys[i];
44280             add[this.displayField] = display[i];
44281             this.addItem(add);
44282         }
44283       
44284         
44285     },
44286     
44287     /**
44288      * Validates the combox array value
44289      * @return {Boolean} True if the value is valid, else false
44290      */
44291     validate : function(){
44292         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44293             this.clearInvalid();
44294             return true;
44295         }
44296         return false;
44297     },
44298     
44299     validateValue : function(value){
44300         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44301         
44302     },
44303     
44304     /*@
44305      * overide
44306      * 
44307      */
44308     isDirty : function() {
44309         if(this.disabled) {
44310             return false;
44311         }
44312         
44313         try {
44314             var d = Roo.decode(String(this.originalValue));
44315         } catch (e) {
44316             return String(this.getValue()) !== String(this.originalValue);
44317         }
44318         
44319         var originalValue = [];
44320         
44321         for (var i = 0; i < d.length; i++){
44322             originalValue.push(d[i][this.valueField]);
44323         }
44324         
44325         return String(this.getValue()) !== String(originalValue.join(this.seperator));
44326         
44327     }
44328     
44329 });
44330
44331
44332
44333 /**
44334  * @class Roo.form.ComboBoxArray.Item
44335  * @extends Roo.BoxComponent
44336  * A selected item in the list
44337  *  Fred [x]  Brian [x]  [Pick another |v]
44338  * 
44339  * @constructor
44340  * Create a new item.
44341  * @param {Object} config Configuration options
44342  */
44343  
44344 Roo.form.ComboBoxArray.Item = function(config) {
44345     config.id = Roo.id();
44346     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44347 }
44348
44349 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44350     data : {},
44351     cb: false,
44352     displayField : false,
44353     tipField : false,
44354     
44355     
44356     defaultAutoCreate : {
44357         tag: 'div',
44358         cls: 'x-cbarray-item',
44359         cn : [ 
44360             { tag: 'div' },
44361             {
44362                 tag: 'img',
44363                 width:16,
44364                 height : 16,
44365                 src : Roo.BLANK_IMAGE_URL ,
44366                 align: 'center'
44367             }
44368         ]
44369         
44370     },
44371     
44372  
44373     onRender : function(ct, position)
44374     {
44375         Roo.form.Field.superclass.onRender.call(this, ct, position);
44376         
44377         if(!this.el){
44378             var cfg = this.getAutoCreate();
44379             this.el = ct.createChild(cfg, position);
44380         }
44381         
44382         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44383         
44384         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44385             this.cb.renderer(this.data) :
44386             String.format('{0}',this.data[this.displayField]);
44387         
44388             
44389         this.el.child('div').dom.setAttribute('qtip',
44390                         String.format('{0}',this.data[this.tipField])
44391         );
44392         
44393         this.el.child('img').on('click', this.remove, this);
44394         
44395     },
44396    
44397     remove : function()
44398     {
44399         if(this.cb.disabled){
44400             return;
44401         }
44402         
44403         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44404             this.cb.items.remove(this);
44405             this.el.child('img').un('click', this.remove, this);
44406             this.el.remove();
44407             this.cb.updateHiddenEl();
44408
44409             this.cb.fireEvent('remove', this.cb, this);
44410         }
44411         
44412     }
44413 });/*
44414  * RooJS Library 1.1.1
44415  * Copyright(c) 2008-2011  Alan Knowles
44416  *
44417  * License - LGPL
44418  */
44419  
44420
44421 /**
44422  * @class Roo.form.ComboNested
44423  * @extends Roo.form.ComboBox
44424  * A combobox for that allows selection of nested items in a list,
44425  * eg.
44426  *
44427  *  Book
44428  *    -> red
44429  *    -> green
44430  *  Table
44431  *    -> square
44432  *      ->red
44433  *      ->green
44434  *    -> rectangle
44435  *      ->green
44436  *      
44437  * 
44438  * @constructor
44439  * Create a new ComboNested
44440  * @param {Object} config Configuration options
44441  */
44442 Roo.form.ComboNested = function(config){
44443     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44444     // should verify some data...
44445     // like
44446     // hiddenName = required..
44447     // displayField = required
44448     // valudField == required
44449     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44450     var _t = this;
44451     Roo.each(req, function(e) {
44452         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44453             throw "Roo.form.ComboNested : missing value for: " + e;
44454         }
44455     });
44456      
44457     
44458 };
44459
44460 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44461    
44462     /*
44463      * @config {Number} max Number of columns to show
44464      */
44465     
44466     maxColumns : 3,
44467    
44468     list : null, // the outermost div..
44469     innerLists : null, // the
44470     views : null,
44471     stores : null,
44472     // private
44473     loadingChildren : false,
44474     
44475     onRender : function(ct, position)
44476     {
44477         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44478         
44479         if(this.hiddenName){
44480             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44481                     'before', true);
44482             this.hiddenField.value =
44483                 this.hiddenValue !== undefined ? this.hiddenValue :
44484                 this.value !== undefined ? this.value : '';
44485
44486             // prevent input submission
44487             this.el.dom.removeAttribute('name');
44488              
44489              
44490         }
44491         
44492         if(Roo.isGecko){
44493             this.el.dom.setAttribute('autocomplete', 'off');
44494         }
44495
44496         var cls = 'x-combo-list';
44497
44498         this.list = new Roo.Layer({
44499             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44500         });
44501
44502         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44503         this.list.setWidth(lw);
44504         this.list.swallowEvent('mousewheel');
44505         this.assetHeight = 0;
44506
44507         if(this.title){
44508             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44509             this.assetHeight += this.header.getHeight();
44510         }
44511         this.innerLists = [];
44512         this.views = [];
44513         this.stores = [];
44514         for (var i =0 ; i < this.maxColumns; i++) {
44515             this.onRenderList( cls, i);
44516         }
44517         
44518         // always needs footer, as we are going to have an 'OK' button.
44519         this.footer = this.list.createChild({cls:cls+'-ft'});
44520         this.pageTb = new Roo.Toolbar(this.footer);  
44521         var _this = this;
44522         this.pageTb.add(  {
44523             
44524             text: 'Done',
44525             handler: function()
44526             {
44527                 _this.collapse();
44528             }
44529         });
44530         
44531         if ( this.allowBlank && !this.disableClear) {
44532             
44533             this.pageTb.add(new Roo.Toolbar.Fill(), {
44534                 cls: 'x-btn-icon x-btn-clear',
44535                 text: '&#160;',
44536                 handler: function()
44537                 {
44538                     _this.collapse();
44539                     _this.clearValue();
44540                     _this.onSelect(false, -1);
44541                 }
44542             });
44543         }
44544         if (this.footer) {
44545             this.assetHeight += this.footer.getHeight();
44546         }
44547         
44548     },
44549     onRenderList : function (  cls, i)
44550     {
44551         
44552         var lw = Math.floor(
44553                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44554         );
44555         
44556         this.list.setWidth(lw); // default to '1'
44557
44558         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44559         //il.on('mouseover', this.onViewOver, this, { list:  i });
44560         //il.on('mousemove', this.onViewMove, this, { list:  i });
44561         il.setWidth(lw);
44562         il.setStyle({ 'overflow-x' : 'hidden'});
44563
44564         if(!this.tpl){
44565             this.tpl = new Roo.Template({
44566                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44567                 isEmpty: function (value, allValues) {
44568                     //Roo.log(value);
44569                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44570                     return dl ? 'has-children' : 'no-children'
44571                 }
44572             });
44573         }
44574         
44575         var store  = this.store;
44576         if (i > 0) {
44577             store  = new Roo.data.SimpleStore({
44578                 //fields : this.store.reader.meta.fields,
44579                 reader : this.store.reader,
44580                 data : [ ]
44581             });
44582         }
44583         this.stores[i]  = store;
44584                   
44585         var view = this.views[i] = new Roo.View(
44586             il,
44587             this.tpl,
44588             {
44589                 singleSelect:true,
44590                 store: store,
44591                 selectedClass: this.selectedClass
44592             }
44593         );
44594         view.getEl().setWidth(lw);
44595         view.getEl().setStyle({
44596             position: i < 1 ? 'relative' : 'absolute',
44597             top: 0,
44598             left: (i * lw ) + 'px',
44599             display : i > 0 ? 'none' : 'block'
44600         });
44601         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44602         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44603         //view.on('click', this.onViewClick, this, { list : i });
44604
44605         store.on('beforeload', this.onBeforeLoad, this);
44606         store.on('load',  this.onLoad, this, { list  : i});
44607         store.on('loadexception', this.onLoadException, this);
44608
44609         // hide the other vies..
44610         
44611         
44612         
44613     },
44614       
44615     restrictHeight : function()
44616     {
44617         var mh = 0;
44618         Roo.each(this.innerLists, function(il,i) {
44619             var el = this.views[i].getEl();
44620             el.dom.style.height = '';
44621             var inner = el.dom;
44622             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44623             // only adjust heights on other ones..
44624             mh = Math.max(h, mh);
44625             if (i < 1) {
44626                 
44627                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44628                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44629                
44630             }
44631             
44632             
44633         }, this);
44634         
44635         this.list.beginUpdate();
44636         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44637         this.list.alignTo(this.el, this.listAlign);
44638         this.list.endUpdate();
44639         
44640     },
44641      
44642     
44643     // -- store handlers..
44644     // private
44645     onBeforeLoad : function()
44646     {
44647         if(!this.hasFocus){
44648             return;
44649         }
44650         this.innerLists[0].update(this.loadingText ?
44651                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44652         this.restrictHeight();
44653         this.selectedIndex = -1;
44654     },
44655     // private
44656     onLoad : function(a,b,c,d)
44657     {
44658         if (!this.loadingChildren) {
44659             // then we are loading the top level. - hide the children
44660             for (var i = 1;i < this.views.length; i++) {
44661                 this.views[i].getEl().setStyle({ display : 'none' });
44662             }
44663             var lw = Math.floor(
44664                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44665             );
44666         
44667              this.list.setWidth(lw); // default to '1'
44668
44669             
44670         }
44671         if(!this.hasFocus){
44672             return;
44673         }
44674         
44675         if(this.store.getCount() > 0) {
44676             this.expand();
44677             this.restrictHeight();   
44678         } else {
44679             this.onEmptyResults();
44680         }
44681         
44682         if (!this.loadingChildren) {
44683             this.selectActive();
44684         }
44685         /*
44686         this.stores[1].loadData([]);
44687         this.stores[2].loadData([]);
44688         this.views
44689         */    
44690     
44691         //this.el.focus();
44692     },
44693     
44694     
44695     // private
44696     onLoadException : function()
44697     {
44698         this.collapse();
44699         Roo.log(this.store.reader.jsonData);
44700         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44701             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44702         }
44703         
44704         
44705     },
44706     // no cleaning of leading spaces on blur here.
44707     cleanLeadingSpace : function(e) { },
44708     
44709
44710     onSelectChange : function (view, sels, opts )
44711     {
44712         var ix = view.getSelectedIndexes();
44713          
44714         if (opts.list > this.maxColumns - 2) {
44715             if (view.store.getCount()<  1) {
44716                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44717
44718             } else  {
44719                 if (ix.length) {
44720                     // used to clear ?? but if we are loading unselected 
44721                     this.setFromData(view.store.getAt(ix[0]).data);
44722                 }
44723                 
44724             }
44725             
44726             return;
44727         }
44728         
44729         if (!ix.length) {
44730             // this get's fired when trigger opens..
44731            // this.setFromData({});
44732             var str = this.stores[opts.list+1];
44733             str.data.clear(); // removeall wihtout the fire events..
44734             return;
44735         }
44736         
44737         var rec = view.store.getAt(ix[0]);
44738          
44739         this.setFromData(rec.data);
44740         this.fireEvent('select', this, rec, ix[0]);
44741         
44742         var lw = Math.floor(
44743              (
44744                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44745              ) / this.maxColumns
44746         );
44747         this.loadingChildren = true;
44748         this.stores[opts.list+1].loadDataFromChildren( rec );
44749         this.loadingChildren = false;
44750         var dl = this.stores[opts.list+1]. getTotalCount();
44751         
44752         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44753         
44754         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44755         for (var i = opts.list+2; i < this.views.length;i++) {
44756             this.views[i].getEl().setStyle({ display : 'none' });
44757         }
44758         
44759         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44760         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44761         
44762         if (this.isLoading) {
44763            // this.selectActive(opts.list);
44764         }
44765          
44766     },
44767     
44768     
44769     
44770     
44771     onDoubleClick : function()
44772     {
44773         this.collapse(); //??
44774     },
44775     
44776      
44777     
44778     
44779     
44780     // private
44781     recordToStack : function(store, prop, value, stack)
44782     {
44783         var cstore = new Roo.data.SimpleStore({
44784             //fields : this.store.reader.meta.fields, // we need array reader.. for
44785             reader : this.store.reader,
44786             data : [ ]
44787         });
44788         var _this = this;
44789         var record  = false;
44790         var srec = false;
44791         if(store.getCount() < 1){
44792             return false;
44793         }
44794         store.each(function(r){
44795             if(r.data[prop] == value){
44796                 record = r;
44797             srec = r;
44798                 return false;
44799             }
44800             if (r.data.cn && r.data.cn.length) {
44801                 cstore.loadDataFromChildren( r);
44802                 var cret = _this.recordToStack(cstore, prop, value, stack);
44803                 if (cret !== false) {
44804                     record = cret;
44805                     srec = r;
44806                     return false;
44807                 }
44808             }
44809              
44810             return true;
44811         });
44812         if (record == false) {
44813             return false
44814         }
44815         stack.unshift(srec);
44816         return record;
44817     },
44818     
44819     /*
44820      * find the stack of stores that match our value.
44821      *
44822      * 
44823      */
44824     
44825     selectActive : function ()
44826     {
44827         // if store is not loaded, then we will need to wait for that to happen first.
44828         var stack = [];
44829         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44830         for (var i = 0; i < stack.length; i++ ) {
44831             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44832         }
44833         
44834     }
44835         
44836          
44837     
44838     
44839     
44840     
44841 });/*
44842  * Based on:
44843  * Ext JS Library 1.1.1
44844  * Copyright(c) 2006-2007, Ext JS, LLC.
44845  *
44846  * Originally Released Under LGPL - original licence link has changed is not relivant.
44847  *
44848  * Fork - LGPL
44849  * <script type="text/javascript">
44850  */
44851 /**
44852  * @class Roo.form.Checkbox
44853  * @extends Roo.form.Field
44854  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44855  * @constructor
44856  * Creates a new Checkbox
44857  * @param {Object} config Configuration options
44858  */
44859 Roo.form.Checkbox = function(config){
44860     Roo.form.Checkbox.superclass.constructor.call(this, config);
44861     this.addEvents({
44862         /**
44863          * @event check
44864          * Fires when the checkbox is checked or unchecked.
44865              * @param {Roo.form.Checkbox} this This checkbox
44866              * @param {Boolean} checked The new checked value
44867              */
44868         check : true
44869     });
44870 };
44871
44872 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44873     /**
44874      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44875      */
44876     focusClass : undefined,
44877     /**
44878      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44879      */
44880     fieldClass: "x-form-field",
44881     /**
44882      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44883      */
44884     checked: false,
44885     /**
44886      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44887      * {tag: "input", type: "checkbox", autocomplete: "off"})
44888      */
44889     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44890     /**
44891      * @cfg {String} boxLabel The text that appears beside the checkbox
44892      */
44893     boxLabel : "",
44894     /**
44895      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44896      */  
44897     inputValue : '1',
44898     /**
44899      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44900      */
44901      valueOff: '0', // value when not checked..
44902
44903     actionMode : 'viewEl', 
44904     //
44905     // private
44906     itemCls : 'x-menu-check-item x-form-item',
44907     groupClass : 'x-menu-group-item',
44908     inputType : 'hidden',
44909     
44910     
44911     inSetChecked: false, // check that we are not calling self...
44912     
44913     inputElement: false, // real input element?
44914     basedOn: false, // ????
44915     
44916     isFormField: true, // not sure where this is needed!!!!
44917
44918     onResize : function(){
44919         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44920         if(!this.boxLabel){
44921             this.el.alignTo(this.wrap, 'c-c');
44922         }
44923     },
44924
44925     initEvents : function(){
44926         Roo.form.Checkbox.superclass.initEvents.call(this);
44927         this.el.on("click", this.onClick,  this);
44928         this.el.on("change", this.onClick,  this);
44929     },
44930
44931
44932     getResizeEl : function(){
44933         return this.wrap;
44934     },
44935
44936     getPositionEl : function(){
44937         return this.wrap;
44938     },
44939
44940     // private
44941     onRender : function(ct, position){
44942         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44943         /*
44944         if(this.inputValue !== undefined){
44945             this.el.dom.value = this.inputValue;
44946         }
44947         */
44948         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44949         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44950         var viewEl = this.wrap.createChild({ 
44951             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44952         this.viewEl = viewEl;   
44953         this.wrap.on('click', this.onClick,  this); 
44954         
44955         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44956         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44957         
44958         
44959         
44960         if(this.boxLabel){
44961             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44962         //    viewEl.on('click', this.onClick,  this); 
44963         }
44964         //if(this.checked){
44965             this.setChecked(this.checked);
44966         //}else{
44967             //this.checked = this.el.dom;
44968         //}
44969
44970     },
44971
44972     // private
44973     initValue : Roo.emptyFn,
44974
44975     /**
44976      * Returns the checked state of the checkbox.
44977      * @return {Boolean} True if checked, else false
44978      */
44979     getValue : function(){
44980         if(this.el){
44981             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44982         }
44983         return this.valueOff;
44984         
44985     },
44986
44987         // private
44988     onClick : function(){ 
44989         if (this.disabled) {
44990             return;
44991         }
44992         this.setChecked(!this.checked);
44993
44994         //if(this.el.dom.checked != this.checked){
44995         //    this.setValue(this.el.dom.checked);
44996        // }
44997     },
44998
44999     /**
45000      * Sets the checked state of the checkbox.
45001      * On is always based on a string comparison between inputValue and the param.
45002      * @param {Boolean/String} value - the value to set 
45003      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45004      */
45005     setValue : function(v,suppressEvent){
45006         
45007         
45008         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
45009         //if(this.el && this.el.dom){
45010         //    this.el.dom.checked = this.checked;
45011         //    this.el.dom.defaultChecked = this.checked;
45012         //}
45013         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
45014         //this.fireEvent("check", this, this.checked);
45015     },
45016     // private..
45017     setChecked : function(state,suppressEvent)
45018     {
45019         if (this.inSetChecked) {
45020             this.checked = state;
45021             return;
45022         }
45023         
45024     
45025         if(this.wrap){
45026             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
45027         }
45028         this.checked = state;
45029         if(suppressEvent !== true){
45030             this.fireEvent('check', this, state);
45031         }
45032         this.inSetChecked = true;
45033                  
45034                 this.el.dom.value = state ? this.inputValue : this.valueOff;
45035                  
45036         this.inSetChecked = false;
45037         
45038     },
45039     // handle setting of hidden value by some other method!!?!?
45040     setFromHidden: function()
45041     {
45042         if(!this.el){
45043             return;
45044         }
45045         //console.log("SET FROM HIDDEN");
45046         //alert('setFrom hidden');
45047         this.setValue(this.el.dom.value);
45048     },
45049     
45050     onDestroy : function()
45051     {
45052         if(this.viewEl){
45053             Roo.get(this.viewEl).remove();
45054         }
45055          
45056         Roo.form.Checkbox.superclass.onDestroy.call(this);
45057     },
45058     
45059     setBoxLabel : function(str)
45060     {
45061         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
45062     }
45063
45064 });/*
45065  * Based on:
45066  * Ext JS Library 1.1.1
45067  * Copyright(c) 2006-2007, Ext JS, LLC.
45068  *
45069  * Originally Released Under LGPL - original licence link has changed is not relivant.
45070  *
45071  * Fork - LGPL
45072  * <script type="text/javascript">
45073  */
45074  
45075 /**
45076  * @class Roo.form.Radio
45077  * @extends Roo.form.Checkbox
45078  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
45079  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
45080  * @constructor
45081  * Creates a new Radio
45082  * @param {Object} config Configuration options
45083  */
45084 Roo.form.Radio = function(){
45085     Roo.form.Radio.superclass.constructor.apply(this, arguments);
45086 };
45087 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
45088     inputType: 'radio',
45089
45090     /**
45091      * If this radio is part of a group, it will return the selected value
45092      * @return {String}
45093      */
45094     getGroupValue : function(){
45095         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
45096     },
45097     
45098     
45099     onRender : function(ct, position){
45100         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
45101         
45102         if(this.inputValue !== undefined){
45103             this.el.dom.value = this.inputValue;
45104         }
45105          
45106         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
45107         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
45108         //var viewEl = this.wrap.createChild({ 
45109         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
45110         //this.viewEl = viewEl;   
45111         //this.wrap.on('click', this.onClick,  this); 
45112         
45113         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
45114         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
45115         
45116         
45117         
45118         if(this.boxLabel){
45119             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
45120         //    viewEl.on('click', this.onClick,  this); 
45121         }
45122          if(this.checked){
45123             this.el.dom.checked =   'checked' ;
45124         }
45125          
45126     },
45127     /**
45128      * Sets the checked state of the checkbox.
45129      * On is always based on a string comparison between inputValue and the param.
45130      * @param {Boolean/String} value - the value to set 
45131      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
45132      */
45133     setValue : function(v,suppressEvent){
45134         
45135         
45136         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
45137         //if(this.el && this.el.dom){
45138         //    this.el.dom.checked = this.checked;
45139         //    this.el.dom.defaultChecked = this.checked;
45140         //}
45141         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
45142         
45143         this.el.dom.form[this.name].value = v;
45144      
45145         //this.fireEvent("check", this, this.checked);
45146     },
45147     // private..
45148     setChecked : function(state,suppressEvent)
45149     {
45150          
45151         if(this.wrap){
45152             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
45153         }
45154         this.checked = state;
45155         if(suppressEvent !== true){
45156             this.fireEvent('check', this, state);
45157         }
45158                  
45159                   
45160        
45161         
45162     },
45163     reset : function(){
45164         // this.setValue(this.resetValue);
45165         //this.originalValue = this.getValue();
45166         this.clearInvalid();
45167     } 
45168     
45169 });Roo.rtf = {}; // namespace
45170 Roo.rtf.Hex = function(hex)
45171 {
45172     this.hexstr = hex;
45173 };
45174 Roo.rtf.Paragraph = function(opts)
45175 {
45176     this.content = []; ///??? is that used?
45177 };Roo.rtf.Span = function(opts)
45178 {
45179     this.value = opts.value;
45180 };
45181
45182 Roo.rtf.Group = function(parent)
45183 {
45184     // we dont want to acutally store parent - it will make debug a nightmare..
45185     this.content = [];
45186     this.cn  = [];
45187      
45188        
45189     
45190 };
45191
45192 Roo.rtf.Group.prototype = {
45193     ignorable : false,
45194     content: false,
45195     cn: false,
45196     addContent : function(node) {
45197         // could set styles...
45198         this.content.push(node);
45199     },
45200     addChild : function(cn)
45201     {
45202         this.cn.push(cn);
45203     },
45204     // only for images really...
45205     toDataURL : function()
45206     {
45207         var mimetype = false;
45208         switch(true) {
45209             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
45210                 mimetype = "image/png";
45211                 break;
45212              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
45213                 mimetype = "image/jpeg";
45214                 break;
45215             default :
45216                 return 'about:blank'; // ?? error?
45217         }
45218         
45219         
45220         var hexstring = this.content[this.content.length-1].value;
45221         
45222         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
45223             return String.fromCharCode(parseInt(a, 16));
45224         }).join(""));
45225     }
45226     
45227 };
45228 // this looks like it's normally the {rtf{ .... }}
45229 Roo.rtf.Document = function()
45230 {
45231     // we dont want to acutally store parent - it will make debug a nightmare..
45232     this.rtlch  = [];
45233     this.content = [];
45234     this.cn = [];
45235     
45236 };
45237 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
45238     addChild : function(cn)
45239     {
45240         this.cn.push(cn);
45241         switch(cn.type) {
45242             case 'rtlch': // most content seems to be inside this??
45243             case 'listtext':
45244             case 'shpinst':
45245                 this.rtlch.push(cn);
45246                 return;
45247             default:
45248                 this[cn.type] = cn;
45249         }
45250         
45251     },
45252     
45253     getElementsByType : function(type)
45254     {
45255         var ret =  [];
45256         this._getElementsByType(type, ret, this.cn, 'rtf');
45257         return ret;
45258     },
45259     _getElementsByType : function (type, ret, search_array, path)
45260     {
45261         search_array.forEach(function(n,i) {
45262             if (n.type == type) {
45263                 n.path = path + '/' + n.type + ':' + i;
45264                 ret.push(n);
45265             }
45266             if (n.cn.length > 0) {
45267                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
45268             }
45269         },this);
45270     }
45271     
45272 });
45273  
45274 Roo.rtf.Ctrl = function(opts)
45275 {
45276     this.value = opts.value;
45277     this.param = opts.param;
45278 };
45279 /**
45280  *
45281  *
45282  * based on this https://github.com/iarna/rtf-parser
45283  * it's really only designed to extract pict from pasted RTF 
45284  *
45285  * usage:
45286  *
45287  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45288  *  
45289  *
45290  */
45291
45292  
45293
45294
45295
45296 Roo.rtf.Parser = function(text) {
45297     //super({objectMode: true})
45298     this.text = '';
45299     this.parserState = this.parseText;
45300     
45301     // these are for interpeter...
45302     this.doc = {};
45303     ///this.parserState = this.parseTop
45304     this.groupStack = [];
45305     this.hexStore = [];
45306     this.doc = false;
45307     
45308     this.groups = []; // where we put the return.
45309     
45310     for (var ii = 0; ii < text.length; ++ii) {
45311         ++this.cpos;
45312         
45313         if (text[ii] === '\n') {
45314             ++this.row;
45315             this.col = 1;
45316         } else {
45317             ++this.col;
45318         }
45319         this.parserState(text[ii]);
45320     }
45321     
45322     
45323     
45324 };
45325 Roo.rtf.Parser.prototype = {
45326     text : '', // string being parsed..
45327     controlWord : '',
45328     controlWordParam :  '',
45329     hexChar : '',
45330     doc : false,
45331     group: false,
45332     groupStack : false,
45333     hexStore : false,
45334     
45335     
45336     cpos : 0, 
45337     row : 1, // reportin?
45338     col : 1, //
45339
45340      
45341     push : function (el)
45342     {
45343         var m = 'cmd'+ el.type;
45344         if (typeof(this[m]) == 'undefined') {
45345             Roo.log('invalid cmd:' + el.type);
45346             return;
45347         }
45348         this[m](el);
45349         //Roo.log(el);
45350     },
45351     flushHexStore : function()
45352     {
45353         if (this.hexStore.length < 1) {
45354             return;
45355         }
45356         var hexstr = this.hexStore.map(
45357             function(cmd) {
45358                 return cmd.value;
45359         }).join('');
45360         
45361         this.group.addContent( new Roo.rtf.Hex( hexstr ));
45362               
45363             
45364         this.hexStore.splice(0)
45365         
45366     },
45367     
45368     cmdgroupstart : function()
45369     {
45370         this.flushHexStore();
45371         if (this.group) {
45372             this.groupStack.push(this.group);
45373         }
45374          // parent..
45375         if (this.doc === false) {
45376             this.group = this.doc = new Roo.rtf.Document();
45377             return;
45378             
45379         }
45380         this.group = new Roo.rtf.Group(this.group);
45381     },
45382     cmdignorable : function()
45383     {
45384         this.flushHexStore();
45385         this.group.ignorable = true;
45386     },
45387     cmdendparagraph : function()
45388     {
45389         this.flushHexStore();
45390         this.group.addContent(new Roo.rtf.Paragraph());
45391     },
45392     cmdgroupend : function ()
45393     {
45394         this.flushHexStore();
45395         var endingGroup = this.group;
45396         
45397         
45398         this.group = this.groupStack.pop();
45399         if (this.group) {
45400             this.group.addChild(endingGroup);
45401         }
45402         
45403         
45404         
45405         var doc = this.group || this.doc;
45406         //if (endingGroup instanceof FontTable) {
45407         //  doc.fonts = endingGroup.table
45408         //} else if (endingGroup instanceof ColorTable) {
45409         //  doc.colors = endingGroup.table
45410         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45411         if (endingGroup.ignorable === false) {
45412             //code
45413             this.groups.push(endingGroup);
45414            // Roo.log( endingGroup );
45415         }
45416             //Roo.each(endingGroup.content, function(item)) {
45417             //    doc.addContent(item);
45418             //}
45419             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45420         //}
45421     },
45422     cmdtext : function (cmd)
45423     {
45424         this.flushHexStore();
45425         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45426             //this.group = this.doc
45427             return;  // we really don't care about stray text...
45428         }
45429         this.group.addContent(new Roo.rtf.Span(cmd));
45430     },
45431     cmdcontrolword : function (cmd)
45432     {
45433         this.flushHexStore();
45434         if (!this.group.type) {
45435             this.group.type = cmd.value;
45436             return;
45437         }
45438         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45439         // we actually don't care about ctrl words...
45440         return ;
45441         /*
45442         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45443         if (this[method]) {
45444             this[method](cmd.param)
45445         } else {
45446             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45447         }
45448         */
45449     },
45450     cmdhexchar : function(cmd) {
45451         this.hexStore.push(cmd);
45452     },
45453     cmderror : function(cmd) {
45454         throw cmd.value;
45455     },
45456     
45457     /*
45458       _flush (done) {
45459         if (this.text !== '\u0000') this.emitText()
45460         done()
45461       }
45462       */
45463       
45464       
45465     parseText : function(c)
45466     {
45467         if (c === '\\') {
45468             this.parserState = this.parseEscapes;
45469         } else if (c === '{') {
45470             this.emitStartGroup();
45471         } else if (c === '}') {
45472             this.emitEndGroup();
45473         } else if (c === '\x0A' || c === '\x0D') {
45474             // cr/lf are noise chars
45475         } else {
45476             this.text += c;
45477         }
45478     },
45479     
45480     parseEscapes: function (c)
45481     {
45482         if (c === '\\' || c === '{' || c === '}') {
45483             this.text += c;
45484             this.parserState = this.parseText;
45485         } else {
45486             this.parserState = this.parseControlSymbol;
45487             this.parseControlSymbol(c);
45488         }
45489     },
45490     parseControlSymbol: function(c)
45491     {
45492         if (c === '~') {
45493             this.text += '\u00a0'; // nbsp
45494             this.parserState = this.parseText
45495         } else if (c === '-') {
45496              this.text += '\u00ad'; // soft hyphen
45497         } else if (c === '_') {
45498             this.text += '\u2011'; // non-breaking hyphen
45499         } else if (c === '*') {
45500             this.emitIgnorable();
45501             this.parserState = this.parseText;
45502         } else if (c === "'") {
45503             this.parserState = this.parseHexChar;
45504         } else if (c === '|') { // formula cacter
45505             this.emitFormula();
45506             this.parserState = this.parseText;
45507         } else if (c === ':') { // subentry in an index entry
45508             this.emitIndexSubEntry();
45509             this.parserState = this.parseText;
45510         } else if (c === '\x0a') {
45511             this.emitEndParagraph();
45512             this.parserState = this.parseText;
45513         } else if (c === '\x0d') {
45514             this.emitEndParagraph();
45515             this.parserState = this.parseText;
45516         } else {
45517             this.parserState = this.parseControlWord;
45518             this.parseControlWord(c);
45519         }
45520     },
45521     parseHexChar: function (c)
45522     {
45523         if (/^[A-Fa-f0-9]$/.test(c)) {
45524             this.hexChar += c;
45525             if (this.hexChar.length >= 2) {
45526               this.emitHexChar();
45527               this.parserState = this.parseText;
45528             }
45529             return;
45530         }
45531         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45532         this.parserState = this.parseText;
45533         
45534     },
45535     parseControlWord : function(c)
45536     {
45537         if (c === ' ') {
45538             this.emitControlWord();
45539             this.parserState = this.parseText;
45540         } else if (/^[-\d]$/.test(c)) {
45541             this.parserState = this.parseControlWordParam;
45542             this.controlWordParam += c;
45543         } else if (/^[A-Za-z]$/.test(c)) {
45544           this.controlWord += c;
45545         } else {
45546           this.emitControlWord();
45547           this.parserState = this.parseText;
45548           this.parseText(c);
45549         }
45550     },
45551     parseControlWordParam : function (c) {
45552         if (/^\d$/.test(c)) {
45553           this.controlWordParam += c;
45554         } else if (c === ' ') {
45555           this.emitControlWord();
45556           this.parserState = this.parseText;
45557         } else {
45558           this.emitControlWord();
45559           this.parserState = this.parseText;
45560           this.parseText(c);
45561         }
45562     },
45563     
45564     
45565     
45566     
45567     emitText : function () {
45568         if (this.text === '') {
45569             return;
45570         }
45571         this.push({
45572             type: 'text',
45573             value: this.text,
45574             pos: this.cpos,
45575             row: this.row,
45576             col: this.col
45577         });
45578         this.text = ''
45579     },
45580     emitControlWord : function ()
45581     {
45582         this.emitText();
45583         if (this.controlWord === '') {
45584             // do we want to track this - it seems just to cause problems.
45585             //this.emitError('empty control word');
45586         } else {
45587             this.push({
45588                   type: 'controlword',
45589                   value: this.controlWord,
45590                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45591                   pos: this.cpos,
45592                   row: this.row,
45593                   col: this.col
45594             });
45595         }
45596         this.controlWord = '';
45597         this.controlWordParam = '';
45598     },
45599     emitStartGroup : function ()
45600     {
45601         this.emitText();
45602         this.push({
45603             type: 'groupstart',
45604             pos: this.cpos,
45605             row: this.row,
45606             col: this.col
45607         });
45608     },
45609     emitEndGroup : function ()
45610     {
45611         this.emitText();
45612         this.push({
45613             type: 'groupend',
45614             pos: this.cpos,
45615             row: this.row,
45616             col: this.col
45617         });
45618     },
45619     emitIgnorable : function ()
45620     {
45621         this.emitText();
45622         this.push({
45623             type: 'ignorable',
45624             pos: this.cpos,
45625             row: this.row,
45626             col: this.col
45627         });
45628     },
45629     emitHexChar : function ()
45630     {
45631         this.emitText();
45632         this.push({
45633             type: 'hexchar',
45634             value: this.hexChar,
45635             pos: this.cpos,
45636             row: this.row,
45637             col: this.col
45638         });
45639         this.hexChar = ''
45640     },
45641     emitError : function (message)
45642     {
45643       this.emitText();
45644       this.push({
45645             type: 'error',
45646             value: message,
45647             row: this.row,
45648             col: this.col,
45649             char: this.cpos //,
45650             //stack: new Error().stack
45651         });
45652     },
45653     emitEndParagraph : function () {
45654         this.emitText();
45655         this.push({
45656             type: 'endparagraph',
45657             pos: this.cpos,
45658             row: this.row,
45659             col: this.col
45660         });
45661     }
45662      
45663 } ;
45664 Roo.htmleditor = {};
45665  
45666 /**
45667  * @class Roo.htmleditor.Filter
45668  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45669  * @cfg {DomElement} node The node to iterate and filter
45670  * @cfg {boolean|String|Array} tag Tags to replace 
45671  * @constructor
45672  * Create a new Filter.
45673  * @param {Object} config Configuration options
45674  */
45675
45676
45677
45678 Roo.htmleditor.Filter = function(cfg) {
45679     Roo.apply(this.cfg);
45680     // this does not actually call walk as it's really just a abstract class
45681 }
45682
45683
45684 Roo.htmleditor.Filter.prototype = {
45685     
45686     node: false,
45687     
45688     tag: false,
45689
45690     // overrride to do replace comments.
45691     replaceComment : false,
45692     
45693     // overrride to do replace or do stuff with tags..
45694     replaceTag : false,
45695     
45696     walk : function(dom)
45697     {
45698         Roo.each( Array.from(dom.childNodes), function( e ) {
45699             switch(true) {
45700                 
45701                 case e.nodeType == 8 &&  this.replaceComment  !== false: // comment
45702                     this.replaceComment(e);
45703                     return;
45704                 
45705                 case e.nodeType != 1: //not a node.
45706                     return;
45707                 
45708                 case this.tag === true: // everything
45709                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
45710                 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
45711                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45712                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45713                     if (this.replaceTag && false === this.replaceTag(e)) {
45714                         return;
45715                     }
45716                     if (e.hasChildNodes()) {
45717                         this.walk(e);
45718                     }
45719                     return;
45720                 
45721                 default:    // tags .. that do not match.
45722                     if (e.hasChildNodes()) {
45723                         this.walk(e);
45724                     }
45725             }
45726             
45727         }, this);
45728         
45729     },
45730     
45731     
45732     removeNodeKeepChildren : function( node)
45733     {
45734     
45735         ar = Array.from(node.childNodes);
45736         for (var i = 0; i < ar.length; i++) {
45737          
45738             node.removeChild(ar[i]);
45739             // what if we need to walk these???
45740             node.parentNode.insertBefore(ar[i], node);
45741            
45742         }
45743         node.parentNode.removeChild(node);
45744     }
45745 }; 
45746
45747 /**
45748  * @class Roo.htmleditor.FilterAttributes
45749  * clean attributes and  styles including http:// etc.. in attribute
45750  * @constructor
45751 * Run a new Attribute Filter
45752 * @param {Object} config Configuration options
45753  */
45754 Roo.htmleditor.FilterAttributes = function(cfg)
45755 {
45756     Roo.apply(this, cfg);
45757     this.attrib_black = this.attrib_black || [];
45758     this.attrib_white = this.attrib_white || [];
45759
45760     this.attrib_clean = this.attrib_clean || [];
45761     this.style_white = this.style_white || [];
45762     this.style_black = this.style_black || [];
45763     this.walk(cfg.node);
45764 }
45765
45766 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45767 {
45768     tag: true, // all tags
45769     
45770     attrib_black : false, // array
45771     attrib_clean : false,
45772     attrib_white : false,
45773
45774     style_white : false,
45775     style_black : false,
45776      
45777      
45778     replaceTag : function(node)
45779     {
45780         if (!node.attributes || !node.attributes.length) {
45781             return true;
45782         }
45783         
45784         for (var i = node.attributes.length-1; i > -1 ; i--) {
45785             var a = node.attributes[i];
45786             //console.log(a);
45787             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45788                 node.removeAttribute(a.name);
45789                 continue;
45790             }
45791             
45792             
45793             
45794             if (a.name.toLowerCase().substr(0,2)=='on')  {
45795                 node.removeAttribute(a.name);
45796                 continue;
45797             }
45798             
45799             
45800             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45801                 node.removeAttribute(a.name);
45802                 continue;
45803             }
45804             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45805                 this.cleanAttr(node,a.name,a.value); // fixme..
45806                 continue;
45807             }
45808             if (a.name == 'style') {
45809                 this.cleanStyle(node,a.name,a.value);
45810                 continue;
45811             }
45812             /// clean up MS crap..
45813             // tecnically this should be a list of valid class'es..
45814             
45815             
45816             if (a.name == 'class') {
45817                 if (a.value.match(/^Mso/)) {
45818                     node.removeAttribute('class');
45819                 }
45820                 
45821                 if (a.value.match(/^body$/)) {
45822                     node.removeAttribute('class');
45823                 }
45824                 continue;
45825             }
45826             
45827             
45828             // style cleanup!?
45829             // class cleanup?
45830             
45831         }
45832         return true; // clean children
45833     },
45834         
45835     cleanAttr: function(node, n,v)
45836     {
45837         
45838         if (v.match(/^\./) || v.match(/^\//)) {
45839             return;
45840         }
45841         if (v.match(/^(http|https):\/\//)
45842             || v.match(/^mailto:/) 
45843             || v.match(/^ftp:/)
45844             || v.match(/^data:/)
45845             ) {
45846             return;
45847         }
45848         if (v.match(/^#/)) {
45849             return;
45850         }
45851         if (v.match(/^\{/)) { // allow template editing.
45852             return;
45853         }
45854 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45855         node.removeAttribute(n);
45856         
45857     },
45858     cleanStyle : function(node,  n,v)
45859     {
45860         if (v.match(/expression/)) { //XSS?? should we even bother..
45861             node.removeAttribute(n);
45862             return;
45863         }
45864         
45865         var parts = v.split(/;/);
45866         var clean = [];
45867         
45868         Roo.each(parts, function(p) {
45869             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45870             if (!p.length) {
45871                 return true;
45872             }
45873             var l = p.split(':').shift().replace(/\s+/g,'');
45874             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45875             
45876             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45877                 return true;
45878             }
45879             //Roo.log()
45880             // only allow 'c whitelisted system attributes'
45881             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45882                 return true;
45883             }
45884             
45885             
45886             clean.push(p);
45887             return true;
45888         },this);
45889         if (clean.length) { 
45890             node.setAttribute(n, clean.join(';'));
45891         } else {
45892             node.removeAttribute(n);
45893         }
45894         
45895     }
45896         
45897         
45898         
45899     
45900 });/**
45901  * @class Roo.htmleditor.FilterBlack
45902  * remove blacklisted elements.
45903  * @constructor
45904  * Run a new Blacklisted Filter
45905  * @param {Object} config Configuration options
45906  */
45907
45908 Roo.htmleditor.FilterBlack = function(cfg)
45909 {
45910     Roo.apply(this, cfg);
45911     this.walk(cfg.node);
45912 }
45913
45914 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45915 {
45916     tag : true, // all elements.
45917    
45918     replaceTag : function(n)
45919     {
45920         n.parentNode.removeChild(n);
45921     }
45922 });
45923 /**
45924  * @class Roo.htmleditor.FilterComment
45925  * remove comments.
45926  * @constructor
45927 * Run a new Comments Filter
45928 * @param {Object} config Configuration options
45929  */
45930 Roo.htmleditor.FilterComment = function(cfg)
45931 {
45932     this.walk(cfg.node);
45933 }
45934
45935 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45936 {
45937   
45938     replaceComment : function(n)
45939     {
45940         n.parentNode.removeChild(n);
45941     }
45942 });/**
45943  * @class Roo.htmleditor.FilterKeepChildren
45944  * remove tags but keep children
45945  * @constructor
45946  * Run a new Keep Children Filter
45947  * @param {Object} config Configuration options
45948  */
45949
45950 Roo.htmleditor.FilterKeepChildren = function(cfg)
45951 {
45952     Roo.apply(this, cfg);
45953     if (this.tag === false) {
45954         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45955     }
45956     // hacky?
45957     if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
45958         this.cleanNamespace = true;
45959     }
45960         
45961     this.walk(cfg.node);
45962 }
45963
45964 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45965 {
45966     cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
45967   
45968     replaceTag : function(node)
45969     {
45970         // walk children...
45971         //Roo.log(node.tagName);
45972         var ar = Array.from(node.childNodes);
45973         //remove first..
45974         
45975         for (var i = 0; i < ar.length; i++) {
45976             var e = ar[i];
45977             if (e.nodeType == 1) {
45978                 if (
45979                     (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
45980                     || // array and it matches
45981                     (typeof(this.tag) == 'string' && this.tag == e.tagName)
45982                     ||
45983                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
45984                     ||
45985                     (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
45986                 ) {
45987                     this.replaceTag(ar[i]); // child is blacklisted as well...
45988                     continue;
45989                 }
45990             }
45991         }  
45992         ar = Array.from(node.childNodes);
45993         for (var i = 0; i < ar.length; i++) {
45994          
45995             node.removeChild(ar[i]);
45996             // what if we need to walk these???
45997             node.parentNode.insertBefore(ar[i], node);
45998             if (this.tag !== false) {
45999                 this.walk(ar[i]);
46000                 
46001             }
46002         }
46003         //Roo.log("REMOVE:" + node.tagName);
46004         node.parentNode.removeChild(node);
46005         return false; // don't walk children
46006         
46007         
46008     }
46009 });/**
46010  * @class Roo.htmleditor.FilterParagraph
46011  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
46012  * like on 'push' to remove the <p> tags and replace them with line breaks.
46013  * @constructor
46014  * Run a new Paragraph Filter
46015  * @param {Object} config Configuration options
46016  */
46017
46018 Roo.htmleditor.FilterParagraph = function(cfg)
46019 {
46020     // no need to apply config.
46021     this.walk(cfg.node);
46022 }
46023
46024 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
46025 {
46026     
46027      
46028     tag : 'P',
46029     
46030      
46031     replaceTag : function(node)
46032     {
46033         
46034         if (node.childNodes.length == 1 &&
46035             node.childNodes[0].nodeType == 3 &&
46036             node.childNodes[0].textContent.trim().length < 1
46037             ) {
46038             // remove and replace with '<BR>';
46039             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
46040             return false; // no need to walk..
46041         }
46042         var ar = Array.from(node.childNodes);
46043         for (var i = 0; i < ar.length; i++) {
46044             node.removeChild(ar[i]);
46045             // what if we need to walk these???
46046             node.parentNode.insertBefore(ar[i], node);
46047         }
46048         // now what about this?
46049         // <p> &nbsp; </p>
46050         
46051         // double BR.
46052         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
46053         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
46054         node.parentNode.removeChild(node);
46055         
46056         return false;
46057
46058     }
46059     
46060 });/**
46061  * @class Roo.htmleditor.FilterSpan
46062  * filter span's with no attributes out..
46063  * @constructor
46064  * Run a new Span Filter
46065  * @param {Object} config Configuration options
46066  */
46067
46068 Roo.htmleditor.FilterSpan = function(cfg)
46069 {
46070     // no need to apply config.
46071     this.walk(cfg.node);
46072 }
46073
46074 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
46075 {
46076      
46077     tag : 'SPAN',
46078      
46079  
46080     replaceTag : function(node)
46081     {
46082         if (node.attributes && node.attributes.length > 0) {
46083             return true; // walk if there are any.
46084         }
46085         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
46086         return false;
46087      
46088     }
46089     
46090 });/**
46091  * @class Roo.htmleditor.FilterTableWidth
46092   try and remove table width data - as that frequently messes up other stuff.
46093  * 
46094  *      was cleanTableWidths.
46095  *
46096  * Quite often pasting from word etc.. results in tables with column and widths.
46097  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
46098  *
46099  * @constructor
46100  * Run a new Table Filter
46101  * @param {Object} config Configuration options
46102  */
46103
46104 Roo.htmleditor.FilterTableWidth = function(cfg)
46105 {
46106     // no need to apply config.
46107     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
46108     this.walk(cfg.node);
46109 }
46110
46111 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
46112 {
46113      
46114      
46115     
46116     replaceTag: function(node) {
46117         
46118         
46119       
46120         if (node.hasAttribute('width')) {
46121             node.removeAttribute('width');
46122         }
46123         
46124          
46125         if (node.hasAttribute("style")) {
46126             // pretty basic...
46127             
46128             var styles = node.getAttribute("style").split(";");
46129             var nstyle = [];
46130             Roo.each(styles, function(s) {
46131                 if (!s.match(/:/)) {
46132                     return;
46133                 }
46134                 var kv = s.split(":");
46135                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
46136                     return;
46137                 }
46138                 // what ever is left... we allow.
46139                 nstyle.push(s);
46140             });
46141             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46142             if (!nstyle.length) {
46143                 node.removeAttribute('style');
46144             }
46145         }
46146         
46147         return true; // continue doing children..
46148     }
46149 });/**
46150  * @class Roo.htmleditor.FilterWord
46151  * try and clean up all the mess that Word generates.
46152  * 
46153  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
46154  
46155  * @constructor
46156  * Run a new Span Filter
46157  * @param {Object} config Configuration options
46158  */
46159
46160 Roo.htmleditor.FilterWord = function(cfg)
46161 {
46162     // no need to apply config.
46163     this.replaceDocBullets(cfg.node);
46164     
46165     this.replaceAname(cfg.node);
46166     // this is disabled as the removal is done by other filters;
46167    // this.walk(cfg.node);
46168     this.replaceImageTable(cfg.node);
46169     
46170 }
46171
46172 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
46173 {
46174     tag: true,
46175      
46176     
46177     /**
46178      * Clean up MS wordisms...
46179      */
46180     replaceTag : function(node)
46181     {
46182          
46183         // no idea what this does - span with text, replaceds with just text.
46184         if(
46185                 node.nodeName == 'SPAN' &&
46186                 !node.hasAttributes() &&
46187                 node.childNodes.length == 1 &&
46188                 node.firstChild.nodeName == "#text"  
46189         ) {
46190             var textNode = node.firstChild;
46191             node.removeChild(textNode);
46192             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
46193                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
46194             }
46195             node.parentNode.insertBefore(textNode, node);
46196             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
46197                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
46198             }
46199             
46200             node.parentNode.removeChild(node);
46201             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
46202         }
46203         
46204    
46205         
46206         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
46207             node.parentNode.removeChild(node);
46208             return false; // dont do chidlren
46209         }
46210         //Roo.log(node.tagName);
46211         // remove - but keep children..
46212         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
46213             //Roo.log('-- removed');
46214             while (node.childNodes.length) {
46215                 var cn = node.childNodes[0];
46216                 node.removeChild(cn);
46217                 node.parentNode.insertBefore(cn, node);
46218                 // move node to parent - and clean it..
46219                 if (cn.nodeType == 1) {
46220                     this.replaceTag(cn);
46221                 }
46222                 
46223             }
46224             node.parentNode.removeChild(node);
46225             /// no need to iterate chidlren = it's got none..
46226             //this.iterateChildren(node, this.cleanWord);
46227             return false; // no need to iterate children.
46228         }
46229         // clean styles
46230         if (node.className.length) {
46231             
46232             var cn = node.className.split(/\W+/);
46233             var cna = [];
46234             Roo.each(cn, function(cls) {
46235                 if (cls.match(/Mso[a-zA-Z]+/)) {
46236                     return;
46237                 }
46238                 cna.push(cls);
46239             });
46240             node.className = cna.length ? cna.join(' ') : '';
46241             if (!cna.length) {
46242                 node.removeAttribute("class");
46243             }
46244         }
46245         
46246         if (node.hasAttribute("lang")) {
46247             node.removeAttribute("lang");
46248         }
46249         
46250         if (node.hasAttribute("style")) {
46251             
46252             var styles = node.getAttribute("style").split(";");
46253             var nstyle = [];
46254             Roo.each(styles, function(s) {
46255                 if (!s.match(/:/)) {
46256                     return;
46257                 }
46258                 var kv = s.split(":");
46259                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
46260                     return;
46261                 }
46262                 // what ever is left... we allow.
46263                 nstyle.push(s);
46264             });
46265             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
46266             if (!nstyle.length) {
46267                 node.removeAttribute('style');
46268             }
46269         }
46270         return true; // do children
46271         
46272         
46273         
46274     },
46275     
46276     styleToObject: function(node)
46277     {
46278         var styles = (node.getAttribute("style") || '').split(";");
46279         var ret = {};
46280         Roo.each(styles, function(s) {
46281             if (!s.match(/:/)) {
46282                 return;
46283             }
46284             var kv = s.split(":");
46285              
46286             // what ever is left... we allow.
46287             ret[kv[0].trim()] = kv[1];
46288         });
46289         return ret;
46290     },
46291     
46292     
46293     replaceAname : function (doc)
46294     {
46295         // replace all the a/name without..
46296         var aa = Array.from(doc.getElementsByTagName('a'));
46297         for (var i = 0; i  < aa.length; i++) {
46298             var a = aa[i];
46299             if (a.hasAttribute("name")) {
46300                 a.removeAttribute("name");
46301             }
46302             if (a.hasAttribute("href")) {
46303                 continue;
46304             }
46305             // reparent children.
46306             this.removeNodeKeepChildren(a);
46307             
46308         }
46309         
46310         
46311         
46312     },
46313
46314     
46315     
46316     replaceDocBullets : function(doc)
46317     {
46318         // this is a bit odd - but it appears some indents use ql-indent-1
46319          //Roo.log(doc.innerHTML);
46320         
46321         var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
46322         for( var i = 0; i < listpara.length; i ++) {
46323             listpara[i].className = "MsoListParagraph";
46324         }
46325         
46326         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
46327         for( var i = 0; i < listpara.length; i ++) {
46328             listpara[i].className = "MsoListParagraph";
46329         }
46330         listpara =  Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
46331         for( var i = 0; i < listpara.length; i ++) {
46332             listpara[i].className = "MsoListParagraph";
46333         }
46334         listpara =  Array.from(doc.getElementsByClassName('ql-indent-1'));
46335         for( var i = 0; i < listpara.length; i ++) {
46336             listpara[i].className = "MsoListParagraph";
46337         }
46338         
46339         // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
46340         var htwo =  Array.from(doc.getElementsByTagName('h2'));
46341         for( var i = 0; i < htwo.length; i ++) {
46342             if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
46343                 htwo[i].className = "MsoListParagraph";
46344             }
46345         }
46346         listpara =  Array.from(doc.getElementsByClassName('MsoNormal'));
46347         for( var i = 0; i < listpara.length; i ++) {
46348             if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
46349                 listpara[i].className = "MsoListParagraph";
46350             } else {
46351                 listpara[i].className = "MsoNormalx";
46352             }
46353         }
46354        
46355         listpara = doc.getElementsByClassName('MsoListParagraph');
46356         // Roo.log(doc.innerHTML);
46357         
46358         
46359         
46360         while(listpara.length) {
46361             
46362             this.replaceDocBullet(listpara.item(0));
46363         }
46364       
46365     },
46366     
46367      
46368     
46369     replaceDocBullet : function(p)
46370     {
46371         // gather all the siblings.
46372         var ns = p,
46373             parent = p.parentNode,
46374             doc = parent.ownerDocument,
46375             items = [];
46376          
46377         //Roo.log("Parsing: " + p.innerText)    ;
46378         var listtype = 'ul';   
46379         while (ns) {
46380             if (ns.nodeType != 1) {
46381                 ns = ns.nextSibling;
46382                 continue;
46383             }
46384             if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
46385                 //Roo.log("Missing para r q1indent - got:" + ns.className);
46386                 break;
46387             }
46388             var spans = ns.getElementsByTagName('span');
46389             
46390             if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
46391                 items.push(ns);
46392                 ns = ns.nextSibling;
46393                 has_list = true;
46394                 if (!spans.length) {
46395                     continue;
46396                 }
46397                 var ff = '';
46398                 var se = spans[0];
46399                 for (var i = 0; i < spans.length;i++) {
46400                     se = spans[i];
46401                     if (se.hasAttribute('style')  && se.hasAttribute('style') && se.style.fontFamily != '') {
46402                         ff = se.style.fontFamily;
46403                         break;
46404                     }
46405                 }
46406                  
46407                     
46408                 //Roo.log("got font family: " + ff);
46409                 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
46410                     listtype = 'ol';
46411                 }
46412                 
46413                 continue;
46414             }
46415             //Roo.log("no mso-list?");
46416             
46417             var spans = ns.getElementsByTagName('span');
46418             if (!spans.length) {
46419                 break;
46420             }
46421             var has_list  = false;
46422             for(var i = 0; i < spans.length; i++) {
46423                 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
46424                     has_list = true;
46425                     break;
46426                 }
46427             }
46428             if (!has_list) {
46429                 break;
46430             }
46431             items.push(ns);
46432             ns = ns.nextSibling;
46433             
46434             
46435         }
46436         if (!items.length) {
46437             ns.className = "";
46438             return;
46439         }
46440         
46441         var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
46442         parent.insertBefore(ul, p);
46443         var lvl = 0;
46444         var stack = [ ul ];
46445         var last_li = false;
46446         
46447         var margin_to_depth = {};
46448         max_margins = -1;
46449         
46450         items.forEach(function(n, ipos) {
46451             //Roo.log("got innertHMLT=" + n.innerHTML);
46452             
46453             var spans = n.getElementsByTagName('span');
46454             if (!spans.length) {
46455                 //Roo.log("No spans found");
46456                  
46457                 parent.removeChild(n);
46458                 
46459                 
46460                 return; // skip it...
46461             }
46462            
46463                 
46464             var num = 1;
46465             var style = {};
46466             for(var i = 0; i < spans.length; i++) {
46467             
46468                 style = this.styleToObject(spans[i]);
46469                 if (typeof(style['mso-list']) == 'undefined') {
46470                     continue;
46471                 }
46472                 if (listtype == 'ol') {
46473                    num = spans[i].innerText.replace(/[^0-9]+]/g,'')  * 1;
46474                 }
46475                 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
46476                 break;
46477             }
46478             //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
46479             style = this.styleToObject(n); // mo-list is from the parent node.
46480             if (typeof(style['mso-list']) == 'undefined') {
46481                 //Roo.log("parent is missing level");
46482                   
46483                 parent.removeChild(n);
46484                  
46485                 return;
46486             }
46487             
46488             var margin = style['margin-left'];
46489             if (typeof(margin_to_depth[margin]) == 'undefined') {
46490                 max_margins++;
46491                 margin_to_depth[margin] = max_margins;
46492             }
46493             nlvl = margin_to_depth[margin] ;
46494              
46495             if (nlvl > lvl) {
46496                 //new indent
46497                 var nul = doc.createElement(listtype); // what about number lists...
46498                 if (!last_li) {
46499                     last_li = doc.createElement('li');
46500                     stack[lvl].appendChild(last_li);
46501                 }
46502                 last_li.appendChild(nul);
46503                 stack[nlvl] = nul;
46504                 
46505             }
46506             lvl = nlvl;
46507             
46508             // not starting at 1..
46509             if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
46510                 stack[nlvl].setAttribute("start", num);
46511             }
46512             
46513             var nli = stack[nlvl].appendChild(doc.createElement('li'));
46514             last_li = nli;
46515             nli.innerHTML = n.innerHTML;
46516             //Roo.log("innerHTML = " + n.innerHTML);
46517             parent.removeChild(n);
46518             
46519              
46520              
46521             
46522         },this);
46523         
46524         
46525         
46526         
46527     },
46528     
46529     replaceImageTable : function(doc)
46530     {
46531          /*
46532           <table cellpadding=0 cellspacing=0 align=left>
46533   <tr>
46534    <td width=423 height=0></td>
46535   </tr>
46536   <tr>
46537    <td></td>
46538    <td><img width=601 height=401
46539    src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
46540    v:shapes="Picture_x0020_2"></td>
46541   </tr>
46542  </table>
46543  */
46544         var imgs = Array.from(doc.getElementsByTagName('img'));
46545         Roo.each(imgs, function(img) {
46546             var td = img.parentNode;
46547             if (td.nodeName !=  'TD') {
46548                 return;
46549             }
46550             var tr = td.parentNode;
46551             if (tr.nodeName !=  'TR') {
46552                 return;
46553             }
46554             var tbody = tr.parentNode;
46555             if (tbody.nodeName !=  'TBODY') {
46556                 return;
46557             }
46558             var table = tbody.parentNode;
46559             if (table.nodeName !=  'TABLE') {
46560                 return;
46561             }
46562             // first row..
46563             
46564             if (table.getElementsByTagName('tr').length != 2) {
46565                 return;
46566             }
46567             if (table.getElementsByTagName('td').length != 3) {
46568                 return;
46569             }
46570             if (table.innerText.trim() != '') {
46571                 return;
46572             }
46573             var p = table.parentNode;
46574             img.parentNode.removeChild(img);
46575             p.insertBefore(img, table);
46576             p.removeChild(table);
46577             
46578             
46579             
46580         });
46581         
46582       
46583     }
46584     
46585 });
46586 /**
46587  * @class Roo.htmleditor.FilterStyleToTag
46588  * part of the word stuff... - certain 'styles' should be converted to tags.
46589  * eg.
46590  *   font-weight: bold -> bold
46591  *   ?? super / subscrit etc..
46592  * 
46593  * @constructor
46594 * Run a new style to tag filter.
46595 * @param {Object} config Configuration options
46596  */
46597 Roo.htmleditor.FilterStyleToTag = function(cfg)
46598 {
46599     
46600     this.tags = {
46601         B  : [ 'fontWeight' , 'bold'],
46602         I :  [ 'fontStyle' , 'italic'],
46603         //pre :  [ 'font-style' , 'italic'],
46604         // h1.. h6 ?? font-size?
46605         SUP : [ 'verticalAlign' , 'super' ],
46606         SUB : [ 'verticalAlign' , 'sub' ]
46607         
46608         
46609     };
46610     
46611     Roo.apply(this, cfg);
46612      
46613     
46614     this.walk(cfg.node);
46615     
46616     
46617     
46618 }
46619
46620
46621 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
46622 {
46623     tag: true, // all tags
46624     
46625     tags : false,
46626     
46627     
46628     replaceTag : function(node)
46629     {
46630         
46631         
46632         if (node.getAttribute("style") === null) {
46633             return true;
46634         }
46635         var inject = [];
46636         for (var k in this.tags) {
46637             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46638                 inject.push(k);
46639                 node.style.removeProperty(this.tags[k][0]);
46640             }
46641         }
46642         if (!inject.length) {
46643             return true; 
46644         }
46645         var cn = Array.from(node.childNodes);
46646         var nn = node;
46647         Roo.each(inject, function(t) {
46648             var nc = node.ownerDocument.createElement(t);
46649             nn.appendChild(nc);
46650             nn = nc;
46651         });
46652         for(var i = 0;i < cn.length;cn++) {
46653             node.removeChild(cn[i]);
46654             nn.appendChild(cn[i]);
46655         }
46656         return true /// iterate thru
46657     }
46658     
46659 })/**
46660  * @class Roo.htmleditor.FilterLongBr
46661  * BR/BR/BR - keep a maximum of 2...
46662  * @constructor
46663  * Run a new Long BR Filter
46664  * @param {Object} config Configuration options
46665  */
46666
46667 Roo.htmleditor.FilterLongBr = function(cfg)
46668 {
46669     // no need to apply config.
46670     this.walk(cfg.node);
46671 }
46672
46673 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46674 {
46675     
46676      
46677     tag : 'BR',
46678     
46679      
46680     replaceTag : function(node)
46681     {
46682         
46683         var ps = node.nextSibling;
46684         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46685             ps = ps.nextSibling;
46686         }
46687         
46688         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
46689             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46690             return false;
46691         }
46692         
46693         if (!ps || ps.nodeType != 1) {
46694             return false;
46695         }
46696         
46697         if (!ps || ps.tagName != 'BR') {
46698            
46699             return false;
46700         }
46701         
46702         
46703         
46704         
46705         
46706         if (!node.previousSibling) {
46707             return false;
46708         }
46709         var ps = node.previousSibling;
46710         
46711         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46712             ps = ps.previousSibling;
46713         }
46714         if (!ps || ps.nodeType != 1) {
46715             return false;
46716         }
46717         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46718         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46719             return false;
46720         }
46721         
46722         node.parentNode.removeChild(node); // remove me...
46723         
46724         return false; // no need to do children
46725
46726     }
46727     
46728 }); 
46729
46730 /**
46731  * @class Roo.htmleditor.FilterBlock
46732  * removes id / data-block and contenteditable that are associated with blocks
46733  * usage should be done on a cloned copy of the dom
46734  * @constructor
46735 * Run a new Attribute Filter { node : xxxx }}
46736 * @param {Object} config Configuration options
46737  */
46738 Roo.htmleditor.FilterBlock = function(cfg)
46739 {
46740     Roo.apply(this, cfg);
46741     var qa = cfg.node.querySelectorAll;
46742     this.removeAttributes('data-block');
46743     this.removeAttributes('contenteditable');
46744     this.removeAttributes('id');
46745     
46746 }
46747
46748 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
46749 {
46750     node: true, // all tags
46751      
46752      
46753     removeAttributes : function(attr)
46754     {
46755         var ar = this.node.querySelectorAll('*[' + attr + ']');
46756         for (var i =0;i<ar.length;i++) {
46757             ar[i].removeAttribute(attr);
46758         }
46759     }
46760         
46761         
46762         
46763     
46764 });
46765 /***
46766  * This is based loosely on tinymce 
46767  * @class Roo.htmleditor.TidySerializer
46768  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46769  * @constructor
46770  * @method Serializer
46771  * @param {Object} settings Name/value settings object.
46772  */
46773
46774
46775 Roo.htmleditor.TidySerializer = function(settings)
46776 {
46777     Roo.apply(this, settings);
46778     
46779     this.writer = new Roo.htmleditor.TidyWriter(settings);
46780     
46781     
46782
46783 };
46784 Roo.htmleditor.TidySerializer.prototype = {
46785     
46786     /**
46787      * @param {boolean} inner do the inner of the node.
46788      */
46789     inner : false,
46790     
46791     writer : false,
46792     
46793     /**
46794     * Serializes the specified node into a string.
46795     *
46796     * @example
46797     * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
46798     * @method serialize
46799     * @param {DomElement} node Node instance to serialize.
46800     * @return {String} String with HTML based on DOM tree.
46801     */
46802     serialize : function(node) {
46803         
46804         // = settings.validate;
46805         var writer = this.writer;
46806         var self  = this;
46807         this.handlers = {
46808             // #text
46809             3: function(node) {
46810                 
46811                 writer.text(node.nodeValue, node);
46812             },
46813             // #comment
46814             8: function(node) {
46815                 writer.comment(node.nodeValue);
46816             },
46817             // Processing instruction
46818             7: function(node) {
46819                 writer.pi(node.name, node.nodeValue);
46820             },
46821             // Doctype
46822             10: function(node) {
46823                 writer.doctype(node.nodeValue);
46824             },
46825             // CDATA
46826             4: function(node) {
46827                 writer.cdata(node.nodeValue);
46828             },
46829             // Document fragment
46830             11: function(node) {
46831                 node = node.firstChild;
46832                 if (!node) {
46833                     return;
46834                 }
46835                 while(node) {
46836                     self.walk(node);
46837                     node = node.nextSibling
46838                 }
46839             }
46840         };
46841         writer.reset();
46842         1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
46843         return writer.getContent();
46844     },
46845
46846     walk: function(node)
46847     {
46848         var attrName, attrValue, sortedAttrs, i, l, elementRule,
46849             handler = this.handlers[node.nodeType];
46850             
46851         if (handler) {
46852             handler(node);
46853             return;
46854         }
46855     
46856         var name = node.nodeName;
46857         var isEmpty = node.childNodes.length < 1;
46858       
46859         var writer = this.writer;
46860         var attrs = node.attributes;
46861         // Sort attributes
46862         
46863         writer.start(node.nodeName, attrs, isEmpty, node);
46864         if (isEmpty) {
46865             return;
46866         }
46867         node = node.firstChild;
46868         if (!node) {
46869             writer.end(name);
46870             return;
46871         }
46872         while (node) {
46873             this.walk(node);
46874             node = node.nextSibling;
46875         }
46876         writer.end(name);
46877         
46878     
46879     }
46880     // Serialize element and treat all non elements as fragments
46881    
46882 }; 
46883
46884 /***
46885  * This is based loosely on tinymce 
46886  * @class Roo.htmleditor.TidyWriter
46887  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
46888  *
46889  * Known issues?
46890  * - not tested much with 'PRE' formated elements.
46891  * 
46892  *
46893  *
46894  */
46895
46896 Roo.htmleditor.TidyWriter = function(settings)
46897 {
46898     
46899     // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
46900     Roo.apply(this, settings);
46901     this.html = [];
46902     this.state = [];
46903      
46904     this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
46905   
46906 }
46907 Roo.htmleditor.TidyWriter.prototype = {
46908
46909  
46910     state : false,
46911     
46912     indent :  '  ',
46913     
46914     // part of state...
46915     indentstr : '',
46916     in_pre: false,
46917     in_inline : false,
46918     last_inline : false,
46919     encode : false,
46920      
46921     
46922             /**
46923     * Writes the a start element such as <p id="a">.
46924     *
46925     * @method start
46926     * @param {String} name Name of the element.
46927     * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
46928     * @param {Boolean} empty Optional empty state if the tag should end like <br />.
46929     */
46930     start: function(name, attrs, empty, node)
46931     {
46932         var i, l, attr, value;
46933         
46934         // there are some situations where adding line break && indentation will not work. will not work.
46935         // <span / b / i ... formating?
46936         
46937         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
46938         var in_pre    = this.in_pre    || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
46939         
46940         var is_short   = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
46941         
46942         var add_lb = name == 'BR' ? false : in_inline;
46943         
46944         if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
46945             i_inline = false;
46946         }
46947
46948         var indentstr =  this.indentstr;
46949         
46950         // e_inline = elements that can be inline, but still allow \n before and after?
46951         // only 'BR' ??? any others?
46952         
46953         // ADD LINE BEFORE tage
46954         if (!this.in_pre) {
46955             if (in_inline) {
46956                 //code
46957                 if (name == 'BR') {
46958                     this.addLine();
46959                 } else if (this.lastElementEndsWS()) {
46960                     this.addLine();
46961                 } else{
46962                     // otherwise - no new line. (and dont indent.)
46963                     indentstr = '';
46964                 }
46965                 
46966             } else {
46967                 this.addLine();
46968             }
46969         } else {
46970             indentstr = '';
46971         }
46972         
46973         this.html.push(indentstr + '<', name.toLowerCase());
46974         
46975         if (attrs) {
46976             for (i = 0, l = attrs.length; i < l; i++) {
46977                 attr = attrs[i];
46978                 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
46979             }
46980         }
46981      
46982         if (empty) {
46983             if (is_short) {
46984                 this.html[this.html.length] = '/>';
46985             } else {
46986                 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
46987             }
46988             var e_inline = name == 'BR' ? false : this.in_inline;
46989             
46990             if (!e_inline && !this.in_pre) {
46991                 this.addLine();
46992             }
46993             return;
46994         
46995         }
46996         // not empty..
46997         this.html[this.html.length] = '>';
46998         
46999         // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
47000         /*
47001         if (!in_inline && !in_pre) {
47002             var cn = node.firstChild;
47003             while(cn) {
47004                 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
47005                     in_inline = true
47006                     break;
47007                 }
47008                 cn = cn.nextSibling;
47009             }
47010              
47011         }
47012         */
47013         
47014         
47015         this.pushState({
47016             indentstr : in_pre   ? '' : (this.indentstr + this.indent),
47017             in_pre : in_pre,
47018             in_inline :  in_inline
47019         });
47020         // add a line after if we are not in a
47021         
47022         if (!in_inline && !in_pre) {
47023             this.addLine();
47024         }
47025         
47026             
47027          
47028         
47029     },
47030     
47031     lastElementEndsWS : function()
47032     {
47033         var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
47034         if (value === false) {
47035             return true;
47036         }
47037         return value.match(/\s+$/);
47038         
47039     },
47040     
47041     /**
47042      * Writes the a end element such as </p>.
47043      *
47044      * @method end
47045      * @param {String} name Name of the element.
47046      */
47047     end: function(name) {
47048         var value;
47049         this.popState();
47050         var indentstr = '';
47051         var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
47052         
47053         if (!this.in_pre && !in_inline) {
47054             this.addLine();
47055             indentstr  = this.indentstr;
47056         }
47057         this.html.push(indentstr + '</', name.toLowerCase(), '>');
47058         this.last_inline = in_inline;
47059         
47060         // pop the indent state..
47061     },
47062     /**
47063      * Writes a text node.
47064      *
47065      * In pre - we should not mess with the contents.
47066      * 
47067      *
47068      * @method text
47069      * @param {String} text String to write out.
47070      * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
47071      */
47072     text: function(in_text, node)
47073     {
47074         // if not in whitespace critical
47075         if (in_text.length < 1) {
47076             return;
47077         }
47078         var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
47079         
47080         if (this.in_pre) {
47081             this.html[this.html.length] =  text;
47082             return;   
47083         }
47084         
47085         if (this.in_inline) {
47086             text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
47087             if (text != ' ') {
47088                 text = text.replace(/\s+/,' ');  // all white space to single white space
47089                 
47090                     
47091                 // if next tag is '<BR>', then we can trim right..
47092                 if (node.nextSibling &&
47093                     node.nextSibling.nodeType == 1 &&
47094                     node.nextSibling.nodeName == 'BR' )
47095                 {
47096                     text = text.replace(/\s+$/g,'');
47097                 }
47098                 // if previous tag was a BR, we can also trim..
47099                 if (node.previousSibling &&
47100                     node.previousSibling.nodeType == 1 &&
47101                     node.previousSibling.nodeName == 'BR' )
47102                 {
47103                     text = this.indentstr +  text.replace(/^\s+/g,'');
47104                 }
47105                 if (text.match(/\n/)) {
47106                     text = text.replace(
47107                         /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
47108                     );
47109                     // remoeve the last whitespace / line break.
47110                     text = text.replace(/\n\s+$/,'');
47111                 }
47112                 // repace long lines
47113                 
47114             }
47115              
47116             this.html[this.html.length] =  text;
47117             return;   
47118         }
47119         // see if previous element was a inline element.
47120         var indentstr = this.indentstr;
47121    
47122         text = text.replace(/\s+/g," "); // all whitespace into single white space.
47123         
47124         // should trim left?
47125         if (node.previousSibling &&
47126             node.previousSibling.nodeType == 1 &&
47127             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
47128         {
47129             indentstr = '';
47130             
47131         } else {
47132             this.addLine();
47133             text = text.replace(/^\s+/,''); // trim left
47134           
47135         }
47136         // should trim right?
47137         if (node.nextSibling &&
47138             node.nextSibling.nodeType == 1 &&
47139             Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
47140         {
47141           // noop
47142             
47143         }  else {
47144             text = text.replace(/\s+$/,''); // trim right
47145         }
47146          
47147               
47148         
47149         
47150         
47151         if (text.length < 1) {
47152             return;
47153         }
47154         if (!text.match(/\n/)) {
47155             this.html.push(indentstr + text);
47156             return;
47157         }
47158         
47159         text = this.indentstr + text.replace(
47160             /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
47161         );
47162         // remoeve the last whitespace / line break.
47163         text = text.replace(/\s+$/,''); 
47164         
47165         this.html.push(text);
47166         
47167         // split and indent..
47168         
47169         
47170     },
47171     /**
47172      * Writes a cdata node such as <![CDATA[data]]>.
47173      *
47174      * @method cdata
47175      * @param {String} text String to write out inside the cdata.
47176      */
47177     cdata: function(text) {
47178         this.html.push('<![CDATA[', text, ']]>');
47179     },
47180     /**
47181     * Writes a comment node such as <!-- Comment -->.
47182     *
47183     * @method cdata
47184     * @param {String} text String to write out inside the comment.
47185     */
47186    comment: function(text) {
47187        this.html.push('<!--', text, '-->');
47188    },
47189     /**
47190      * Writes a PI node such as <?xml attr="value" ?>.
47191      *
47192      * @method pi
47193      * @param {String} name Name of the pi.
47194      * @param {String} text String to write out inside the pi.
47195      */
47196     pi: function(name, text) {
47197         text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
47198         this.indent != '' && this.html.push('\n');
47199     },
47200     /**
47201      * Writes a doctype node such as <!DOCTYPE data>.
47202      *
47203      * @method doctype
47204      * @param {String} text String to write out inside the doctype.
47205      */
47206     doctype: function(text) {
47207         this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
47208     },
47209     /**
47210      * Resets the internal buffer if one wants to reuse the writer.
47211      *
47212      * @method reset
47213      */
47214     reset: function() {
47215         this.html.length = 0;
47216         this.state = [];
47217         this.pushState({
47218             indentstr : '',
47219             in_pre : false, 
47220             in_inline : false
47221         })
47222     },
47223     /**
47224      * Returns the contents that got serialized.
47225      *
47226      * @method getContent
47227      * @return {String} HTML contents that got written down.
47228      */
47229     getContent: function() {
47230         return this.html.join('').replace(/\n$/, '');
47231     },
47232     
47233     pushState : function(cfg)
47234     {
47235         this.state.push(cfg);
47236         Roo.apply(this, cfg);
47237     },
47238     
47239     popState : function()
47240     {
47241         if (this.state.length < 1) {
47242             return; // nothing to push
47243         }
47244         var cfg = {
47245             in_pre: false,
47246             indentstr : ''
47247         };
47248         this.state.pop();
47249         if (this.state.length > 0) {
47250             cfg = this.state[this.state.length-1]; 
47251         }
47252         Roo.apply(this, cfg);
47253     },
47254     
47255     addLine: function()
47256     {
47257         if (this.html.length < 1) {
47258             return;
47259         }
47260         
47261         
47262         var value = this.html[this.html.length - 1];
47263         if (value.length > 0 && '\n' !== value) {
47264             this.html.push('\n');
47265         }
47266     }
47267     
47268     
47269 //'pre script noscript style textarea video audio iframe object code'
47270 // shortended... 'area base basefont br col frame hr img input isindex link  meta param embed source wbr track');
47271 // inline 
47272 };
47273
47274 Roo.htmleditor.TidyWriter.inline_elements = [
47275         'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
47276         'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
47277 ];
47278 Roo.htmleditor.TidyWriter.shortend_elements = [
47279     'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
47280     'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
47281 ];
47282
47283 Roo.htmleditor.TidyWriter.whitespace_elements = [
47284     'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
47285 ];/***
47286  * This is based loosely on tinymce 
47287  * @class Roo.htmleditor.TidyEntities
47288  * @static
47289  * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
47290  *
47291  * Not 100% sure this is actually used or needed.
47292  */
47293
47294 Roo.htmleditor.TidyEntities = {
47295     
47296     /**
47297      * initialize data..
47298      */
47299     init : function (){
47300      
47301         this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
47302        
47303     },
47304
47305
47306     buildEntitiesLookup: function(items, radix) {
47307         var i, chr, entity, lookup = {};
47308         if (!items) {
47309             return {};
47310         }
47311         items = typeof(items) == 'string' ? items.split(',') : items;
47312         radix = radix || 10;
47313         // Build entities lookup table
47314         for (i = 0; i < items.length; i += 2) {
47315             chr = String.fromCharCode(parseInt(items[i], radix));
47316             // Only add non base entities
47317             if (!this.baseEntities[chr]) {
47318                 entity = '&' + items[i + 1] + ';';
47319                 lookup[chr] = entity;
47320                 lookup[entity] = chr;
47321             }
47322         }
47323         return lookup;
47324         
47325     },
47326     
47327     asciiMap : {
47328             128: '€',
47329             130: '‚',
47330             131: 'ƒ',
47331             132: '„',
47332             133: '…',
47333             134: '†',
47334             135: '‡',
47335             136: 'ˆ',
47336             137: '‰',
47337             138: 'Š',
47338             139: '‹',
47339             140: 'Œ',
47340             142: 'Ž',
47341             145: '‘',
47342             146: '’',
47343             147: '“',
47344             148: '”',
47345             149: '•',
47346             150: '–',
47347             151: '—',
47348             152: '˜',
47349             153: '™',
47350             154: 'š',
47351             155: '›',
47352             156: 'œ',
47353             158: 'ž',
47354             159: 'Ÿ'
47355     },
47356     // Raw entities
47357     baseEntities : {
47358         '"': '&quot;',
47359         // Needs to be escaped since the YUI compressor would otherwise break the code
47360         '\'': '&#39;',
47361         '<': '&lt;',
47362         '>': '&gt;',
47363         '&': '&amp;',
47364         '`': '&#96;'
47365     },
47366     // Reverse lookup table for raw entities
47367     reverseEntities : {
47368         '&lt;': '<',
47369         '&gt;': '>',
47370         '&amp;': '&',
47371         '&quot;': '"',
47372         '&apos;': '\''
47373     },
47374     
47375     attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47376     textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
47377     rawCharsRegExp : /[<>&\"\']/g,
47378     entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
47379     namedEntities  : false,
47380     namedEntitiesData : [ 
47381         '50',
47382         'nbsp',
47383         '51',
47384         'iexcl',
47385         '52',
47386         'cent',
47387         '53',
47388         'pound',
47389         '54',
47390         'curren',
47391         '55',
47392         'yen',
47393         '56',
47394         'brvbar',
47395         '57',
47396         'sect',
47397         '58',
47398         'uml',
47399         '59',
47400         'copy',
47401         '5a',
47402         'ordf',
47403         '5b',
47404         'laquo',
47405         '5c',
47406         'not',
47407         '5d',
47408         'shy',
47409         '5e',
47410         'reg',
47411         '5f',
47412         'macr',
47413         '5g',
47414         'deg',
47415         '5h',
47416         'plusmn',
47417         '5i',
47418         'sup2',
47419         '5j',
47420         'sup3',
47421         '5k',
47422         'acute',
47423         '5l',
47424         'micro',
47425         '5m',
47426         'para',
47427         '5n',
47428         'middot',
47429         '5o',
47430         'cedil',
47431         '5p',
47432         'sup1',
47433         '5q',
47434         'ordm',
47435         '5r',
47436         'raquo',
47437         '5s',
47438         'frac14',
47439         '5t',
47440         'frac12',
47441         '5u',
47442         'frac34',
47443         '5v',
47444         'iquest',
47445         '60',
47446         'Agrave',
47447         '61',
47448         'Aacute',
47449         '62',
47450         'Acirc',
47451         '63',
47452         'Atilde',
47453         '64',
47454         'Auml',
47455         '65',
47456         'Aring',
47457         '66',
47458         'AElig',
47459         '67',
47460         'Ccedil',
47461         '68',
47462         'Egrave',
47463         '69',
47464         'Eacute',
47465         '6a',
47466         'Ecirc',
47467         '6b',
47468         'Euml',
47469         '6c',
47470         'Igrave',
47471         '6d',
47472         'Iacute',
47473         '6e',
47474         'Icirc',
47475         '6f',
47476         'Iuml',
47477         '6g',
47478         'ETH',
47479         '6h',
47480         'Ntilde',
47481         '6i',
47482         'Ograve',
47483         '6j',
47484         'Oacute',
47485         '6k',
47486         'Ocirc',
47487         '6l',
47488         'Otilde',
47489         '6m',
47490         'Ouml',
47491         '6n',
47492         'times',
47493         '6o',
47494         'Oslash',
47495         '6p',
47496         'Ugrave',
47497         '6q',
47498         'Uacute',
47499         '6r',
47500         'Ucirc',
47501         '6s',
47502         'Uuml',
47503         '6t',
47504         'Yacute',
47505         '6u',
47506         'THORN',
47507         '6v',
47508         'szlig',
47509         '70',
47510         'agrave',
47511         '71',
47512         'aacute',
47513         '72',
47514         'acirc',
47515         '73',
47516         'atilde',
47517         '74',
47518         'auml',
47519         '75',
47520         'aring',
47521         '76',
47522         'aelig',
47523         '77',
47524         'ccedil',
47525         '78',
47526         'egrave',
47527         '79',
47528         'eacute',
47529         '7a',
47530         'ecirc',
47531         '7b',
47532         'euml',
47533         '7c',
47534         'igrave',
47535         '7d',
47536         'iacute',
47537         '7e',
47538         'icirc',
47539         '7f',
47540         'iuml',
47541         '7g',
47542         'eth',
47543         '7h',
47544         'ntilde',
47545         '7i',
47546         'ograve',
47547         '7j',
47548         'oacute',
47549         '7k',
47550         'ocirc',
47551         '7l',
47552         'otilde',
47553         '7m',
47554         'ouml',
47555         '7n',
47556         'divide',
47557         '7o',
47558         'oslash',
47559         '7p',
47560         'ugrave',
47561         '7q',
47562         'uacute',
47563         '7r',
47564         'ucirc',
47565         '7s',
47566         'uuml',
47567         '7t',
47568         'yacute',
47569         '7u',
47570         'thorn',
47571         '7v',
47572         'yuml',
47573         'ci',
47574         'fnof',
47575         'sh',
47576         'Alpha',
47577         'si',
47578         'Beta',
47579         'sj',
47580         'Gamma',
47581         'sk',
47582         'Delta',
47583         'sl',
47584         'Epsilon',
47585         'sm',
47586         'Zeta',
47587         'sn',
47588         'Eta',
47589         'so',
47590         'Theta',
47591         'sp',
47592         'Iota',
47593         'sq',
47594         'Kappa',
47595         'sr',
47596         'Lambda',
47597         'ss',
47598         'Mu',
47599         'st',
47600         'Nu',
47601         'su',
47602         'Xi',
47603         'sv',
47604         'Omicron',
47605         't0',
47606         'Pi',
47607         't1',
47608         'Rho',
47609         't3',
47610         'Sigma',
47611         't4',
47612         'Tau',
47613         't5',
47614         'Upsilon',
47615         't6',
47616         'Phi',
47617         't7',
47618         'Chi',
47619         't8',
47620         'Psi',
47621         't9',
47622         'Omega',
47623         'th',
47624         'alpha',
47625         'ti',
47626         'beta',
47627         'tj',
47628         'gamma',
47629         'tk',
47630         'delta',
47631         'tl',
47632         'epsilon',
47633         'tm',
47634         'zeta',
47635         'tn',
47636         'eta',
47637         'to',
47638         'theta',
47639         'tp',
47640         'iota',
47641         'tq',
47642         'kappa',
47643         'tr',
47644         'lambda',
47645         'ts',
47646         'mu',
47647         'tt',
47648         'nu',
47649         'tu',
47650         'xi',
47651         'tv',
47652         'omicron',
47653         'u0',
47654         'pi',
47655         'u1',
47656         'rho',
47657         'u2',
47658         'sigmaf',
47659         'u3',
47660         'sigma',
47661         'u4',
47662         'tau',
47663         'u5',
47664         'upsilon',
47665         'u6',
47666         'phi',
47667         'u7',
47668         'chi',
47669         'u8',
47670         'psi',
47671         'u9',
47672         'omega',
47673         'uh',
47674         'thetasym',
47675         'ui',
47676         'upsih',
47677         'um',
47678         'piv',
47679         '812',
47680         'bull',
47681         '816',
47682         'hellip',
47683         '81i',
47684         'prime',
47685         '81j',
47686         'Prime',
47687         '81u',
47688         'oline',
47689         '824',
47690         'frasl',
47691         '88o',
47692         'weierp',
47693         '88h',
47694         'image',
47695         '88s',
47696         'real',
47697         '892',
47698         'trade',
47699         '89l',
47700         'alefsym',
47701         '8cg',
47702         'larr',
47703         '8ch',
47704         'uarr',
47705         '8ci',
47706         'rarr',
47707         '8cj',
47708         'darr',
47709         '8ck',
47710         'harr',
47711         '8dl',
47712         'crarr',
47713         '8eg',
47714         'lArr',
47715         '8eh',
47716         'uArr',
47717         '8ei',
47718         'rArr',
47719         '8ej',
47720         'dArr',
47721         '8ek',
47722         'hArr',
47723         '8g0',
47724         'forall',
47725         '8g2',
47726         'part',
47727         '8g3',
47728         'exist',
47729         '8g5',
47730         'empty',
47731         '8g7',
47732         'nabla',
47733         '8g8',
47734         'isin',
47735         '8g9',
47736         'notin',
47737         '8gb',
47738         'ni',
47739         '8gf',
47740         'prod',
47741         '8gh',
47742         'sum',
47743         '8gi',
47744         'minus',
47745         '8gn',
47746         'lowast',
47747         '8gq',
47748         'radic',
47749         '8gt',
47750         'prop',
47751         '8gu',
47752         'infin',
47753         '8h0',
47754         'ang',
47755         '8h7',
47756         'and',
47757         '8h8',
47758         'or',
47759         '8h9',
47760         'cap',
47761         '8ha',
47762         'cup',
47763         '8hb',
47764         'int',
47765         '8hk',
47766         'there4',
47767         '8hs',
47768         'sim',
47769         '8i5',
47770         'cong',
47771         '8i8',
47772         'asymp',
47773         '8j0',
47774         'ne',
47775         '8j1',
47776         'equiv',
47777         '8j4',
47778         'le',
47779         '8j5',
47780         'ge',
47781         '8k2',
47782         'sub',
47783         '8k3',
47784         'sup',
47785         '8k4',
47786         'nsub',
47787         '8k6',
47788         'sube',
47789         '8k7',
47790         'supe',
47791         '8kl',
47792         'oplus',
47793         '8kn',
47794         'otimes',
47795         '8l5',
47796         'perp',
47797         '8m5',
47798         'sdot',
47799         '8o8',
47800         'lceil',
47801         '8o9',
47802         'rceil',
47803         '8oa',
47804         'lfloor',
47805         '8ob',
47806         'rfloor',
47807         '8p9',
47808         'lang',
47809         '8pa',
47810         'rang',
47811         '9ea',
47812         'loz',
47813         '9j0',
47814         'spades',
47815         '9j3',
47816         'clubs',
47817         '9j5',
47818         'hearts',
47819         '9j6',
47820         'diams',
47821         'ai',
47822         'OElig',
47823         'aj',
47824         'oelig',
47825         'b0',
47826         'Scaron',
47827         'b1',
47828         'scaron',
47829         'bo',
47830         'Yuml',
47831         'm6',
47832         'circ',
47833         'ms',
47834         'tilde',
47835         '802',
47836         'ensp',
47837         '803',
47838         'emsp',
47839         '809',
47840         'thinsp',
47841         '80c',
47842         'zwnj',
47843         '80d',
47844         'zwj',
47845         '80e',
47846         'lrm',
47847         '80f',
47848         'rlm',
47849         '80j',
47850         'ndash',
47851         '80k',
47852         'mdash',
47853         '80o',
47854         'lsquo',
47855         '80p',
47856         'rsquo',
47857         '80q',
47858         'sbquo',
47859         '80s',
47860         'ldquo',
47861         '80t',
47862         'rdquo',
47863         '80u',
47864         'bdquo',
47865         '810',
47866         'dagger',
47867         '811',
47868         'Dagger',
47869         '81g',
47870         'permil',
47871         '81p',
47872         'lsaquo',
47873         '81q',
47874         'rsaquo',
47875         '85c',
47876         'euro'
47877     ],
47878
47879          
47880     /**
47881      * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
47882      *
47883      * @method encodeRaw
47884      * @param {String} text Text to encode.
47885      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47886      * @return {String} Entity encoded text.
47887      */
47888     encodeRaw: function(text, attr)
47889     {
47890         var t = this;
47891         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47892             return t.baseEntities[chr] || chr;
47893         });
47894     },
47895     /**
47896      * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
47897      * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
47898      * and is exposed as the DOMUtils.encode function.
47899      *
47900      * @method encodeAllRaw
47901      * @param {String} text Text to encode.
47902      * @return {String} Entity encoded text.
47903      */
47904     encodeAllRaw: function(text) {
47905         var t = this;
47906         return ('' + text).replace(this.rawCharsRegExp, function(chr) {
47907             return t.baseEntities[chr] || chr;
47908         });
47909     },
47910     /**
47911      * Encodes the specified string using numeric entities. The core entities will be
47912      * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
47913      *
47914      * @method encodeNumeric
47915      * @param {String} text Text to encode.
47916      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47917      * @return {String} Entity encoded text.
47918      */
47919     encodeNumeric: function(text, attr) {
47920         var t = this;
47921         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47922             // Multi byte sequence convert it to a single entity
47923             if (chr.length > 1) {
47924                 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
47925             }
47926             return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
47927         });
47928     },
47929     /**
47930      * Encodes the specified string using named entities. The core entities will be encoded
47931      * as named ones but all non lower ascii characters will be encoded into named entities.
47932      *
47933      * @method encodeNamed
47934      * @param {String} text Text to encode.
47935      * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
47936      * @param {Object} entities Optional parameter with entities to use.
47937      * @return {String} Entity encoded text.
47938      */
47939     encodeNamed: function(text, attr, entities) {
47940         var t = this;
47941         entities = entities || this.namedEntities;
47942         return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
47943             return t.baseEntities[chr] || entities[chr] || chr;
47944         });
47945     },
47946     /**
47947      * Returns an encode function based on the name(s) and it's optional entities.
47948      *
47949      * @method getEncodeFunc
47950      * @param {String} name Comma separated list of encoders for example named,numeric.
47951      * @param {String} entities Optional parameter with entities to use instead of the built in set.
47952      * @return {function} Encode function to be used.
47953      */
47954     getEncodeFunc: function(name, entities) {
47955         entities = this.buildEntitiesLookup(entities) || this.namedEntities;
47956         var t = this;
47957         function encodeNamedAndNumeric(text, attr) {
47958             return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
47959                 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
47960             });
47961         }
47962
47963         function encodeCustomNamed(text, attr) {
47964             return t.encodeNamed(text, attr, entities);
47965         }
47966         // Replace + with , to be compatible with previous TinyMCE versions
47967         name = this.makeMap(name.replace(/\+/g, ','));
47968         // Named and numeric encoder
47969         if (name.named && name.numeric) {
47970             return this.encodeNamedAndNumeric;
47971         }
47972         // Named encoder
47973         if (name.named) {
47974             // Custom names
47975             if (entities) {
47976                 return encodeCustomNamed;
47977             }
47978             return this.encodeNamed;
47979         }
47980         // Numeric
47981         if (name.numeric) {
47982             return this.encodeNumeric;
47983         }
47984         // Raw encoder
47985         return this.encodeRaw;
47986     },
47987     /**
47988      * Decodes the specified string, this will replace entities with raw UTF characters.
47989      *
47990      * @method decode
47991      * @param {String} text Text to entity decode.
47992      * @return {String} Entity decoded string.
47993      */
47994     decode: function(text)
47995     {
47996         var  t = this;
47997         return text.replace(this.entityRegExp, function(all, numeric) {
47998             if (numeric) {
47999                 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
48000                 // Support upper UTF
48001                 if (numeric > 65535) {
48002                     numeric -= 65536;
48003                     return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
48004                 }
48005                 return t.asciiMap[numeric] || String.fromCharCode(numeric);
48006             }
48007             return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
48008         });
48009     },
48010     nativeDecode : function (text) {
48011         return text;
48012     },
48013     makeMap : function (items, delim, map) {
48014                 var i;
48015                 items = items || [];
48016                 delim = delim || ',';
48017                 if (typeof items == "string") {
48018                         items = items.split(delim);
48019                 }
48020                 map = map || {};
48021                 i = items.length;
48022                 while (i--) {
48023                         map[items[i]] = {};
48024                 }
48025                 return map;
48026         }
48027 };
48028     
48029     
48030     
48031 Roo.htmleditor.TidyEntities.init();
48032 /**
48033  * @class Roo.htmleditor.KeyEnter
48034  * Handle Enter press..
48035  * @cfg {Roo.HtmlEditorCore} core the editor.
48036  * @constructor
48037  * Create a new Filter.
48038  * @param {Object} config Configuration options
48039  */
48040
48041
48042
48043
48044
48045 Roo.htmleditor.KeyEnter = function(cfg) {
48046     Roo.apply(this, cfg);
48047     // this does not actually call walk as it's really just a abstract class
48048  
48049     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
48050 }
48051
48052 //Roo.htmleditor.KeyEnter.i = 0;
48053
48054
48055 Roo.htmleditor.KeyEnter.prototype = {
48056     
48057     core : false,
48058     
48059     keypress : function(e)
48060     {
48061         if (e.charCode != 13 && e.charCode != 10) {
48062             Roo.log([e.charCode,e]);
48063             return true;
48064         }
48065         e.preventDefault();
48066         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
48067         var doc = this.core.doc;
48068           //add a new line
48069        
48070     
48071         var sel = this.core.getSelection();
48072         var range = sel.getRangeAt(0);
48073         var n = range.commonAncestorContainer;
48074         var pc = range.closest([ 'ol', 'ul']);
48075         var pli = range.closest('li');
48076         if (!pc || e.ctrlKey) {
48077             // on it list, or ctrl pressed.
48078             if (!e.ctrlKey) {
48079                 sel.insertNode('br', 'after'); 
48080             } else {
48081                 // only do this if we have ctrl key..
48082                 var br = doc.createElement('br');
48083                 br.className = 'clear';
48084                 br.setAttribute('style', 'clear: both');
48085                 sel.insertNode(br, 'after'); 
48086             }
48087             
48088          
48089             this.core.undoManager.addEvent();
48090             this.core.fireEditorEvent(e);
48091             return false;
48092         }
48093         
48094         // deal with <li> insetion
48095         if (pli.innerText.trim() == '' &&
48096             pli.previousSibling &&
48097             pli.previousSibling.nodeName == 'LI' &&
48098             pli.previousSibling.innerText.trim() ==  '') {
48099             pli.parentNode.removeChild(pli.previousSibling);
48100             sel.cursorAfter(pc);
48101             this.core.undoManager.addEvent();
48102             this.core.fireEditorEvent(e);
48103             return false;
48104         }
48105     
48106         var li = doc.createElement('LI');
48107         li.innerHTML = '&nbsp;';
48108         if (!pli || !pli.firstSibling) {
48109             pc.appendChild(li);
48110         } else {
48111             pli.parentNode.insertBefore(li, pli.firstSibling);
48112         }
48113         sel.cursorText (li.firstChild);
48114       
48115         this.core.undoManager.addEvent();
48116         this.core.fireEditorEvent(e);
48117
48118         return false;
48119         
48120     
48121         
48122         
48123          
48124     }
48125 };
48126      
48127 /**
48128  * @class Roo.htmleditor.Block
48129  * Base class for html editor blocks - do not use it directly .. extend it..
48130  * @cfg {DomElement} node The node to apply stuff to.
48131  * @cfg {String} friendly_name the name that appears in the context bar about this block
48132  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
48133  
48134  * @constructor
48135  * Create a new Filter.
48136  * @param {Object} config Configuration options
48137  */
48138
48139 Roo.htmleditor.Block  = function(cfg)
48140 {
48141     // do nothing .. should not be called really.
48142 }
48143 /**
48144  * factory method to get the block from an element (using cache if necessary)
48145  * @static
48146  * @param {HtmlElement} the dom element
48147  */
48148 Roo.htmleditor.Block.factory = function(node)
48149 {
48150     var cc = Roo.htmleditor.Block.cache;
48151     var id = Roo.get(node).id;
48152     if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
48153         Roo.htmleditor.Block.cache[id].readElement(node);
48154         return Roo.htmleditor.Block.cache[id];
48155     }
48156     var db  = node.getAttribute('data-block');
48157     if (!db) {
48158         db = node.nodeName.toLowerCase().toUpperCaseFirst();
48159     }
48160     var cls = Roo.htmleditor['Block' + db];
48161     if (typeof(cls) == 'undefined') {
48162         //Roo.log(node.getAttribute('data-block'));
48163         Roo.log("OOps missing block : " + 'Block' + db);
48164         return false;
48165     }
48166     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
48167     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
48168 };
48169
48170 /**
48171  * initalize all Elements from content that are 'blockable'
48172  * @static
48173  * @param the body element
48174  */
48175 Roo.htmleditor.Block.initAll = function(body, type)
48176 {
48177     if (typeof(type) == 'undefined') {
48178         var ia = Roo.htmleditor.Block.initAll;
48179         ia(body,'table');
48180         ia(body,'td');
48181         ia(body,'figure');
48182         return;
48183     }
48184     Roo.each(Roo.get(body).query(type), function(e) {
48185         Roo.htmleditor.Block.factory(e);    
48186     },this);
48187 };
48188 // question goes here... do we need to clear out this cache sometimes?
48189 // or show we make it relivant to the htmleditor.
48190 Roo.htmleditor.Block.cache = {};
48191
48192 Roo.htmleditor.Block.prototype = {
48193     
48194     node : false,
48195     
48196      // used by context menu
48197     friendly_name : 'Based Block',
48198     
48199     // text for button to delete this element
48200     deleteTitle : false,
48201     
48202     context : false,
48203     /**
48204      * Update a node with values from this object
48205      * @param {DomElement} node
48206      */
48207     updateElement : function(node)
48208     {
48209         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
48210     },
48211      /**
48212      * convert to plain HTML for calling insertAtCursor..
48213      */
48214     toHTML : function()
48215     {
48216         return Roo.DomHelper.markup(this.toObject());
48217     },
48218     /**
48219      * used by readEleemnt to extract data from a node
48220      * may need improving as it's pretty basic
48221      
48222      * @param {DomElement} node
48223      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
48224      * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
48225      * @param {String} style the style property - eg. text-align
48226      */
48227     getVal : function(node, tag, attr, style)
48228     {
48229         var n = node;
48230         if (tag !== true && n.tagName != tag.toUpperCase()) {
48231             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
48232             // but kiss for now.
48233             n = node.getElementsByTagName(tag).item(0);
48234         }
48235         if (!n) {
48236             return '';
48237         }
48238         if (attr === false) {
48239             return n;
48240         }
48241         if (attr == 'html') {
48242             return n.innerHTML;
48243         }
48244         if (attr == 'style') {
48245             return n.style[style]; 
48246         }
48247         
48248         return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
48249             
48250     },
48251     /**
48252      * create a DomHelper friendly object - for use with 
48253      * Roo.DomHelper.markup / overwrite / etc..
48254      * (override this)
48255      */
48256     toObject : function()
48257     {
48258         return {};
48259     },
48260       /**
48261      * Read a node that has a 'data-block' property - and extract the values from it.
48262      * @param {DomElement} node - the node
48263      */
48264     readElement : function(node)
48265     {
48266         
48267     } 
48268     
48269     
48270 };
48271
48272  
48273
48274 /**
48275  * @class Roo.htmleditor.BlockFigure
48276  * Block that has an image and a figcaption
48277  * @cfg {String} image_src the url for the image
48278  * @cfg {String} align (left|right) alignment for the block default left
48279  * @cfg {String} caption the text to appear below  (and in the alt tag)
48280  * @cfg {String} caption_display (block|none) display or not the caption
48281  * @cfg {String|number} image_width the width of the image number or %?
48282  * @cfg {String|number} image_height the height of the image number or %?
48283  * 
48284  * @constructor
48285  * Create a new Filter.
48286  * @param {Object} config Configuration options
48287  */
48288
48289 Roo.htmleditor.BlockFigure = function(cfg)
48290 {
48291     if (cfg.node) {
48292         this.readElement(cfg.node);
48293         this.updateElement(cfg.node);
48294     }
48295     Roo.apply(this, cfg);
48296 }
48297 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
48298  
48299     
48300     // setable values.
48301     image_src: '',
48302     align: 'center',
48303     caption : '',
48304     caption_display : 'block',
48305     width : '100%',
48306     cls : '',
48307     href: '',
48308     video_url : '',
48309     
48310     // margin: '2%', not used
48311     
48312     text_align: 'left', //   (left|right) alignment for the text caption default left. - not used at present
48313
48314     
48315     // used by context menu
48316     friendly_name : 'Image with caption',
48317     deleteTitle : "Delete Image and Caption",
48318     
48319     contextMenu : function(toolbar)
48320     {
48321         
48322         var block = function() {
48323             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48324         };
48325         
48326         
48327         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48328         
48329         var syncValue = toolbar.editorcore.syncValue;
48330         
48331         var fields = {};
48332         
48333         return [
48334              {
48335                 xtype : 'TextItem',
48336                 text : "Source: ",
48337                 xns : rooui.Toolbar  //Boostrap?
48338             },
48339             {
48340                 xtype : 'Button',
48341                 text: 'Change Image URL',
48342                  
48343                 listeners : {
48344                     click: function (btn, state)
48345                     {
48346                         var b = block();
48347                         
48348                         Roo.MessageBox.show({
48349                             title : "Image Source URL",
48350                             msg : "Enter the url for the image",
48351                             buttons: Roo.MessageBox.OKCANCEL,
48352                             fn: function(btn, val){
48353                                 if (btn != 'ok') {
48354                                     return;
48355                                 }
48356                                 b.image_src = val;
48357                                 b.updateElement();
48358                                 syncValue();
48359                                 toolbar.editorcore.onEditorEvent();
48360                             },
48361                             minWidth:250,
48362                             prompt:true,
48363                             //multiline: multiline,
48364                             modal : true,
48365                             value : b.image_src
48366                         });
48367                     }
48368                 },
48369                 xns : rooui.Toolbar
48370             },
48371          
48372             {
48373                 xtype : 'Button',
48374                 text: 'Change Link URL',
48375                  
48376                 listeners : {
48377                     click: function (btn, state)
48378                     {
48379                         var b = block();
48380                         
48381                         Roo.MessageBox.show({
48382                             title : "Link URL",
48383                             msg : "Enter the url for the link - leave blank to have no link",
48384                             buttons: Roo.MessageBox.OKCANCEL,
48385                             fn: function(btn, val){
48386                                 if (btn != 'ok') {
48387                                     return;
48388                                 }
48389                                 b.href = val;
48390                                 b.updateElement();
48391                                 syncValue();
48392                                 toolbar.editorcore.onEditorEvent();
48393                             },
48394                             minWidth:250,
48395                             prompt:true,
48396                             //multiline: multiline,
48397                             modal : true,
48398                             value : b.href
48399                         });
48400                     }
48401                 },
48402                 xns : rooui.Toolbar
48403             },
48404             {
48405                 xtype : 'Button',
48406                 text: 'Show Video URL',
48407                  
48408                 listeners : {
48409                     click: function (btn, state)
48410                     {
48411                         Roo.MessageBox.alert("Video URL",
48412                             block().video_url == '' ? 'This image is not linked ot a video' :
48413                                 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
48414                     }
48415                 },
48416                 xns : rooui.Toolbar
48417             },
48418             
48419             
48420             {
48421                 xtype : 'TextItem',
48422                 text : "Width: ",
48423                 xns : rooui.Toolbar  //Boostrap?
48424             },
48425             {
48426                 xtype : 'ComboBox',
48427                 allowBlank : false,
48428                 displayField : 'val',
48429                 editable : true,
48430                 listWidth : 100,
48431                 triggerAction : 'all',
48432                 typeAhead : true,
48433                 valueField : 'val',
48434                 width : 70,
48435                 name : 'width',
48436                 listeners : {
48437                     select : function (combo, r, index)
48438                     {
48439                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48440                         var b = block();
48441                         b.width = r.get('val');
48442                         b.updateElement();
48443                         syncValue();
48444                         toolbar.editorcore.onEditorEvent();
48445                     }
48446                 },
48447                 xns : rooui.form,
48448                 store : {
48449                     xtype : 'SimpleStore',
48450                     data : [
48451                         ['100%'],
48452                         ['80%'],
48453                         ['50%'],
48454                         ['20%'],
48455                         ['10%']
48456                     ],
48457                     fields : [ 'val'],
48458                     xns : Roo.data
48459                 }
48460             },
48461             {
48462                 xtype : 'TextItem',
48463                 text : "Align: ",
48464                 xns : rooui.Toolbar  //Boostrap?
48465             },
48466             {
48467                 xtype : 'ComboBox',
48468                 allowBlank : false,
48469                 displayField : 'val',
48470                 editable : true,
48471                 listWidth : 100,
48472                 triggerAction : 'all',
48473                 typeAhead : true,
48474                 valueField : 'val',
48475                 width : 70,
48476                 name : 'align',
48477                 listeners : {
48478                     select : function (combo, r, index)
48479                     {
48480                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48481                         var b = block();
48482                         b.align = r.get('val');
48483                         b.updateElement();
48484                         syncValue();
48485                         toolbar.editorcore.onEditorEvent();
48486                     }
48487                 },
48488                 xns : rooui.form,
48489                 store : {
48490                     xtype : 'SimpleStore',
48491                     data : [
48492                         ['left'],
48493                         ['right'],
48494                         ['center']
48495                     ],
48496                     fields : [ 'val'],
48497                     xns : Roo.data
48498                 }
48499             },
48500             
48501               
48502             {
48503                 xtype : 'Button',
48504                 text: 'Hide Caption',
48505                 name : 'caption_display',
48506                 pressed : false,
48507                 enableToggle : true,
48508                 setValue : function(v) {
48509                     // this trigger toggle.
48510                      
48511                     this.setText(v ? "Hide Caption" : "Show Caption");
48512                     this.setPressed(v != 'block');
48513                 },
48514                 listeners : {
48515                     toggle: function (btn, state)
48516                     {
48517                         var b  = block();
48518                         b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
48519                         this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
48520                         b.updateElement();
48521                         syncValue();
48522                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48523                         toolbar.editorcore.onEditorEvent();
48524                     }
48525                 },
48526                 xns : rooui.Toolbar
48527             }
48528         ];
48529         
48530     },
48531     /**
48532      * create a DomHelper friendly object - for use with
48533      * Roo.DomHelper.markup / overwrite / etc..
48534      */
48535     toObject : function()
48536     {
48537         var d = document.createElement('div');
48538         d.innerHTML = this.caption;
48539         
48540         var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0; 
48541         
48542         var iw = this.align == 'center' ? this.width : '100%';
48543         var img =   {
48544             tag : 'img',
48545             contenteditable : 'false',
48546             src : this.image_src,
48547             alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
48548             style: {
48549                 width : iw,
48550                 maxWidth : iw + ' !important', // this is not getting rendered?
48551                 margin : m  
48552                 
48553             }
48554         };
48555         /*
48556         '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
48557                     '<a href="{2}">' + 
48558                         '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' + 
48559                     '</a>' + 
48560                 '</div>',
48561         */
48562                 
48563         if (this.href.length > 0) {
48564             img = {
48565                 tag : 'a',
48566                 href: this.href,
48567                 contenteditable : 'true',
48568                 cn : [
48569                     img
48570                 ]
48571             };
48572         }
48573         
48574         
48575         if (this.video_url.length > 0) {
48576             img = {
48577                 tag : 'div',
48578                 cls : this.cls,
48579                 frameborder : 0,
48580                 allowfullscreen : true,
48581                 width : 420,  // these are for video tricks - that we replace the outer
48582                 height : 315,
48583                 src : this.video_url,
48584                 cn : [
48585                     img
48586                 ]
48587             };
48588         }
48589         // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
48590         var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
48591         
48592   
48593         var ret =   {
48594             tag: 'figure',
48595             'data-block' : 'Figure',
48596             'data-width' : this.width, 
48597             contenteditable : 'false',
48598             
48599             style : {
48600                 display: 'block',
48601                 float :  this.align ,
48602                 maxWidth :  this.align == 'center' ? '100% !important' : (this.width + ' !important'),
48603                 width : this.align == 'center' ? '100%' : this.width,
48604                 margin:  '0px',
48605                 padding: this.align == 'center' ? '0' : '0 10px' ,
48606                 textAlign : this.align   // seems to work for email..
48607                 
48608             },
48609            
48610             
48611             align : this.align,
48612             cn : [
48613                 img,
48614               
48615                 {
48616                     tag: 'figcaption',
48617                     'data-display' : this.caption_display,
48618                     style : {
48619                         textAlign : 'left',
48620                         fontSize : '16px',
48621                         lineHeight : '24px',
48622                         display : this.caption_display,
48623                         maxWidth : (this.align == 'center' ?  this.width : '100%' ) + ' !important',
48624                         margin: m,
48625                         width: this.align == 'center' ?  this.width : '100%' 
48626                     
48627                          
48628                     },
48629                     cls : this.cls.length > 0 ? (this.cls  + '-thumbnail' ) : '',
48630                     cn : [
48631                         {
48632                             tag: 'div',
48633                             style  : {
48634                                 marginTop : '16px',
48635                                 textAlign : 'left'
48636                             },
48637                             align: 'left',
48638                             cn : [
48639                                 {
48640                                     // we can not rely on yahoo syndication to use CSS elements - so have to use  '<i>' to encase stuff.
48641                                     tag : 'i',
48642                                     contenteditable : true,
48643                                     html : captionhtml
48644                                 }
48645                                 
48646                             ]
48647                         }
48648                         
48649                     ]
48650                     
48651                 }
48652             ]
48653         };
48654         return ret;
48655          
48656     },
48657     
48658     readElement : function(node)
48659     {
48660         // this should not really come from the link...
48661         this.video_url = this.getVal(node, 'div', 'src');
48662         this.cls = this.getVal(node, 'div', 'class');
48663         this.href = this.getVal(node, 'a', 'href');
48664         
48665         
48666         this.image_src = this.getVal(node, 'img', 'src');
48667          
48668         this.align = this.getVal(node, 'figure', 'align');
48669         var figcaption = this.getVal(node, 'figcaption', false);
48670         if (figcaption !== '') {
48671             this.caption = this.getVal(figcaption, 'i', 'html');
48672         }
48673         
48674
48675         this.caption_display = this.getVal(node, 'figcaption', 'data-display');
48676         //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
48677         this.width = this.getVal(node, true, 'data-width');
48678         //this.margin = this.getVal(node, 'figure', 'style', 'margin');
48679         
48680     },
48681     removeNode : function()
48682     {
48683         return this.node;
48684     }
48685     
48686   
48687    
48688      
48689     
48690     
48691     
48692     
48693 })
48694
48695  
48696
48697 /**
48698  * @class Roo.htmleditor.BlockTable
48699  * Block that manages a table
48700  * 
48701  * @constructor
48702  * Create a new Filter.
48703  * @param {Object} config Configuration options
48704  */
48705
48706 Roo.htmleditor.BlockTable = function(cfg)
48707 {
48708     if (cfg.node) {
48709         this.readElement(cfg.node);
48710         this.updateElement(cfg.node);
48711     }
48712     Roo.apply(this, cfg);
48713     if (!cfg.node) {
48714         this.rows = [];
48715         for(var r = 0; r < this.no_row; r++) {
48716             this.rows[r] = [];
48717             for(var c = 0; c < this.no_col; c++) {
48718                 this.rows[r][c] = this.emptyCell();
48719             }
48720         }
48721     }
48722     
48723     
48724 }
48725 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
48726  
48727     rows : false,
48728     no_col : 1,
48729     no_row : 1,
48730     
48731     
48732     width: '100%',
48733     
48734     // used by context menu
48735     friendly_name : 'Table',
48736     deleteTitle : 'Delete Table',
48737     // context menu is drawn once..
48738     
48739     contextMenu : function(toolbar)
48740     {
48741         
48742         var block = function() {
48743             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
48744         };
48745         
48746         
48747         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
48748         
48749         var syncValue = toolbar.editorcore.syncValue;
48750         
48751         var fields = {};
48752         
48753         return [
48754             {
48755                 xtype : 'TextItem',
48756                 text : "Width: ",
48757                 xns : rooui.Toolbar  //Boostrap?
48758             },
48759             {
48760                 xtype : 'ComboBox',
48761                 allowBlank : false,
48762                 displayField : 'val',
48763                 editable : true,
48764                 listWidth : 100,
48765                 triggerAction : 'all',
48766                 typeAhead : true,
48767                 valueField : 'val',
48768                 width : 100,
48769                 name : 'width',
48770                 listeners : {
48771                     select : function (combo, r, index)
48772                     {
48773                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48774                         var b = block();
48775                         b.width = r.get('val');
48776                         b.updateElement();
48777                         syncValue();
48778                         toolbar.editorcore.onEditorEvent();
48779                     }
48780                 },
48781                 xns : rooui.form,
48782                 store : {
48783                     xtype : 'SimpleStore',
48784                     data : [
48785                         ['100%'],
48786                         ['auto']
48787                     ],
48788                     fields : [ 'val'],
48789                     xns : Roo.data
48790                 }
48791             },
48792             // -------- Cols
48793             
48794             {
48795                 xtype : 'TextItem',
48796                 text : "Columns: ",
48797                 xns : rooui.Toolbar  //Boostrap?
48798             },
48799          
48800             {
48801                 xtype : 'Button',
48802                 text: '-',
48803                 listeners : {
48804                     click : function (_self, e)
48805                     {
48806                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48807                         block().removeColumn();
48808                         syncValue();
48809                         toolbar.editorcore.onEditorEvent();
48810                     }
48811                 },
48812                 xns : rooui.Toolbar
48813             },
48814             {
48815                 xtype : 'Button',
48816                 text: '+',
48817                 listeners : {
48818                     click : function (_self, e)
48819                     {
48820                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48821                         block().addColumn();
48822                         syncValue();
48823                         toolbar.editorcore.onEditorEvent();
48824                     }
48825                 },
48826                 xns : rooui.Toolbar
48827             },
48828             // -------- ROWS
48829             {
48830                 xtype : 'TextItem',
48831                 text : "Rows: ",
48832                 xns : rooui.Toolbar  //Boostrap?
48833             },
48834          
48835             {
48836                 xtype : 'Button',
48837                 text: '-',
48838                 listeners : {
48839                     click : function (_self, e)
48840                     {
48841                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
48842                         block().removeRow();
48843                         syncValue();
48844                         toolbar.editorcore.onEditorEvent();
48845                     }
48846                 },
48847                 xns : rooui.Toolbar
48848             },
48849             {
48850                 xtype : 'Button',
48851                 text: '+',
48852                 listeners : {
48853                     click : function (_self, e)
48854                     {
48855                         block().addRow();
48856                         syncValue();
48857                         toolbar.editorcore.onEditorEvent();
48858                     }
48859                 },
48860                 xns : rooui.Toolbar
48861             },
48862             // -------- ROWS
48863             {
48864                 xtype : 'Button',
48865                 text: 'Reset Column Widths',
48866                 listeners : {
48867                     
48868                     click : function (_self, e)
48869                     {
48870                         block().resetWidths();
48871                         syncValue();
48872                         toolbar.editorcore.onEditorEvent();
48873                     }
48874                 },
48875                 xns : rooui.Toolbar
48876             } 
48877             
48878             
48879             
48880         ];
48881         
48882     },
48883     
48884     
48885   /**
48886      * create a DomHelper friendly object - for use with
48887      * Roo.DomHelper.markup / overwrite / etc..
48888      * ?? should it be called with option to hide all editing features?
48889      */
48890     toObject : function()
48891     {
48892         
48893         var ret = {
48894             tag : 'table',
48895             contenteditable : 'false', // this stops cell selection from picking the table.
48896             'data-block' : 'Table',
48897             style : {
48898                 width:  this.width,
48899                 border : 'solid 1px #000', // ??? hard coded?
48900                 'border-collapse' : 'collapse' 
48901             },
48902             cn : [
48903                 { tag : 'tbody' , cn : [] }
48904             ]
48905         };
48906         
48907         // do we have a head = not really 
48908         var ncols = 0;
48909         Roo.each(this.rows, function( row ) {
48910             var tr = {
48911                 tag: 'tr',
48912                 style : {
48913                     margin: '6px',
48914                     border : 'solid 1px #000',
48915                     textAlign : 'left' 
48916                 },
48917                 cn : [ ]
48918             };
48919             
48920             ret.cn[0].cn.push(tr);
48921             // does the row have any properties? ?? height?
48922             var nc = 0;
48923             Roo.each(row, function( cell ) {
48924                 
48925                 var td = {
48926                     tag : 'td',
48927                     contenteditable :  'true',
48928                     'data-block' : 'Td',
48929                     html : cell.html,
48930                     style : cell.style
48931                 };
48932                 if (cell.colspan > 1) {
48933                     td.colspan = cell.colspan ;
48934                     nc += cell.colspan;
48935                 } else {
48936                     nc++;
48937                 }
48938                 if (cell.rowspan > 1) {
48939                     td.rowspan = cell.rowspan ;
48940                 }
48941                 
48942                 
48943                 // widths ?
48944                 tr.cn.push(td);
48945                     
48946                 
48947             }, this);
48948             ncols = Math.max(nc, ncols);
48949             
48950             
48951         }, this);
48952         // add the header row..
48953         
48954         ncols++;
48955          
48956         
48957         return ret;
48958          
48959     },
48960     
48961     readElement : function(node)
48962     {
48963         node  = node ? node : this.node ;
48964         this.width = this.getVal(node, true, 'style', 'width') || '100%';
48965         
48966         this.rows = [];
48967         this.no_row = 0;
48968         var trs = Array.from(node.rows);
48969         trs.forEach(function(tr) {
48970             var row =  [];
48971             this.rows.push(row);
48972             
48973             this.no_row++;
48974             var no_column = 0;
48975             Array.from(tr.cells).forEach(function(td) {
48976                 
48977                 var add = {
48978                     colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
48979                     rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
48980                     style : td.hasAttribute('style') ? td.getAttribute('style') : '',
48981                     html : td.innerHTML
48982                 };
48983                 no_column += add.colspan;
48984                      
48985                 
48986                 row.push(add);
48987                 
48988                 
48989             },this);
48990             this.no_col = Math.max(this.no_col, no_column);
48991             
48992             
48993         },this);
48994         
48995         
48996     },
48997     normalizeRows: function()
48998     {
48999         var ret= [];
49000         var rid = -1;
49001         this.rows.forEach(function(row) {
49002             rid++;
49003             ret[rid] = [];
49004             row = this.normalizeRow(row);
49005             var cid = 0;
49006             row.forEach(function(c) {
49007                 while (typeof(ret[rid][cid]) != 'undefined') {
49008                     cid++;
49009                 }
49010                 if (typeof(ret[rid]) == 'undefined') {
49011                     ret[rid] = [];
49012                 }
49013                 ret[rid][cid] = c;
49014                 c.row = rid;
49015                 c.col = cid;
49016                 if (c.rowspan < 2) {
49017                     return;
49018                 }
49019                 
49020                 for(var i = 1 ;i < c.rowspan; i++) {
49021                     if (typeof(ret[rid+i]) == 'undefined') {
49022                         ret[rid+i] = [];
49023                     }
49024                     ret[rid+i][cid] = c;
49025                 }
49026             });
49027         }, this);
49028         return ret;
49029     
49030     },
49031     
49032     normalizeRow: function(row)
49033     {
49034         var ret= [];
49035         row.forEach(function(c) {
49036             if (c.colspan < 2) {
49037                 ret.push(c);
49038                 return;
49039             }
49040             for(var i =0 ;i < c.colspan; i++) {
49041                 ret.push(c);
49042             }
49043         });
49044         return ret;
49045     
49046     },
49047     
49048     deleteColumn : function(sel)
49049     {
49050         if (!sel || sel.type != 'col') {
49051             return;
49052         }
49053         if (this.no_col < 2) {
49054             return;
49055         }
49056         
49057         this.rows.forEach(function(row) {
49058             var cols = this.normalizeRow(row);
49059             var col = cols[sel.col];
49060             if (col.colspan > 1) {
49061                 col.colspan --;
49062             } else {
49063                 row.remove(col);
49064             }
49065             
49066         }, this);
49067         this.no_col--;
49068         
49069     },
49070     removeColumn : function()
49071     {
49072         this.deleteColumn({
49073             type: 'col',
49074             col : this.no_col-1
49075         });
49076         this.updateElement();
49077     },
49078     
49079      
49080     addColumn : function()
49081     {
49082         
49083         this.rows.forEach(function(row) {
49084             row.push(this.emptyCell());
49085            
49086         }, this);
49087         this.updateElement();
49088     },
49089     
49090     deleteRow : function(sel)
49091     {
49092         if (!sel || sel.type != 'row') {
49093             return;
49094         }
49095         
49096         if (this.no_row < 2) {
49097             return;
49098         }
49099         
49100         var rows = this.normalizeRows();
49101         
49102         
49103         rows[sel.row].forEach(function(col) {
49104             if (col.rowspan > 1) {
49105                 col.rowspan--;
49106             } else {
49107                 col.remove = 1; // flage it as removed.
49108             }
49109             
49110         }, this);
49111         var newrows = [];
49112         this.rows.forEach(function(row) {
49113             newrow = [];
49114             row.forEach(function(c) {
49115                 if (typeof(c.remove) == 'undefined') {
49116                     newrow.push(c);
49117                 }
49118                 
49119             });
49120             if (newrow.length > 0) {
49121                 newrows.push(row);
49122             }
49123         });
49124         this.rows =  newrows;
49125         
49126         
49127         
49128         this.no_row--;
49129         this.updateElement();
49130         
49131     },
49132     removeRow : function()
49133     {
49134         this.deleteRow({
49135             type: 'row',
49136             row : this.no_row-1
49137         });
49138         
49139     },
49140     
49141      
49142     addRow : function()
49143     {
49144         
49145         var row = [];
49146         for (var i = 0; i < this.no_col; i++ ) {
49147             
49148             row.push(this.emptyCell());
49149            
49150         }
49151         this.rows.push(row);
49152         this.updateElement();
49153         
49154     },
49155      
49156     // the default cell object... at present...
49157     emptyCell : function() {
49158         return (new Roo.htmleditor.BlockTd({})).toObject();
49159         
49160      
49161     },
49162     
49163     removeNode : function()
49164     {
49165         return this.node;
49166     },
49167     
49168     
49169     
49170     resetWidths : function()
49171     {
49172         Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
49173             var nn = Roo.htmleditor.Block.factory(n);
49174             nn.width = '';
49175             nn.updateElement(n);
49176         });
49177     }
49178     
49179     
49180     
49181     
49182 })
49183
49184 /**
49185  *
49186  * editing a TD?
49187  *
49188  * since selections really work on the table cell, then editing really should work from there
49189  *
49190  * The original plan was to support merging etc... - but that may not be needed yet..
49191  *
49192  * So this simple version will support:
49193  *   add/remove cols
49194  *   adjust the width +/-
49195  *   reset the width...
49196  *   
49197  *
49198  */
49199
49200
49201  
49202
49203 /**
49204  * @class Roo.htmleditor.BlockTable
49205  * Block that manages a table
49206  * 
49207  * @constructor
49208  * Create a new Filter.
49209  * @param {Object} config Configuration options
49210  */
49211
49212 Roo.htmleditor.BlockTd = function(cfg)
49213 {
49214     if (cfg.node) {
49215         this.readElement(cfg.node);
49216         this.updateElement(cfg.node);
49217     }
49218     Roo.apply(this, cfg);
49219      
49220     
49221     
49222 }
49223 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
49224  
49225     node : false,
49226     
49227     width: '',
49228     textAlign : 'left',
49229     valign : 'top',
49230     
49231     colspan : 1,
49232     rowspan : 1,
49233     
49234     
49235     // used by context menu
49236     friendly_name : 'Table Cell',
49237     deleteTitle : false, // use our customer delete
49238     
49239     // context menu is drawn once..
49240     
49241     contextMenu : function(toolbar)
49242     {
49243         
49244         var cell = function() {
49245             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
49246         };
49247         
49248         var table = function() {
49249             return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
49250         };
49251         
49252         var lr = false;
49253         var saveSel = function()
49254         {
49255             lr = toolbar.editorcore.getSelection().getRangeAt(0);
49256         }
49257         var restoreSel = function()
49258         {
49259             if (lr) {
49260                 (function() {
49261                     toolbar.editorcore.focus();
49262                     var cr = toolbar.editorcore.getSelection();
49263                     cr.removeAllRanges();
49264                     cr.addRange(lr);
49265                     toolbar.editorcore.onEditorEvent();
49266                 }).defer(10, this);
49267                 
49268                 
49269             }
49270         }
49271         
49272         var rooui =  typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
49273         
49274         var syncValue = toolbar.editorcore.syncValue;
49275         
49276         var fields = {};
49277         
49278         return [
49279             {
49280                 xtype : 'Button',
49281                 text : 'Edit Table',
49282                 listeners : {
49283                     click : function() {
49284                         var t = toolbar.tb.selectedNode.closest('table');
49285                         toolbar.editorcore.selectNode(t);
49286                         toolbar.editorcore.onEditorEvent();                        
49287                     }
49288                 }
49289                 
49290             },
49291               
49292            
49293              
49294             {
49295                 xtype : 'TextItem',
49296                 text : "Column Width: ",
49297                  xns : rooui.Toolbar 
49298                
49299             },
49300             {
49301                 xtype : 'Button',
49302                 text: '-',
49303                 listeners : {
49304                     click : function (_self, e)
49305                     {
49306                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49307                         cell().shrinkColumn();
49308                         syncValue();
49309                          toolbar.editorcore.onEditorEvent();
49310                     }
49311                 },
49312                 xns : rooui.Toolbar
49313             },
49314             {
49315                 xtype : 'Button',
49316                 text: '+',
49317                 listeners : {
49318                     click : function (_self, e)
49319                     {
49320                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49321                         cell().growColumn();
49322                         syncValue();
49323                         toolbar.editorcore.onEditorEvent();
49324                     }
49325                 },
49326                 xns : rooui.Toolbar
49327             },
49328             
49329             {
49330                 xtype : 'TextItem',
49331                 text : "Vertical Align: ",
49332                 xns : rooui.Toolbar  //Boostrap?
49333             },
49334             {
49335                 xtype : 'ComboBox',
49336                 allowBlank : false,
49337                 displayField : 'val',
49338                 editable : true,
49339                 listWidth : 100,
49340                 triggerAction : 'all',
49341                 typeAhead : true,
49342                 valueField : 'val',
49343                 width : 100,
49344                 name : 'valign',
49345                 listeners : {
49346                     select : function (combo, r, index)
49347                     {
49348                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49349                         var b = cell();
49350                         b.valign = r.get('val');
49351                         b.updateElement();
49352                         syncValue();
49353                         toolbar.editorcore.onEditorEvent();
49354                     }
49355                 },
49356                 xns : rooui.form,
49357                 store : {
49358                     xtype : 'SimpleStore',
49359                     data : [
49360                         ['top'],
49361                         ['middle'],
49362                         ['bottom'] // there are afew more... 
49363                     ],
49364                     fields : [ 'val'],
49365                     xns : Roo.data
49366                 }
49367             },
49368             
49369             {
49370                 xtype : 'TextItem',
49371                 text : "Merge Cells: ",
49372                  xns : rooui.Toolbar 
49373                
49374             },
49375             
49376             
49377             {
49378                 xtype : 'Button',
49379                 text: 'Right',
49380                 listeners : {
49381                     click : function (_self, e)
49382                     {
49383                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49384                         cell().mergeRight();
49385                         //block().growColumn();
49386                         syncValue();
49387                         toolbar.editorcore.onEditorEvent();
49388                     }
49389                 },
49390                 xns : rooui.Toolbar
49391             },
49392              
49393             {
49394                 xtype : 'Button',
49395                 text: 'Below',
49396                 listeners : {
49397                     click : function (_self, e)
49398                     {
49399                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49400                         cell().mergeBelow();
49401                         //block().growColumn();
49402                         syncValue();
49403                         toolbar.editorcore.onEditorEvent();
49404                     }
49405                 },
49406                 xns : rooui.Toolbar
49407             },
49408             {
49409                 xtype : 'TextItem',
49410                 text : "| ",
49411                  xns : rooui.Toolbar 
49412                
49413             },
49414             
49415             {
49416                 xtype : 'Button',
49417                 text: 'Split',
49418                 listeners : {
49419                     click : function (_self, e)
49420                     {
49421                         //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49422                         cell().split();
49423                         syncValue();
49424                         toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
49425                         toolbar.editorcore.onEditorEvent();
49426                                              
49427                     }
49428                 },
49429                 xns : rooui.Toolbar
49430             },
49431             {
49432                 xtype : 'Fill',
49433                 xns : rooui.Toolbar 
49434                
49435             },
49436         
49437           
49438             {
49439                 xtype : 'Button',
49440                 text: 'Delete',
49441                  
49442                 xns : rooui.Toolbar,
49443                 menu : {
49444                     xtype : 'Menu',
49445                     xns : rooui.menu,
49446                     items : [
49447                         {
49448                             xtype : 'Item',
49449                             html: 'Column',
49450                             listeners : {
49451                                 click : function (_self, e)
49452                                 {
49453                                     var t = table();
49454                                     
49455                                     cell().deleteColumn();
49456                                     syncValue();
49457                                     toolbar.editorcore.selectNode(t.node);
49458                                     toolbar.editorcore.onEditorEvent();   
49459                                 }
49460                             },
49461                             xns : rooui.menu
49462                         },
49463                         {
49464                             xtype : 'Item',
49465                             html: 'Row',
49466                             listeners : {
49467                                 click : function (_self, e)
49468                                 {
49469                                     var t = table();
49470                                     cell().deleteRow();
49471                                     syncValue();
49472                                     
49473                                     toolbar.editorcore.selectNode(t.node);
49474                                     toolbar.editorcore.onEditorEvent();   
49475                                                          
49476                                 }
49477                             },
49478                             xns : rooui.menu
49479                         },
49480                        {
49481                             xtype : 'Separator',
49482                             xns : rooui.menu
49483                         },
49484                         {
49485                             xtype : 'Item',
49486                             html: 'Table',
49487                             listeners : {
49488                                 click : function (_self, e)
49489                                 {
49490                                     var t = table();
49491                                     var nn = t.node.nextSibling || t.node.previousSibling;
49492                                     t.node.parentNode.removeChild(t.node);
49493                                     if (nn) { 
49494                                         toolbar.editorcore.selectNode(nn, true);
49495                                     }
49496                                     toolbar.editorcore.onEditorEvent();   
49497                                                          
49498                                 }
49499                             },
49500                             xns : rooui.menu
49501                         }
49502                     ]
49503                 }
49504             }
49505             
49506             // align... << fixme
49507             
49508         ];
49509         
49510     },
49511     
49512     
49513   /**
49514      * create a DomHelper friendly object - for use with
49515      * Roo.DomHelper.markup / overwrite / etc..
49516      * ?? should it be called with option to hide all editing features?
49517      */
49518  /**
49519      * create a DomHelper friendly object - for use with
49520      * Roo.DomHelper.markup / overwrite / etc..
49521      * ?? should it be called with option to hide all editing features?
49522      */
49523     toObject : function()
49524     {
49525         var ret = {
49526             tag : 'td',
49527             contenteditable : 'true', // this stops cell selection from picking the table.
49528             'data-block' : 'Td',
49529             valign : this.valign,
49530             style : {  
49531                 'text-align' :  this.textAlign,
49532                 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
49533                 'border-collapse' : 'collapse',
49534                 padding : '6px', // 8 for desktop / 4 for mobile
49535                 'vertical-align': this.valign
49536             },
49537             html : this.html
49538         };
49539         if (this.width != '') {
49540             ret.width = this.width;
49541             ret.style.width = this.width;
49542         }
49543         
49544         
49545         if (this.colspan > 1) {
49546             ret.colspan = this.colspan ;
49547         } 
49548         if (this.rowspan > 1) {
49549             ret.rowspan = this.rowspan ;
49550         }
49551         
49552            
49553         
49554         return ret;
49555          
49556     },
49557     
49558     readElement : function(node)
49559     {
49560         node  = node ? node : this.node ;
49561         this.width = node.style.width;
49562         this.colspan = Math.max(1,1*node.getAttribute('colspan'));
49563         this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
49564         this.html = node.innerHTML;
49565         if (node.style.textAlign != '') {
49566             this.textAlign = node.style.textAlign;
49567         }
49568         
49569         
49570     },
49571      
49572     // the default cell object... at present...
49573     emptyCell : function() {
49574         return {
49575             colspan :  1,
49576             rowspan :  1,
49577             textAlign : 'left',
49578             html : "&nbsp;" // is this going to be editable now?
49579         };
49580      
49581     },
49582     
49583     removeNode : function()
49584     {
49585         return this.node.closest('table');
49586          
49587     },
49588     
49589     cellData : false,
49590     
49591     colWidths : false,
49592     
49593     toTableArray  : function()
49594     {
49595         var ret = [];
49596         var tab = this.node.closest('tr').closest('table');
49597         Array.from(tab.rows).forEach(function(r, ri){
49598             ret[ri] = [];
49599         });
49600         var rn = 0;
49601         this.colWidths = [];
49602         var all_auto = true;
49603         Array.from(tab.rows).forEach(function(r, ri){
49604             
49605             var cn = 0;
49606             Array.from(r.cells).forEach(function(ce, ci){
49607                 var c =  {
49608                     cell : ce,
49609                     row : rn,
49610                     col: cn,
49611                     colspan : ce.colSpan,
49612                     rowspan : ce.rowSpan
49613                 };
49614                 if (ce.isEqualNode(this.node)) {
49615                     this.cellData = c;
49616                 }
49617                 // if we have been filled up by a row?
49618                 if (typeof(ret[rn][cn]) != 'undefined') {
49619                     while(typeof(ret[rn][cn]) != 'undefined') {
49620                         cn++;
49621                     }
49622                     c.col = cn;
49623                 }
49624                 
49625                 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
49626                     this.colWidths[cn] =   ce.style.width;
49627                     if (this.colWidths[cn] != '') {
49628                         all_auto = false;
49629                     }
49630                 }
49631                 
49632                 
49633                 if (c.colspan < 2 && c.rowspan < 2 ) {
49634                     ret[rn][cn] = c;
49635                     cn++;
49636                     return;
49637                 }
49638                 for(var j = 0; j < c.rowspan; j++) {
49639                     if (typeof(ret[rn+j]) == 'undefined') {
49640                         continue; // we have a problem..
49641                     }
49642                     ret[rn+j][cn] = c;
49643                     for(var i = 0; i < c.colspan; i++) {
49644                         ret[rn+j][cn+i] = c;
49645                     }
49646                 }
49647                 
49648                 cn += c.colspan;
49649             }, this);
49650             rn++;
49651         }, this);
49652         
49653         // initalize widths.?
49654         // either all widths or no widths..
49655         if (all_auto) {
49656             this.colWidths[0] = false; // no widths flag.
49657         }
49658         
49659         
49660         return ret;
49661         
49662     },
49663     
49664     
49665     
49666     
49667     mergeRight: function()
49668     {
49669          
49670         // get the contents of the next cell along..
49671         var tr = this.node.closest('tr');
49672         var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
49673         if (i >= tr.childNodes.length - 1) {
49674             return; // no cells on right to merge with.
49675         }
49676         var table = this.toTableArray();
49677         
49678         if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
49679             return; // nothing right?
49680         }
49681         var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
49682         // right cell - must be same rowspan and on the same row.
49683         if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
49684             return; // right hand side is not same rowspan.
49685         }
49686         
49687         
49688         
49689         this.node.innerHTML += ' ' + rc.cell.innerHTML;
49690         tr.removeChild(rc.cell);
49691         this.colspan += rc.colspan;
49692         this.node.setAttribute('colspan', this.colspan);
49693
49694         var table = this.toTableArray();
49695         this.normalizeWidths(table);
49696         this.updateWidths(table);
49697     },
49698     
49699     
49700     mergeBelow : function()
49701     {
49702         var table = this.toTableArray();
49703         if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
49704             return; // no row below
49705         }
49706         if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
49707             return; // nothing right?
49708         }
49709         var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
49710         
49711         if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
49712             return; // right hand side is not same rowspan.
49713         }
49714         this.node.innerHTML =  this.node.innerHTML + rc.cell.innerHTML ;
49715         rc.cell.parentNode.removeChild(rc.cell);
49716         this.rowspan += rc.rowspan;
49717         this.node.setAttribute('rowspan', this.rowspan);
49718     },
49719     
49720     split: function()
49721     {
49722         if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
49723             return;
49724         }
49725         var table = this.toTableArray();
49726         var cd = this.cellData;
49727         this.rowspan = 1;
49728         this.colspan = 1;
49729         
49730         for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
49731              
49732             
49733             for(var c = cd.col; c < cd.col + cd.colspan; c++) {
49734                 if (r == cd.row && c == cd.col) {
49735                     this.node.removeAttribute('rowspan');
49736                     this.node.removeAttribute('colspan');
49737                 }
49738                  
49739                 var ntd = this.node.cloneNode(); // which col/row should be 0..
49740                 ntd.removeAttribute('id'); 
49741                 ntd.style.width  = this.colWidths[c];
49742                 ntd.innerHTML = '';
49743                 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1   };
49744             }
49745             
49746         }
49747         this.redrawAllCells(table);
49748         
49749     },
49750     
49751     
49752     
49753     redrawAllCells: function(table)
49754     {
49755         
49756          
49757         var tab = this.node.closest('tr').closest('table');
49758         var ctr = tab.rows[0].parentNode;
49759         Array.from(tab.rows).forEach(function(r, ri){
49760             
49761             Array.from(r.cells).forEach(function(ce, ci){
49762                 ce.parentNode.removeChild(ce);
49763             });
49764             r.parentNode.removeChild(r);
49765         });
49766         for(var r = 0 ; r < table.length; r++) {
49767             var re = tab.rows[r];
49768             
49769             var re = tab.ownerDocument.createElement('tr');
49770             ctr.appendChild(re);
49771             for(var c = 0 ; c < table[r].length; c++) {
49772                 if (table[r][c].cell === false) {
49773                     continue;
49774                 }
49775                 
49776                 re.appendChild(table[r][c].cell);
49777                  
49778                 table[r][c].cell = false;
49779             }
49780         }
49781         
49782     },
49783     updateWidths : function(table)
49784     {
49785         for(var r = 0 ; r < table.length; r++) {
49786            
49787             for(var c = 0 ; c < table[r].length; c++) {
49788                 if (table[r][c].cell === false) {
49789                     continue;
49790                 }
49791                 
49792                 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
49793                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49794                     el.width = Math.floor(this.colWidths[c])  +'%';
49795                     el.updateElement(el.node);
49796                 }
49797                 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
49798                     var el = Roo.htmleditor.Block.factory(table[r][c].cell);
49799                     var width = 0;
49800                     for(var i = 0; i < table[r][c].colspan; i ++) {
49801                         width += Math.floor(this.colWidths[c + i]);
49802                     }
49803                     el.width = width  +'%';
49804                     el.updateElement(el.node);
49805                 }
49806                 table[r][c].cell = false; // done
49807             }
49808         }
49809     },
49810     normalizeWidths : function(table)
49811     {
49812         if (this.colWidths[0] === false) {
49813             var nw = 100.0 / this.colWidths.length;
49814             this.colWidths.forEach(function(w,i) {
49815                 this.colWidths[i] = nw;
49816             },this);
49817             return;
49818         }
49819     
49820         var t = 0, missing = [];
49821         
49822         this.colWidths.forEach(function(w,i) {
49823             //if you mix % and
49824             this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
49825             var add =  this.colWidths[i];
49826             if (add > 0) {
49827                 t+=add;
49828                 return;
49829             }
49830             missing.push(i);
49831             
49832             
49833         },this);
49834         var nc = this.colWidths.length;
49835         if (missing.length) {
49836             var mult = (nc - missing.length) / (1.0 * nc);
49837             var t = mult * t;
49838             var ew = (100 -t) / (1.0 * missing.length);
49839             this.colWidths.forEach(function(w,i) {
49840                 if (w > 0) {
49841                     this.colWidths[i] = w * mult;
49842                     return;
49843                 }
49844                 
49845                 this.colWidths[i] = ew;
49846             }, this);
49847             // have to make up numbers..
49848              
49849         }
49850         // now we should have all the widths..
49851         
49852     
49853     },
49854     
49855     shrinkColumn : function()
49856     {
49857         var table = this.toTableArray();
49858         this.normalizeWidths(table);
49859         var col = this.cellData.col;
49860         var nw = this.colWidths[col] * 0.8;
49861         if (nw < 5) {
49862             return;
49863         }
49864         var otherAdd = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
49865         this.colWidths.forEach(function(w,i) {
49866             if (i == col) {
49867                  this.colWidths[i] = nw;
49868                 return;
49869             }
49870             this.colWidths[i] += otherAdd
49871         }, this);
49872         this.updateWidths(table);
49873          
49874     },
49875     growColumn : function()
49876     {
49877         var table = this.toTableArray();
49878         this.normalizeWidths(table);
49879         var col = this.cellData.col;
49880         var nw = this.colWidths[col] * 1.2;
49881         if (nw > 90) {
49882             return;
49883         }
49884         var otherSub = (this.colWidths[col]  * 0.2) / (this.colWidths.length -1);
49885         this.colWidths.forEach(function(w,i) {
49886             if (i == col) {
49887                 this.colWidths[i] = nw;
49888                 return;
49889             }
49890             this.colWidths[i] -= otherSub
49891         }, this);
49892         this.updateWidths(table);
49893          
49894     },
49895     deleteRow : function()
49896     {
49897         // delete this rows 'tr'
49898         // if any of the cells in this row have a rowspan > 1 && row!= this row..
49899         // then reduce the rowspan.
49900         var table = this.toTableArray();
49901         // this.cellData.row;
49902         for (var i =0;i< table[this.cellData.row].length ; i++) {
49903             var c = table[this.cellData.row][i];
49904             if (c.row != this.cellData.row) {
49905                 
49906                 c.rowspan--;
49907                 c.cell.setAttribute('rowspan', c.rowspan);
49908                 continue;
49909             }
49910             if (c.rowspan > 1) {
49911                 c.rowspan--;
49912                 c.cell.setAttribute('rowspan', c.rowspan);
49913             }
49914         }
49915         table.splice(this.cellData.row,1);
49916         this.redrawAllCells(table);
49917         
49918     },
49919     deleteColumn : function()
49920     {
49921         var table = this.toTableArray();
49922         
49923         for (var i =0;i< table.length ; i++) {
49924             var c = table[i][this.cellData.col];
49925             if (c.col != this.cellData.col) {
49926                 table[i][this.cellData.col].colspan--;
49927             } else if (c.colspan > 1) {
49928                 c.colspan--;
49929                 c.cell.setAttribute('colspan', c.colspan);
49930             }
49931             table[i].splice(this.cellData.col,1);
49932         }
49933         
49934         this.redrawAllCells(table);
49935     }
49936     
49937     
49938     
49939     
49940 })
49941
49942 //<script type="text/javascript">
49943
49944 /*
49945  * Based  Ext JS Library 1.1.1
49946  * Copyright(c) 2006-2007, Ext JS, LLC.
49947  * LGPL
49948  *
49949  */
49950  
49951 /**
49952  * @class Roo.HtmlEditorCore
49953  * @extends Roo.Component
49954  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
49955  *
49956  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
49957  */
49958
49959 Roo.HtmlEditorCore = function(config){
49960     
49961     
49962     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
49963     
49964     
49965     this.addEvents({
49966         /**
49967          * @event initialize
49968          * Fires when the editor is fully initialized (including the iframe)
49969          * @param {Roo.HtmlEditorCore} this
49970          */
49971         initialize: true,
49972         /**
49973          * @event activate
49974          * Fires when the editor is first receives the focus. Any insertion must wait
49975          * until after this event.
49976          * @param {Roo.HtmlEditorCore} this
49977          */
49978         activate: true,
49979          /**
49980          * @event beforesync
49981          * Fires before the textarea is updated with content from the editor iframe. Return false
49982          * to cancel the sync.
49983          * @param {Roo.HtmlEditorCore} this
49984          * @param {String} html
49985          */
49986         beforesync: true,
49987          /**
49988          * @event beforepush
49989          * Fires before the iframe editor is updated with content from the textarea. Return false
49990          * to cancel the push.
49991          * @param {Roo.HtmlEditorCore} this
49992          * @param {String} html
49993          */
49994         beforepush: true,
49995          /**
49996          * @event sync
49997          * Fires when the textarea is updated with content from the editor iframe.
49998          * @param {Roo.HtmlEditorCore} this
49999          * @param {String} html
50000          */
50001         sync: true,
50002          /**
50003          * @event push
50004          * Fires when the iframe editor is updated with content from the textarea.
50005          * @param {Roo.HtmlEditorCore} this
50006          * @param {String} html
50007          */
50008         push: true,
50009         
50010         /**
50011          * @event editorevent
50012          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
50013          * @param {Roo.HtmlEditorCore} this
50014          */
50015         editorevent: true 
50016          
50017         
50018     });
50019     
50020     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
50021     
50022     // defaults : white / black...
50023     this.applyBlacklists();
50024     
50025     
50026     
50027 };
50028
50029
50030 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
50031
50032
50033      /**
50034      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
50035      */
50036     
50037     owner : false,
50038     
50039      /**
50040      * @cfg {String} css styling for resizing. (used on bootstrap only)
50041      */
50042     resize : false,
50043      /**
50044      * @cfg {Number} height (in pixels)
50045      */   
50046     height: 300,
50047    /**
50048      * @cfg {Number} width (in pixels)
50049      */   
50050     width: 500,
50051      /**
50052      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
50053      *         if you are doing an email editor, this probably needs disabling, it's designed
50054      */
50055     autoClean: true,
50056     
50057     /**
50058      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
50059      */
50060     enableBlocks : true,
50061     /**
50062      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
50063      * 
50064      */
50065     stylesheets: false,
50066      /**
50067      * @cfg {String} language default en - language of text (usefull for rtl languages)
50068      * 
50069      */
50070     language: 'en',
50071     
50072     /**
50073      * @cfg {boolean} allowComments - default false - allow comments in HTML source
50074      *          - by default they are stripped - if you are editing email you may need this.
50075      */
50076     allowComments: false,
50077     // id of frame..
50078     frameId: false,
50079     
50080     // private properties
50081     validationEvent : false,
50082     deferHeight: true,
50083     initialized : false,
50084     activated : false,
50085     sourceEditMode : false,
50086     onFocus : Roo.emptyFn,
50087     iframePad:3,
50088     hideMode:'offsets',
50089     
50090     clearUp: true,
50091     
50092     // blacklist + whitelisted elements..
50093     black: false,
50094     white: false,
50095      
50096     bodyCls : '',
50097
50098     
50099     undoManager : false,
50100     /**
50101      * Protected method that will not generally be called directly. It
50102      * is called when the editor initializes the iframe with HTML contents. Override this method if you
50103      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
50104      */
50105     getDocMarkup : function(){
50106         // body styles..
50107         var st = '';
50108         
50109         // inherit styels from page...?? 
50110         if (this.stylesheets === false) {
50111             
50112             Roo.get(document.head).select('style').each(function(node) {
50113                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
50114             });
50115             
50116             Roo.get(document.head).select('link').each(function(node) { 
50117                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
50118             });
50119             
50120         } else if (!this.stylesheets.length) {
50121                 // simple..
50122                 st = '<style type="text/css">' +
50123                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
50124                    '</style>';
50125         } else {
50126             for (var i in this.stylesheets) {
50127                 if (typeof(this.stylesheets[i]) != 'string') {
50128                     continue;
50129                 }
50130                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
50131             }
50132             
50133         }
50134         
50135         st +=  '<style type="text/css">' +
50136             'IMG { cursor: pointer } ' +
50137         '</style>';
50138         
50139         st += '<meta name="google" content="notranslate">';
50140         
50141         var cls = 'notranslate roo-htmleditor-body';
50142         
50143         if(this.bodyCls.length){
50144             cls += ' ' + this.bodyCls;
50145         }
50146         
50147         return '<html  class="notranslate" translate="no"><head>' + st  +
50148             //<style type="text/css">' +
50149             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
50150             //'</style>' +
50151             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
50152     },
50153
50154     // private
50155     onRender : function(ct, position)
50156     {
50157         var _t = this;
50158         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
50159         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
50160         
50161         
50162         this.el.dom.style.border = '0 none';
50163         this.el.dom.setAttribute('tabIndex', -1);
50164         this.el.addClass('x-hidden hide');
50165         
50166         
50167         
50168         if(Roo.isIE){ // fix IE 1px bogus margin
50169             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
50170         }
50171        
50172         
50173         this.frameId = Roo.id();
50174         
50175         var ifcfg = {
50176             tag: 'iframe',
50177             cls: 'form-control', // bootstrap..
50178             id: this.frameId,
50179             name: this.frameId,
50180             frameBorder : 'no',
50181             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
50182         };
50183         if (this.resize) {
50184             ifcfg.style = { resize : this.resize };
50185         }
50186         
50187         var iframe = this.owner.wrap.createChild(ifcfg, this.el); 
50188         
50189         
50190         this.iframe = iframe.dom;
50191
50192         this.assignDocWin();
50193         
50194         this.doc.designMode = 'on';
50195        
50196         this.doc.open();
50197         this.doc.write(this.getDocMarkup());
50198         this.doc.close();
50199
50200         
50201         var task = { // must defer to wait for browser to be ready
50202             run : function(){
50203                 //console.log("run task?" + this.doc.readyState);
50204                 this.assignDocWin();
50205                 if(this.doc.body || this.doc.readyState == 'complete'){
50206                     try {
50207                         this.doc.designMode="on";
50208                         
50209                     } catch (e) {
50210                         return;
50211                     }
50212                     Roo.TaskMgr.stop(task);
50213                     this.initEditor.defer(10, this);
50214                 }
50215             },
50216             interval : 10,
50217             duration: 10000,
50218             scope: this
50219         };
50220         Roo.TaskMgr.start(task);
50221
50222     },
50223
50224     // private
50225     onResize : function(w, h)
50226     {
50227          Roo.log('resize: ' +w + ',' + h );
50228         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
50229         if(!this.iframe){
50230             return;
50231         }
50232         if(typeof w == 'number'){
50233             
50234             this.iframe.style.width = w + 'px';
50235         }
50236         if(typeof h == 'number'){
50237             
50238             this.iframe.style.height = h + 'px';
50239             if(this.doc){
50240                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
50241             }
50242         }
50243         
50244     },
50245
50246     /**
50247      * Toggles the editor between standard and source edit mode.
50248      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
50249      */
50250     toggleSourceEdit : function(sourceEditMode){
50251         
50252         this.sourceEditMode = sourceEditMode === true;
50253         
50254         if(this.sourceEditMode){
50255  
50256             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
50257             
50258         }else{
50259             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
50260             //this.iframe.className = '';
50261             this.deferFocus();
50262         }
50263         //this.setSize(this.owner.wrap.getSize());
50264         //this.fireEvent('editmodechange', this, this.sourceEditMode);
50265     },
50266
50267     
50268   
50269
50270     /**
50271      * Protected method that will not generally be called directly. If you need/want
50272      * custom HTML cleanup, this is the method you should override.
50273      * @param {String} html The HTML to be cleaned
50274      * return {String} The cleaned HTML
50275      */
50276     cleanHtml : function(html)
50277     {
50278         html = String(html);
50279         if(html.length > 5){
50280             if(Roo.isSafari){ // strip safari nonsense
50281                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
50282             }
50283         }
50284         if(html == '&nbsp;'){
50285             html = '';
50286         }
50287         return html;
50288     },
50289
50290     /**
50291      * HTML Editor -> Textarea
50292      * Protected method that will not generally be called directly. Syncs the contents
50293      * of the editor iframe with the textarea.
50294      */
50295     syncValue : function()
50296     {
50297         //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
50298         if(this.initialized){
50299             
50300             if (this.undoManager) {
50301                 this.undoManager.addEvent();
50302             }
50303
50304             
50305             var bd = (this.doc.body || this.doc.documentElement);
50306            
50307             
50308             var sel = this.win.getSelection();
50309             
50310             var div = document.createElement('div');
50311             div.innerHTML = bd.innerHTML;
50312             var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
50313             if (gtx.length > 0) {
50314                 var rm = gtx.item(0).parentNode;
50315                 rm.parentNode.removeChild(rm);
50316             }
50317             
50318            
50319             if (this.enableBlocks) {
50320                 new Roo.htmleditor.FilterBlock({ node : div });
50321             }
50322             
50323             var html = div.innerHTML;
50324             
50325             //?? tidy?
50326             if (this.autoClean) {
50327                 
50328                 new Roo.htmleditor.FilterAttributes({
50329                     node : div,
50330                     attrib_white : [
50331                             'href',
50332                             'src',
50333                             'name',
50334                             'align',
50335                             'colspan',
50336                             'rowspan',
50337                             'data-display',
50338                             'data-width',
50339                             'start' ,
50340                             'style',
50341                             // youtube embed.
50342                             'class',
50343                             'allowfullscreen',
50344                             'frameborder',
50345                             'width',
50346                             'height',
50347                             'alt'
50348                             ],
50349                     attrib_clean : ['href', 'src' ] 
50350                 });
50351                 
50352                 var tidy = new Roo.htmleditor.TidySerializer({
50353                     inner:  true
50354                 });
50355                 html  = tidy.serialize(div);
50356                 
50357             }
50358             
50359             
50360             if(Roo.isSafari){
50361                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
50362                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
50363                 if(m && m[1]){
50364                     html = '<div style="'+m[0]+'">' + html + '</div>';
50365                 }
50366             }
50367             html = this.cleanHtml(html);
50368             // fix up the special chars.. normaly like back quotes in word...
50369             // however we do not want to do this with chinese..
50370             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
50371                 
50372                 var cc = match.charCodeAt();
50373
50374                 // Get the character value, handling surrogate pairs
50375                 if (match.length == 2) {
50376                     // It's a surrogate pair, calculate the Unicode code point
50377                     var high = match.charCodeAt(0) - 0xD800;
50378                     var low  = match.charCodeAt(1) - 0xDC00;
50379                     cc = (high * 0x400) + low + 0x10000;
50380                 }  else if (
50381                     (cc >= 0x4E00 && cc < 0xA000 ) ||
50382                     (cc >= 0x3400 && cc < 0x4E00 ) ||
50383                     (cc >= 0xf900 && cc < 0xfb00 )
50384                 ) {
50385                         return match;
50386                 }  
50387          
50388                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
50389                 return "&#" + cc + ";";
50390                 
50391                 
50392             });
50393             
50394             
50395              
50396             if(this.owner.fireEvent('beforesync', this, html) !== false){
50397                 this.el.dom.value = html;
50398                 this.owner.fireEvent('sync', this, html);
50399             }
50400         }
50401     },
50402
50403     /**
50404      * TEXTAREA -> EDITABLE
50405      * Protected method that will not generally be called directly. Pushes the value of the textarea
50406      * into the iframe editor.
50407      */
50408     pushValue : function()
50409     {
50410         //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
50411         if(this.initialized){
50412             var v = this.el.dom.value.trim();
50413             
50414             
50415             if(this.owner.fireEvent('beforepush', this, v) !== false){
50416                 var d = (this.doc.body || this.doc.documentElement);
50417                 d.innerHTML = v;
50418                  
50419                 this.el.dom.value = d.innerHTML;
50420                 this.owner.fireEvent('push', this, v);
50421             }
50422             if (this.autoClean) {
50423                 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
50424                 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
50425             }
50426             if (this.enableBlocks) {
50427                 Roo.htmleditor.Block.initAll(this.doc.body);
50428             }
50429             
50430             this.updateLanguage();
50431             
50432             var lc = this.doc.body.lastChild;
50433             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
50434                 // add an extra line at the end.
50435                 this.doc.body.appendChild(this.doc.createElement('br'));
50436             }
50437             
50438             
50439         }
50440     },
50441
50442     // private
50443     deferFocus : function(){
50444         this.focus.defer(10, this);
50445     },
50446
50447     // doc'ed in Field
50448     focus : function(){
50449         if(this.win && !this.sourceEditMode){
50450             this.win.focus();
50451         }else{
50452             this.el.focus();
50453         }
50454     },
50455     
50456     assignDocWin: function()
50457     {
50458         var iframe = this.iframe;
50459         
50460          if(Roo.isIE){
50461             this.doc = iframe.contentWindow.document;
50462             this.win = iframe.contentWindow;
50463         } else {
50464 //            if (!Roo.get(this.frameId)) {
50465 //                return;
50466 //            }
50467 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50468 //            this.win = Roo.get(this.frameId).dom.contentWindow;
50469             
50470             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
50471                 return;
50472             }
50473             
50474             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
50475             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
50476         }
50477     },
50478     
50479     // private
50480     initEditor : function(){
50481         //console.log("INIT EDITOR");
50482         this.assignDocWin();
50483         
50484         
50485         
50486         this.doc.designMode="on";
50487         this.doc.open();
50488         this.doc.write(this.getDocMarkup());
50489         this.doc.close();
50490         
50491         var dbody = (this.doc.body || this.doc.documentElement);
50492         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
50493         // this copies styles from the containing element into thsi one..
50494         // not sure why we need all of this..
50495         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
50496         
50497         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
50498         //ss['background-attachment'] = 'fixed'; // w3c
50499         dbody.bgProperties = 'fixed'; // ie
50500         dbody.setAttribute("translate", "no");
50501         
50502         //Roo.DomHelper.applyStyles(dbody, ss);
50503         Roo.EventManager.on(this.doc, {
50504              
50505             'mouseup': this.onEditorEvent,
50506             'dblclick': this.onEditorEvent,
50507             'click': this.onEditorEvent,
50508             'keyup': this.onEditorEvent,
50509             
50510             buffer:100,
50511             scope: this
50512         });
50513         Roo.EventManager.on(this.doc, {
50514             'paste': this.onPasteEvent,
50515             scope : this
50516         });
50517         if(Roo.isGecko){
50518             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
50519         }
50520         //??? needed???
50521         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
50522             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
50523         }
50524         this.initialized = true;
50525
50526         
50527         // initialize special key events - enter
50528         new Roo.htmleditor.KeyEnter({core : this});
50529         
50530          
50531         
50532         this.owner.fireEvent('initialize', this);
50533         this.pushValue();
50534     },
50535     // this is to prevent a href clicks resulting in a redirect?
50536    
50537     onPasteEvent : function(e,v)
50538     {
50539         // I think we better assume paste is going to be a dirty load of rubish from word..
50540         
50541         // even pasting into a 'email version' of this widget will have to clean up that mess.
50542         var cd = (e.browserEvent.clipboardData || window.clipboardData);
50543         
50544         // check what type of paste - if it's an image, then handle it differently.
50545         if (cd.files && cd.files.length > 0) {
50546             // pasting images?
50547             var urlAPI = (window.createObjectURL && window) || 
50548                 (window.URL && URL.revokeObjectURL && URL) || 
50549                 (window.webkitURL && webkitURL);
50550             
50551             var r = new FileReader();
50552             var t = this;
50553             r.addEventListener('load',function()
50554             {
50555                 
50556                 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
50557                 // is insert asycn?
50558                 if (t.enableBlocks) {
50559                     
50560                     Array.from(d.getElementsByTagName('img')).forEach(function(img) {
50561                         if (img.closest('figure')) { // assume!! that it's aready
50562                             return;
50563                         }
50564                         var fig  = new Roo.htmleditor.BlockFigure({
50565                             image_src  : img.src
50566                         });
50567                         fig.updateElement(img); // replace it..
50568                         
50569                     });
50570                 }
50571                 t.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
50572                 t.owner.fireEvent('paste', this);
50573             });
50574             r.readAsDataURL(cd.files[0]);
50575             
50576             e.preventDefault();
50577             
50578             return false;
50579         }
50580         if (cd.types.indexOf('text/html') < 0 ) {
50581             return false;
50582         }
50583         var images = [];
50584         var html = cd.getData('text/html'); // clipboard event
50585         if (cd.types.indexOf('text/rtf') > -1) {
50586             var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
50587             images = parser.doc ? parser.doc.getElementsByType('pict') : [];
50588         }
50589         //Roo.log(images);
50590         //Roo.log(imgs);
50591         // fixme..
50592         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
50593                        .map(function(g) { return g.toDataURL(); })
50594                        .filter(function(g) { return g != 'about:blank'; });
50595         
50596         //Roo.log(html);
50597         html = this.cleanWordChars(html);
50598         
50599         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
50600         
50601         
50602         var sn = this.getParentElement();
50603         // check if d contains a table, and prevent nesting??
50604         //Roo.log(d.getElementsByTagName('table'));
50605         //Roo.log(sn);
50606         //Roo.log(sn.closest('table'));
50607         if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
50608             e.preventDefault();
50609             this.insertAtCursor("You can not nest tables");
50610             //Roo.log("prevent?"); // fixme - 
50611             return false;
50612         }
50613         
50614         
50615         
50616         if (images.length > 0) {
50617             // replace all v:imagedata - with img.
50618             var ar = Array.from(d.getElementsByTagName('v:imagedata'));
50619             Roo.each(ar, function(node) {
50620                 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
50621                 node.parentNode.removeChild(node);
50622             });
50623             
50624             
50625             Roo.each(d.getElementsByTagName('img'), function(img, i) {
50626                 img.setAttribute('src', images[i]);
50627             });
50628         }
50629         if (this.autoClean) {
50630             new Roo.htmleditor.FilterWord({ node : d });
50631             
50632             new Roo.htmleditor.FilterStyleToTag({ node : d });
50633             new Roo.htmleditor.FilterAttributes({
50634                 node : d,
50635                 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
50636                 attrib_clean : ['href', 'src' ] 
50637             });
50638             new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
50639             // should be fonts..
50640             new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
50641             new Roo.htmleditor.FilterParagraph({ node : d });
50642             new Roo.htmleditor.FilterSpan({ node : d });
50643             new Roo.htmleditor.FilterLongBr({ node : d });
50644             new Roo.htmleditor.FilterComment({ node : d });
50645             
50646             
50647         }
50648         if (this.enableBlocks) {
50649                 
50650             Array.from(d.getElementsByTagName('img')).forEach(function(img) {
50651                 if (img.closest('figure')) { // assume!! that it's aready
50652                     return;
50653                 }
50654                 var fig  = new Roo.htmleditor.BlockFigure({
50655                     image_src  : img.src
50656                 });
50657                 fig.updateElement(img); // replace it..
50658                 
50659             });
50660         }
50661         
50662         
50663         this.insertAtCursor(d.innerHTML.replace(/&nbsp;/g,' '));
50664         if (this.enableBlocks) {
50665             Roo.htmleditor.Block.initAll(this.doc.body);
50666         }
50667          
50668         
50669         e.preventDefault();
50670         this.owner.fireEvent('paste', this);
50671         return false;
50672         // default behaveiour should be our local cleanup paste? (optional?)
50673         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
50674         //this.owner.fireEvent('paste', e, v);
50675     },
50676     // private
50677     onDestroy : function(){
50678         
50679         
50680         
50681         if(this.rendered){
50682             
50683             //for (var i =0; i < this.toolbars.length;i++) {
50684             //    // fixme - ask toolbars for heights?
50685             //    this.toolbars[i].onDestroy();
50686            // }
50687             
50688             //this.wrap.dom.innerHTML = '';
50689             //this.wrap.remove();
50690         }
50691     },
50692
50693     // private
50694     onFirstFocus : function(){
50695         
50696         this.assignDocWin();
50697         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
50698         
50699         this.activated = true;
50700          
50701     
50702         if(Roo.isGecko){ // prevent silly gecko errors
50703             this.win.focus();
50704             var s = this.win.getSelection();
50705             if(!s.focusNode || s.focusNode.nodeType != 3){
50706                 var r = s.getRangeAt(0);
50707                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
50708                 r.collapse(true);
50709                 this.deferFocus();
50710             }
50711             try{
50712                 this.execCmd('useCSS', true);
50713                 this.execCmd('styleWithCSS', false);
50714             }catch(e){}
50715         }
50716         this.owner.fireEvent('activate', this);
50717     },
50718
50719     // private
50720     adjustFont: function(btn){
50721         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
50722         //if(Roo.isSafari){ // safari
50723         //    adjust *= 2;
50724        // }
50725         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
50726         if(Roo.isSafari){ // safari
50727             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
50728             v =  (v < 10) ? 10 : v;
50729             v =  (v > 48) ? 48 : v;
50730             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
50731             
50732         }
50733         
50734         
50735         v = Math.max(1, v+adjust);
50736         
50737         this.execCmd('FontSize', v  );
50738     },
50739
50740     onEditorEvent : function(e)
50741     {
50742          
50743         
50744         if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
50745             return; // we do not handle this.. (undo manager does..)
50746         }
50747         // clicking a 'block'?
50748         
50749         // in theory this detects if the last element is not a br, then we try and do that.
50750         // its so clicking in space at bottom triggers adding a br and moving the cursor.
50751         if (e &&
50752             e.target.nodeName == 'BODY' &&
50753             e.type == "mouseup" &&
50754             this.doc.body.lastChild
50755            ) {
50756             var lc = this.doc.body.lastChild;
50757             // gtx-trans is google translate plugin adding crap.
50758             while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
50759                 lc = lc.previousSibling;
50760             }
50761             if (lc.nodeType == 1 && lc.nodeName != 'BR') {
50762             // if last element is <BR> - then dont do anything.
50763             
50764                 var ns = this.doc.createElement('br');
50765                 this.doc.body.appendChild(ns);
50766                 range = this.doc.createRange();
50767                 range.setStartAfter(ns);
50768                 range.collapse(true);
50769                 var sel = this.win.getSelection();
50770                 sel.removeAllRanges();
50771                 sel.addRange(range);
50772             }
50773         }
50774         
50775         
50776         
50777         this.fireEditorEvent(e);
50778       //  this.updateToolbar();
50779         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
50780     },
50781     
50782     fireEditorEvent: function(e)
50783     {
50784         this.owner.fireEvent('editorevent', this, e);
50785     },
50786
50787     insertTag : function(tg)
50788     {
50789         // could be a bit smarter... -> wrap the current selected tRoo..
50790         if (tg.toLowerCase() == 'span' ||
50791             tg.toLowerCase() == 'code' ||
50792             tg.toLowerCase() == 'sup' ||
50793             tg.toLowerCase() == 'sub' 
50794             ) {
50795             
50796             range = this.createRange(this.getSelection());
50797             var wrappingNode = this.doc.createElement(tg.toLowerCase());
50798             wrappingNode.appendChild(range.extractContents());
50799             range.insertNode(wrappingNode);
50800
50801             return;
50802             
50803             
50804             
50805         }
50806         this.execCmd("formatblock",   tg);
50807         this.undoManager.addEvent(); 
50808     },
50809     
50810     insertText : function(txt)
50811     {
50812         
50813         
50814         var range = this.createRange();
50815         range.deleteContents();
50816                //alert(Sender.getAttribute('label'));
50817                
50818         range.insertNode(this.doc.createTextNode(txt));
50819         this.undoManager.addEvent();
50820     } ,
50821     
50822      
50823
50824     /**
50825      * Executes a Midas editor command on the editor document and performs necessary focus and
50826      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
50827      * @param {String} cmd The Midas command
50828      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50829      */
50830     relayCmd : function(cmd, value)
50831     {
50832         
50833         switch (cmd) {
50834             case 'justifyleft':
50835             case 'justifyright':
50836             case 'justifycenter':
50837                 // if we are in a cell, then we will adjust the
50838                 var n = this.getParentElement();
50839                 var td = n.closest('td');
50840                 if (td) {
50841                     var bl = Roo.htmleditor.Block.factory(td);
50842                     bl.textAlign = cmd.replace('justify','');
50843                     bl.updateElement();
50844                     this.owner.fireEvent('editorevent', this);
50845                     return;
50846                 }
50847                 this.execCmd('styleWithCSS', true); // 
50848                 break;
50849             case 'bold':
50850             case 'italic':
50851             case 'underline':                
50852                 // if there is no selection, then we insert, and set the curson inside it..
50853                 this.execCmd('styleWithCSS', false); 
50854                 break;
50855                 
50856         
50857             default:
50858                 break;
50859         }
50860         
50861         
50862         this.win.focus();
50863         this.execCmd(cmd, value);
50864         this.owner.fireEvent('editorevent', this);
50865         //this.updateToolbar();
50866         this.owner.deferFocus();
50867     },
50868
50869     /**
50870      * Executes a Midas editor command directly on the editor document.
50871      * For visual commands, you should use {@link #relayCmd} instead.
50872      * <b>This should only be called after the editor is initialized.</b>
50873      * @param {String} cmd The Midas command
50874      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
50875      */
50876     execCmd : function(cmd, value){
50877         this.doc.execCommand(cmd, false, value === undefined ? null : value);
50878         this.syncValue();
50879     },
50880  
50881  
50882    
50883     /**
50884      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
50885      * to insert tRoo.
50886      * @param {String} text | dom node.. 
50887      */
50888     insertAtCursor : function(text)
50889     {
50890         
50891         if(!this.activated){
50892             return;
50893         }
50894          
50895         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
50896             this.win.focus();
50897             
50898             
50899             // from jquery ui (MIT licenced)
50900             var range, node;
50901             var win = this.win;
50902             
50903             if (win.getSelection && win.getSelection().getRangeAt) {
50904                 
50905                 // delete the existing?
50906                 
50907                 this.createRange(this.getSelection()).deleteContents();
50908                 range = win.getSelection().getRangeAt(0);
50909                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
50910                 range.insertNode(node);
50911                 range = range.cloneRange();
50912                 range.collapse(false);
50913                  
50914                 win.getSelection().removeAllRanges();
50915                 win.getSelection().addRange(range);
50916                 
50917                 
50918                 
50919             } else if (win.document.selection && win.document.selection.createRange) {
50920                 // no firefox support
50921                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50922                 win.document.selection.createRange().pasteHTML(txt);
50923             
50924             } else {
50925                 // no firefox support
50926                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
50927                 this.execCmd('InsertHTML', txt);
50928             } 
50929             this.syncValue();
50930             
50931             this.deferFocus();
50932         }
50933     },
50934  // private
50935     mozKeyPress : function(e){
50936         if(e.ctrlKey){
50937             var c = e.getCharCode(), cmd;
50938           
50939             if(c > 0){
50940                 c = String.fromCharCode(c).toLowerCase();
50941                 switch(c){
50942                     case 'b':
50943                         cmd = 'bold';
50944                         break;
50945                     case 'i':
50946                         cmd = 'italic';
50947                         break;
50948                     
50949                     case 'u':
50950                         cmd = 'underline';
50951                         break;
50952                     
50953                     //case 'v':
50954                       //  this.cleanUpPaste.defer(100, this);
50955                       //  return;
50956                         
50957                 }
50958                 if(cmd){
50959                     
50960                     this.relayCmd(cmd);
50961                     //this.win.focus();
50962                     //this.execCmd(cmd);
50963                     //this.deferFocus();
50964                     e.preventDefault();
50965                 }
50966                 
50967             }
50968         }
50969     },
50970
50971     // private
50972     fixKeys : function(){ // load time branching for fastest keydown performance
50973         
50974         
50975         if(Roo.isIE){
50976             return function(e){
50977                 var k = e.getKey(), r;
50978                 if(k == e.TAB){
50979                     e.stopEvent();
50980                     r = this.doc.selection.createRange();
50981                     if(r){
50982                         r.collapse(true);
50983                         r.pasteHTML('&#160;&#160;&#160;&#160;');
50984                         this.deferFocus();
50985                     }
50986                     return;
50987                 }
50988                 /// this is handled by Roo.htmleditor.KeyEnter
50989                  /*
50990                 if(k == e.ENTER){
50991                     r = this.doc.selection.createRange();
50992                     if(r){
50993                         var target = r.parentElement();
50994                         if(!target || target.tagName.toLowerCase() != 'li'){
50995                             e.stopEvent();
50996                             r.pasteHTML('<br/>');
50997                             r.collapse(false);
50998                             r.select();
50999                         }
51000                     }
51001                 }
51002                 */
51003                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
51004                 //    this.cleanUpPaste.defer(100, this);
51005                 //    return;
51006                 //}
51007                 
51008                 
51009             };
51010         }else if(Roo.isOpera){
51011             return function(e){
51012                 var k = e.getKey();
51013                 if(k == e.TAB){
51014                     e.stopEvent();
51015                     this.win.focus();
51016                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
51017                     this.deferFocus();
51018                 }
51019                
51020                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
51021                 //    this.cleanUpPaste.defer(100, this);
51022                  //   return;
51023                 //}
51024                 
51025             };
51026         }else if(Roo.isSafari){
51027             return function(e){
51028                 var k = e.getKey();
51029                 
51030                 if(k == e.TAB){
51031                     e.stopEvent();
51032                     this.execCmd('InsertText','\t');
51033                     this.deferFocus();
51034                     return;
51035                 }
51036                  this.mozKeyPress(e);
51037                 
51038                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
51039                  //   this.cleanUpPaste.defer(100, this);
51040                  //   return;
51041                // }
51042                 
51043              };
51044         }
51045     }(),
51046     
51047     getAllAncestors: function()
51048     {
51049         var p = this.getSelectedNode();
51050         var a = [];
51051         if (!p) {
51052             a.push(p); // push blank onto stack..
51053             p = this.getParentElement();
51054         }
51055         
51056         
51057         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
51058             a.push(p);
51059             p = p.parentNode;
51060         }
51061         a.push(this.doc.body);
51062         return a;
51063     },
51064     lastSel : false,
51065     lastSelNode : false,
51066     
51067     
51068     getSelection : function() 
51069     {
51070         this.assignDocWin();
51071         return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
51072     },
51073     /**
51074      * Select a dom node
51075      * @param {DomElement} node the node to select
51076      */
51077     selectNode : function(node, collapse)
51078     {
51079         var nodeRange = node.ownerDocument.createRange();
51080         try {
51081             nodeRange.selectNode(node);
51082         } catch (e) {
51083             nodeRange.selectNodeContents(node);
51084         }
51085         if (collapse === true) {
51086             nodeRange.collapse(true);
51087         }
51088         //
51089         var s = this.win.getSelection();
51090         s.removeAllRanges();
51091         s.addRange(nodeRange);
51092     },
51093     
51094     getSelectedNode: function() 
51095     {
51096         // this may only work on Gecko!!!
51097         
51098         // should we cache this!!!!
51099         
51100          
51101          
51102         var range = this.createRange(this.getSelection()).cloneRange();
51103         
51104         if (Roo.isIE) {
51105             var parent = range.parentElement();
51106             while (true) {
51107                 var testRange = range.duplicate();
51108                 testRange.moveToElementText(parent);
51109                 if (testRange.inRange(range)) {
51110                     break;
51111                 }
51112                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
51113                     break;
51114                 }
51115                 parent = parent.parentElement;
51116             }
51117             return parent;
51118         }
51119         
51120         // is ancestor a text element.
51121         var ac =  range.commonAncestorContainer;
51122         if (ac.nodeType == 3) {
51123             ac = ac.parentNode;
51124         }
51125         
51126         var ar = ac.childNodes;
51127          
51128         var nodes = [];
51129         var other_nodes = [];
51130         var has_other_nodes = false;
51131         for (var i=0;i<ar.length;i++) {
51132             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
51133                 continue;
51134             }
51135             // fullly contained node.
51136             
51137             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
51138                 nodes.push(ar[i]);
51139                 continue;
51140             }
51141             
51142             // probably selected..
51143             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
51144                 other_nodes.push(ar[i]);
51145                 continue;
51146             }
51147             // outer..
51148             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
51149                 continue;
51150             }
51151             
51152             
51153             has_other_nodes = true;
51154         }
51155         if (!nodes.length && other_nodes.length) {
51156             nodes= other_nodes;
51157         }
51158         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
51159             return false;
51160         }
51161         
51162         return nodes[0];
51163     },
51164     
51165     
51166     createRange: function(sel)
51167     {
51168         // this has strange effects when using with 
51169         // top toolbar - not sure if it's a great idea.
51170         //this.editor.contentWindow.focus();
51171         if (typeof sel != "undefined") {
51172             try {
51173                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
51174             } catch(e) {
51175                 return this.doc.createRange();
51176             }
51177         } else {
51178             return this.doc.createRange();
51179         }
51180     },
51181     getParentElement: function()
51182     {
51183         
51184         this.assignDocWin();
51185         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
51186         
51187         var range = this.createRange(sel);
51188          
51189         try {
51190             var p = range.commonAncestorContainer;
51191             while (p.nodeType == 3) { // text node
51192                 p = p.parentNode;
51193             }
51194             return p;
51195         } catch (e) {
51196             return null;
51197         }
51198     
51199     },
51200     /***
51201      *
51202      * Range intersection.. the hard stuff...
51203      *  '-1' = before
51204      *  '0' = hits..
51205      *  '1' = after.
51206      *         [ -- selected range --- ]
51207      *   [fail]                        [fail]
51208      *
51209      *    basically..
51210      *      if end is before start or  hits it. fail.
51211      *      if start is after end or hits it fail.
51212      *
51213      *   if either hits (but other is outside. - then it's not 
51214      *   
51215      *    
51216      **/
51217     
51218     
51219     // @see http://www.thismuchiknow.co.uk/?p=64.
51220     rangeIntersectsNode : function(range, node)
51221     {
51222         var nodeRange = node.ownerDocument.createRange();
51223         try {
51224             nodeRange.selectNode(node);
51225         } catch (e) {
51226             nodeRange.selectNodeContents(node);
51227         }
51228     
51229         var rangeStartRange = range.cloneRange();
51230         rangeStartRange.collapse(true);
51231     
51232         var rangeEndRange = range.cloneRange();
51233         rangeEndRange.collapse(false);
51234     
51235         var nodeStartRange = nodeRange.cloneRange();
51236         nodeStartRange.collapse(true);
51237     
51238         var nodeEndRange = nodeRange.cloneRange();
51239         nodeEndRange.collapse(false);
51240     
51241         return rangeStartRange.compareBoundaryPoints(
51242                  Range.START_TO_START, nodeEndRange) == -1 &&
51243                rangeEndRange.compareBoundaryPoints(
51244                  Range.START_TO_START, nodeStartRange) == 1;
51245         
51246          
51247     },
51248     rangeCompareNode : function(range, node)
51249     {
51250         var nodeRange = node.ownerDocument.createRange();
51251         try {
51252             nodeRange.selectNode(node);
51253         } catch (e) {
51254             nodeRange.selectNodeContents(node);
51255         }
51256         
51257         
51258         range.collapse(true);
51259     
51260         nodeRange.collapse(true);
51261      
51262         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
51263         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
51264          
51265         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
51266         
51267         var nodeIsBefore   =  ss == 1;
51268         var nodeIsAfter    = ee == -1;
51269         
51270         if (nodeIsBefore && nodeIsAfter) {
51271             return 0; // outer
51272         }
51273         if (!nodeIsBefore && nodeIsAfter) {
51274             return 1; //right trailed.
51275         }
51276         
51277         if (nodeIsBefore && !nodeIsAfter) {
51278             return 2;  // left trailed.
51279         }
51280         // fully contined.
51281         return 3;
51282     },
51283  
51284     cleanWordChars : function(input) {// change the chars to hex code
51285         
51286        var swapCodes  = [ 
51287             [    8211, "&#8211;" ], 
51288             [    8212, "&#8212;" ], 
51289             [    8216,  "'" ],  
51290             [    8217, "'" ],  
51291             [    8220, '"' ],  
51292             [    8221, '"' ],  
51293             [    8226, "*" ],  
51294             [    8230, "..." ]
51295         ]; 
51296         var output = input;
51297         Roo.each(swapCodes, function(sw) { 
51298             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
51299             
51300             output = output.replace(swapper, sw[1]);
51301         });
51302         
51303         return output;
51304     },
51305     
51306      
51307     
51308         
51309     
51310     cleanUpChild : function (node)
51311     {
51312         
51313         new Roo.htmleditor.FilterComment({node : node});
51314         new Roo.htmleditor.FilterAttributes({
51315                 node : node,
51316                 attrib_black : this.ablack,
51317                 attrib_clean : this.aclean,
51318                 style_white : this.cwhite,
51319                 style_black : this.cblack
51320         });
51321         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
51322         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
51323          
51324         
51325     },
51326     
51327     /**
51328      * Clean up MS wordisms...
51329      * @deprecated - use filter directly
51330      */
51331     cleanWord : function(node)
51332     {
51333         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
51334         new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
51335         
51336     },
51337    
51338     
51339     /**
51340
51341      * @deprecated - use filters
51342      */
51343     cleanTableWidths : function(node)
51344     {
51345         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
51346         
51347  
51348     },
51349     
51350      
51351         
51352     applyBlacklists : function()
51353     {
51354         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
51355         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
51356         
51357         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
51358         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
51359         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
51360         
51361         this.white = [];
51362         this.black = [];
51363         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
51364             if (b.indexOf(tag) > -1) {
51365                 return;
51366             }
51367             this.white.push(tag);
51368             
51369         }, this);
51370         
51371         Roo.each(w, function(tag) {
51372             if (b.indexOf(tag) > -1) {
51373                 return;
51374             }
51375             if (this.white.indexOf(tag) > -1) {
51376                 return;
51377             }
51378             this.white.push(tag);
51379             
51380         }, this);
51381         
51382         
51383         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
51384             if (w.indexOf(tag) > -1) {
51385                 return;
51386             }
51387             this.black.push(tag);
51388             
51389         }, this);
51390         
51391         Roo.each(b, function(tag) {
51392             if (w.indexOf(tag) > -1) {
51393                 return;
51394             }
51395             if (this.black.indexOf(tag) > -1) {
51396                 return;
51397             }
51398             this.black.push(tag);
51399             
51400         }, this);
51401         
51402         
51403         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
51404         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
51405         
51406         this.cwhite = [];
51407         this.cblack = [];
51408         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
51409             if (b.indexOf(tag) > -1) {
51410                 return;
51411             }
51412             this.cwhite.push(tag);
51413             
51414         }, this);
51415         
51416         Roo.each(w, function(tag) {
51417             if (b.indexOf(tag) > -1) {
51418                 return;
51419             }
51420             if (this.cwhite.indexOf(tag) > -1) {
51421                 return;
51422             }
51423             this.cwhite.push(tag);
51424             
51425         }, this);
51426         
51427         
51428         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
51429             if (w.indexOf(tag) > -1) {
51430                 return;
51431             }
51432             this.cblack.push(tag);
51433             
51434         }, this);
51435         
51436         Roo.each(b, function(tag) {
51437             if (w.indexOf(tag) > -1) {
51438                 return;
51439             }
51440             if (this.cblack.indexOf(tag) > -1) {
51441                 return;
51442             }
51443             this.cblack.push(tag);
51444             
51445         }, this);
51446     },
51447     
51448     setStylesheets : function(stylesheets)
51449     {
51450         if(typeof(stylesheets) == 'string'){
51451             Roo.get(this.iframe.contentDocument.head).createChild({
51452                 tag : 'link',
51453                 rel : 'stylesheet',
51454                 type : 'text/css',
51455                 href : stylesheets
51456             });
51457             
51458             return;
51459         }
51460         var _this = this;
51461      
51462         Roo.each(stylesheets, function(s) {
51463             if(!s.length){
51464                 return;
51465             }
51466             
51467             Roo.get(_this.iframe.contentDocument.head).createChild({
51468                 tag : 'link',
51469                 rel : 'stylesheet',
51470                 type : 'text/css',
51471                 href : s
51472             });
51473         });
51474
51475         
51476     },
51477     
51478     
51479     updateLanguage : function()
51480     {
51481         if (!this.iframe || !this.iframe.contentDocument) {
51482             return;
51483         }
51484         Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
51485     },
51486     
51487     
51488     removeStylesheets : function()
51489     {
51490         var _this = this;
51491         
51492         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
51493             s.remove();
51494         });
51495     },
51496     
51497     setStyle : function(style)
51498     {
51499         Roo.get(this.iframe.contentDocument.head).createChild({
51500             tag : 'style',
51501             type : 'text/css',
51502             html : style
51503         });
51504
51505         return;
51506     }
51507     
51508     // hide stuff that is not compatible
51509     /**
51510      * @event blur
51511      * @hide
51512      */
51513     /**
51514      * @event change
51515      * @hide
51516      */
51517     /**
51518      * @event focus
51519      * @hide
51520      */
51521     /**
51522      * @event specialkey
51523      * @hide
51524      */
51525     /**
51526      * @cfg {String} fieldClass @hide
51527      */
51528     /**
51529      * @cfg {String} focusClass @hide
51530      */
51531     /**
51532      * @cfg {String} autoCreate @hide
51533      */
51534     /**
51535      * @cfg {String} inputType @hide
51536      */
51537     /**
51538      * @cfg {String} invalidClass @hide
51539      */
51540     /**
51541      * @cfg {String} invalidText @hide
51542      */
51543     /**
51544      * @cfg {String} msgFx @hide
51545      */
51546     /**
51547      * @cfg {String} validateOnBlur @hide
51548      */
51549 });
51550
51551 Roo.HtmlEditorCore.white = [
51552         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
51553         
51554        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
51555        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
51556        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
51557        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
51558        'TABLE',   'UL',         'XMP', 
51559        
51560        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
51561       'THEAD',   'TR', 
51562      
51563       'DIR', 'MENU', 'OL', 'UL', 'DL',
51564        
51565       'EMBED',  'OBJECT'
51566 ];
51567
51568
51569 Roo.HtmlEditorCore.black = [
51570     //    'embed',  'object', // enable - backend responsiblity to clean thiese
51571         'APPLET', // 
51572         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
51573         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
51574         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
51575         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
51576         //'FONT' // CLEAN LATER..
51577         'COLGROUP', 'COL'   // messy tables.
51578         
51579         
51580 ];
51581 Roo.HtmlEditorCore.clean = [ // ?? needed???
51582      'SCRIPT', 'STYLE', 'TITLE', 'XML'
51583 ];
51584 Roo.HtmlEditorCore.tag_remove = [
51585     'FONT', 'TBODY'  
51586 ];
51587 // attributes..
51588
51589 Roo.HtmlEditorCore.ablack = [
51590     'on'
51591 ];
51592     
51593 Roo.HtmlEditorCore.aclean = [ 
51594     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
51595 ];
51596
51597 // protocols..
51598 Roo.HtmlEditorCore.pwhite= [
51599         'http',  'https',  'mailto'
51600 ];
51601
51602 // white listed style attributes.
51603 Roo.HtmlEditorCore.cwhite= [
51604       //  'text-align', /// default is to allow most things..
51605       
51606          
51607 //        'font-size'//??
51608 ];
51609
51610 // black listed style attributes.
51611 Roo.HtmlEditorCore.cblack= [
51612       //  'font-size' -- this can be set by the project 
51613 ];
51614
51615
51616
51617
51618     //<script type="text/javascript">
51619
51620 /*
51621  * Ext JS Library 1.1.1
51622  * Copyright(c) 2006-2007, Ext JS, LLC.
51623  * Licence LGPL
51624  * 
51625  */
51626  
51627  
51628 Roo.form.HtmlEditor = function(config){
51629     
51630     
51631     
51632     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
51633     
51634     if (!this.toolbars) {
51635         this.toolbars = [];
51636     }
51637     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
51638     
51639     
51640 };
51641
51642 /**
51643  * @class Roo.form.HtmlEditor
51644  * @extends Roo.form.Field
51645  * Provides a lightweight HTML Editor component.
51646  *
51647  * This has been tested on Fireforx / Chrome.. IE may not be so great..
51648  * 
51649  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
51650  * supported by this editor.</b><br/><br/>
51651  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
51652  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
51653  */
51654 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
51655     /**
51656      * @cfg {Boolean} clearUp
51657      */
51658     clearUp : true,
51659       /**
51660      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
51661      */
51662     toolbars : false,
51663    
51664      /**
51665      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
51666      *                        Roo.resizable.
51667      */
51668     resizable : false,
51669      /**
51670      * @cfg {Number} height (in pixels)
51671      */   
51672     height: 300,
51673    /**
51674      * @cfg {Number} width (in pixels)
51675      */   
51676     width: 500,
51677     
51678     /**
51679      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
51680      * 
51681      */
51682     stylesheets: false,
51683     
51684     
51685      /**
51686      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
51687      * 
51688      */
51689     cblack: false,
51690     /**
51691      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
51692      * 
51693      */
51694     cwhite: false,
51695     
51696      /**
51697      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
51698      * 
51699      */
51700     black: false,
51701     /**
51702      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
51703      * 
51704      */
51705     white: false,
51706     /**
51707      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
51708      */
51709     allowComments: false,
51710     /**
51711      * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
51712      */
51713     enableBlocks : true,
51714     
51715     /**
51716      * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
51717      *         if you are doing an email editor, this probably needs disabling, it's designed
51718      */
51719     autoClean: true,
51720     /**
51721      * @cfg {string} bodyCls default '' default classes to add to body of editable area - usually undoreset is a good start..
51722      */
51723     bodyCls : '',
51724     /**
51725      * @cfg {String} language default en - language of text (usefull for rtl languages)
51726      * 
51727      */
51728     language: 'en',
51729     
51730      
51731     // id of frame..
51732     frameId: false,
51733     
51734     // private properties
51735     validationEvent : false,
51736     deferHeight: true,
51737     initialized : false,
51738     activated : false,
51739     
51740     onFocus : Roo.emptyFn,
51741     iframePad:3,
51742     hideMode:'offsets',
51743     
51744     actionMode : 'container', // defaults to hiding it...
51745     
51746     defaultAutoCreate : { // modified by initCompnoent..
51747         tag: "textarea",
51748         style:"width:500px;height:300px;",
51749         autocomplete: "new-password"
51750     },
51751
51752     // private
51753     initComponent : function(){
51754         this.addEvents({
51755             /**
51756              * @event initialize
51757              * Fires when the editor is fully initialized (including the iframe)
51758              * @param {HtmlEditor} this
51759              */
51760             initialize: true,
51761             /**
51762              * @event activate
51763              * Fires when the editor is first receives the focus. Any insertion must wait
51764              * until after this event.
51765              * @param {HtmlEditor} this
51766              */
51767             activate: true,
51768              /**
51769              * @event beforesync
51770              * Fires before the textarea is updated with content from the editor iframe. Return false
51771              * to cancel the sync.
51772              * @param {HtmlEditor} this
51773              * @param {String} html
51774              */
51775             beforesync: true,
51776              /**
51777              * @event beforepush
51778              * Fires before the iframe editor is updated with content from the textarea. Return false
51779              * to cancel the push.
51780              * @param {HtmlEditor} this
51781              * @param {String} html
51782              */
51783             beforepush: true,
51784              /**
51785              * @event sync
51786              * Fires when the textarea is updated with content from the editor iframe.
51787              * @param {HtmlEditor} this
51788              * @param {String} html
51789              */
51790             sync: true,
51791              /**
51792              * @event push
51793              * Fires when the iframe editor is updated with content from the textarea.
51794              * @param {HtmlEditor} this
51795              * @param {String} html
51796              */
51797             push: true,
51798              /**
51799              * @event editmodechange
51800              * Fires when the editor switches edit modes
51801              * @param {HtmlEditor} this
51802              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
51803              */
51804             editmodechange: true,
51805             /**
51806              * @event editorevent
51807              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
51808              * @param {HtmlEditor} this
51809              */
51810             editorevent: true,
51811             /**
51812              * @event firstfocus
51813              * Fires when on first focus - needed by toolbars..
51814              * @param {HtmlEditor} this
51815              */
51816             firstfocus: true,
51817             /**
51818              * @event autosave
51819              * Auto save the htmlEditor value as a file into Events
51820              * @param {HtmlEditor} this
51821              */
51822             autosave: true,
51823             /**
51824              * @event savedpreview
51825              * preview the saved version of htmlEditor
51826              * @param {HtmlEditor} this
51827              */
51828             savedpreview: true,
51829             
51830             /**
51831             * @event stylesheetsclick
51832             * Fires when press the Sytlesheets button
51833             * @param {Roo.HtmlEditorCore} this
51834             */
51835             stylesheetsclick: true,
51836             /**
51837             * @event paste
51838             * Fires when press user pastes into the editor
51839             * @param {Roo.HtmlEditorCore} this
51840             */
51841             paste: true,
51842              /**
51843             * @event imageadd
51844             * Fires when on any editor when an image is added (excluding paste)
51845             * @param {Roo.HtmlEditorCore} this
51846             */
51847            imageadd: true ,
51848             /**
51849             * @event imagedelete
51850             * Fires when on any editor when an image is deleted
51851             * @param {Roo.HtmlEditorCore} this
51852             */
51853            imagedelete: true  
51854         });
51855         this.defaultAutoCreate =  {
51856             tag: "textarea",
51857             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
51858             autocomplete: "new-password"
51859         };
51860     },
51861
51862     /**
51863      * Protected method that will not generally be called directly. It
51864      * is called when the editor creates its toolbar. Override this method if you need to
51865      * add custom toolbar buttons.
51866      * @param {HtmlEditor} editor
51867      */
51868     createToolbar : function(editor){
51869         Roo.log("create toolbars");
51870         if (!editor.toolbars || !editor.toolbars.length) {
51871             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
51872         }
51873         
51874         for (var i =0 ; i < editor.toolbars.length;i++) {
51875             editor.toolbars[i] = Roo.factory(
51876                     typeof(editor.toolbars[i]) == 'string' ?
51877                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
51878                 Roo.form.HtmlEditor);
51879             editor.toolbars[i].init(editor);
51880         }
51881          
51882         
51883     },
51884     /**
51885      * get the Context selected node
51886      * @returns {DomElement|boolean} selected node if active or false if none
51887      * 
51888      */
51889     getSelectedNode : function()
51890     {
51891         if (this.toolbars.length < 2 || !this.toolbars[1].tb) {
51892             return false;
51893         }
51894         return this.toolbars[1].tb.selectedNode;
51895     
51896     },
51897     // private
51898     onRender : function(ct, position)
51899     {
51900         var _t = this;
51901         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
51902         
51903         this.wrap = this.el.wrap({
51904             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
51905         });
51906         
51907         this.editorcore.onRender(ct, position);
51908          
51909         if (this.resizable) {
51910             this.resizeEl = new Roo.Resizable(this.wrap, {
51911                 pinned : true,
51912                 wrap: true,
51913                 dynamic : true,
51914                 minHeight : this.height,
51915                 height: this.height,
51916                 handles : this.resizable,
51917                 width: this.width,
51918                 listeners : {
51919                     resize : function(r, w, h) {
51920                         _t.onResize(w,h); // -something
51921                     }
51922                 }
51923             });
51924             
51925         }
51926         this.createToolbar(this);
51927        
51928         
51929         if(!this.width){
51930             this.setSize(this.wrap.getSize());
51931         }
51932         if (this.resizeEl) {
51933             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
51934             // should trigger onReize..
51935         }
51936         
51937         this.keyNav = new Roo.KeyNav(this.el, {
51938             
51939             "tab" : function(e){
51940                 e.preventDefault();
51941                 
51942                 var value = this.getValue();
51943                 
51944                 var start = this.el.dom.selectionStart;
51945                 var end = this.el.dom.selectionEnd;
51946                 
51947                 if(!e.shiftKey){
51948                     
51949                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
51950                     this.el.dom.setSelectionRange(end + 1, end + 1);
51951                     return;
51952                 }
51953                 
51954                 var f = value.substring(0, start).split("\t");
51955                 
51956                 if(f.pop().length != 0){
51957                     return;
51958                 }
51959                 
51960                 this.setValue(f.join("\t") + value.substring(end));
51961                 this.el.dom.setSelectionRange(start - 1, start - 1);
51962                 
51963             },
51964             
51965             "home" : function(e){
51966                 e.preventDefault();
51967                 
51968                 var curr = this.el.dom.selectionStart;
51969                 var lines = this.getValue().split("\n");
51970                 
51971                 if(!lines.length){
51972                     return;
51973                 }
51974                 
51975                 if(e.ctrlKey){
51976                     this.el.dom.setSelectionRange(0, 0);
51977                     return;
51978                 }
51979                 
51980                 var pos = 0;
51981                 
51982                 for (var i = 0; i < lines.length;i++) {
51983                     pos += lines[i].length;
51984                     
51985                     if(i != 0){
51986                         pos += 1;
51987                     }
51988                     
51989                     if(pos < curr){
51990                         continue;
51991                     }
51992                     
51993                     pos -= lines[i].length;
51994                     
51995                     break;
51996                 }
51997                 
51998                 if(!e.shiftKey){
51999                     this.el.dom.setSelectionRange(pos, pos);
52000                     return;
52001                 }
52002                 
52003                 this.el.dom.selectionStart = pos;
52004                 this.el.dom.selectionEnd = curr;
52005             },
52006             
52007             "end" : function(e){
52008                 e.preventDefault();
52009                 
52010                 var curr = this.el.dom.selectionStart;
52011                 var lines = this.getValue().split("\n");
52012                 
52013                 if(!lines.length){
52014                     return;
52015                 }
52016                 
52017                 if(e.ctrlKey){
52018                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
52019                     return;
52020                 }
52021                 
52022                 var pos = 0;
52023                 
52024                 for (var i = 0; i < lines.length;i++) {
52025                     
52026                     pos += lines[i].length;
52027                     
52028                     if(i != 0){
52029                         pos += 1;
52030                     }
52031                     
52032                     if(pos < curr){
52033                         continue;
52034                     }
52035                     
52036                     break;
52037                 }
52038                 
52039                 if(!e.shiftKey){
52040                     this.el.dom.setSelectionRange(pos, pos);
52041                     return;
52042                 }
52043                 
52044                 this.el.dom.selectionStart = curr;
52045                 this.el.dom.selectionEnd = pos;
52046             },
52047
52048             scope : this,
52049
52050             doRelay : function(foo, bar, hname){
52051                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
52052             },
52053
52054             forceKeyDown: true
52055         });
52056         
52057 //        if(this.autosave && this.w){
52058 //            this.autoSaveFn = setInterval(this.autosave, 1000);
52059 //        }
52060     },
52061
52062     // private
52063     onResize : function(w, h)
52064     {
52065         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
52066         var ew = false;
52067         var eh = false;
52068         
52069         if(this.el ){
52070             if(typeof w == 'number'){
52071                 var aw = w - this.wrap.getFrameWidth('lr');
52072                 this.el.setWidth(this.adjustWidth('textarea', aw));
52073                 ew = aw;
52074             }
52075             if(typeof h == 'number'){
52076                 var tbh = 0;
52077                 for (var i =0; i < this.toolbars.length;i++) {
52078                     // fixme - ask toolbars for heights?
52079                     tbh += this.toolbars[i].tb.el.getHeight();
52080                     if (this.toolbars[i].footer) {
52081                         tbh += this.toolbars[i].footer.el.getHeight();
52082                     }
52083                 }
52084                 
52085                 
52086                 
52087                 
52088                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
52089                 ah -= 5; // knock a few pixes off for look..
52090 //                Roo.log(ah);
52091                 this.el.setHeight(this.adjustWidth('textarea', ah));
52092                 var eh = ah;
52093             }
52094         }
52095         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
52096         this.editorcore.onResize(ew,eh);
52097         
52098     },
52099
52100     /**
52101      * Toggles the editor between standard and source edit mode.
52102      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
52103      */
52104     toggleSourceEdit : function(sourceEditMode)
52105     {
52106         this.editorcore.toggleSourceEdit(sourceEditMode);
52107         
52108         if(this.editorcore.sourceEditMode){
52109             Roo.log('editor - showing textarea');
52110             
52111 //            Roo.log('in');
52112 //            Roo.log(this.syncValue());
52113             this.editorcore.syncValue();
52114             this.el.removeClass('x-hidden');
52115             this.el.dom.removeAttribute('tabIndex');
52116             this.el.focus();
52117             this.el.dom.scrollTop = 0;
52118             
52119             
52120             for (var i = 0; i < this.toolbars.length; i++) {
52121                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
52122                     this.toolbars[i].tb.hide();
52123                     this.toolbars[i].footer.hide();
52124                 }
52125             }
52126             
52127         }else{
52128             Roo.log('editor - hiding textarea');
52129 //            Roo.log('out')
52130 //            Roo.log(this.pushValue()); 
52131             this.editorcore.pushValue();
52132             
52133             this.el.addClass('x-hidden');
52134             this.el.dom.setAttribute('tabIndex', -1);
52135             
52136             for (var i = 0; i < this.toolbars.length; i++) {
52137                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
52138                     this.toolbars[i].tb.show();
52139                     this.toolbars[i].footer.show();
52140                 }
52141             }
52142             
52143             //this.deferFocus();
52144         }
52145         
52146         this.setSize(this.wrap.getSize());
52147         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
52148         
52149         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
52150     },
52151  
52152     // private (for BoxComponent)
52153     adjustSize : Roo.BoxComponent.prototype.adjustSize,
52154
52155     // private (for BoxComponent)
52156     getResizeEl : function(){
52157         return this.wrap;
52158     },
52159
52160     // private (for BoxComponent)
52161     getPositionEl : function(){
52162         return this.wrap;
52163     },
52164
52165     // private
52166     initEvents : function(){
52167         this.originalValue = this.getValue();
52168     },
52169
52170     /**
52171      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
52172      * @method
52173      */
52174     markInvalid : Roo.emptyFn,
52175     /**
52176      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
52177      * @method
52178      */
52179     clearInvalid : Roo.emptyFn,
52180
52181     setValue : function(v){
52182         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
52183         this.editorcore.pushValue();
52184     },
52185
52186     /**
52187      * update the language in the body - really done by core
52188      * @param {String} language - eg. en / ar / zh-CN etc..
52189      */
52190     updateLanguage : function(lang)
52191     {
52192         this.language = lang;
52193         this.editorcore.language = lang;
52194         this.editorcore.updateLanguage();
52195      
52196     },
52197     // private
52198     deferFocus : function(){
52199         this.focus.defer(10, this);
52200     },
52201
52202     // doc'ed in Field
52203     focus : function(){
52204         this.editorcore.focus();
52205         
52206     },
52207       
52208
52209     // private
52210     onDestroy : function(){
52211         
52212         
52213         
52214         if(this.rendered){
52215             
52216             for (var i =0; i < this.toolbars.length;i++) {
52217                 // fixme - ask toolbars for heights?
52218                 this.toolbars[i].onDestroy();
52219             }
52220             
52221             this.wrap.dom.innerHTML = '';
52222             this.wrap.remove();
52223         }
52224     },
52225
52226     // private
52227     onFirstFocus : function(){
52228         //Roo.log("onFirstFocus");
52229         this.editorcore.onFirstFocus();
52230          for (var i =0; i < this.toolbars.length;i++) {
52231             this.toolbars[i].onFirstFocus();
52232         }
52233         
52234     },
52235     
52236     // private
52237     syncValue : function()
52238     {
52239         this.editorcore.syncValue();
52240     },
52241     
52242     pushValue : function()
52243     {
52244         this.editorcore.pushValue();
52245     },
52246     
52247     setStylesheets : function(stylesheets)
52248     {
52249         this.editorcore.setStylesheets(stylesheets);
52250     },
52251     
52252     removeStylesheets : function()
52253     {
52254         this.editorcore.removeStylesheets();
52255     }
52256      
52257     
52258     // hide stuff that is not compatible
52259     /**
52260      * @event blur
52261      * @hide
52262      */
52263     /**
52264      * @event change
52265      * @hide
52266      */
52267     /**
52268      * @event focus
52269      * @hide
52270      */
52271     /**
52272      * @event specialkey
52273      * @hide
52274      */
52275     /**
52276      * @cfg {String} fieldClass @hide
52277      */
52278     /**
52279      * @cfg {String} focusClass @hide
52280      */
52281     /**
52282      * @cfg {String} autoCreate @hide
52283      */
52284     /**
52285      * @cfg {String} inputType @hide
52286      */
52287     /**
52288      * @cfg {String} invalidClass @hide
52289      */
52290     /**
52291      * @cfg {String} invalidText @hide
52292      */
52293     /**
52294      * @cfg {String} msgFx @hide
52295      */
52296     /**
52297      * @cfg {String} validateOnBlur @hide
52298      */
52299 });
52300  
52301     /*
52302  * Based on
52303  * Ext JS Library 1.1.1
52304  * Copyright(c) 2006-2007, Ext JS, LLC.
52305  *  
52306  
52307  */
52308
52309 /**
52310  * @class Roo.form.HtmlEditor.ToolbarStandard
52311  * Basic Toolbar
52312
52313  * Usage:
52314  *
52315  new Roo.form.HtmlEditor({
52316     ....
52317     toolbars : [
52318         new Roo.form.HtmlEditorToolbar1({
52319             disable : { fonts: 1 , format: 1, ..., ... , ...],
52320             btns : [ .... ]
52321         })
52322     }
52323      
52324  * 
52325  * @cfg {Object} disable List of elements to disable..
52326  * @cfg {Roo.Toolbar.Item|Roo.Toolbar.Button|Roo.Toolbar.SplitButton|Roo.form.Field} btns[] List of additional buttons.
52327  * 
52328  * 
52329  * NEEDS Extra CSS? 
52330  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
52331  */
52332  
52333 Roo.form.HtmlEditor.ToolbarStandard = function(config)
52334 {
52335     
52336     Roo.apply(this, config);
52337     
52338     // default disabled, based on 'good practice'..
52339     this.disable = this.disable || {};
52340     Roo.applyIf(this.disable, {
52341         fontSize : true,
52342         colors : true,
52343         specialElements : true
52344     });
52345     
52346     
52347     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
52348     // dont call parent... till later.
52349 }
52350
52351 Roo.form.HtmlEditor.ToolbarStandard.prototype = {
52352     
52353     tb: false,
52354     
52355     rendered: false,
52356     
52357     editor : false,
52358     editorcore : false,
52359     /**
52360      * @cfg {Object} disable  List of toolbar elements to disable
52361          
52362      */
52363     disable : false,
52364     
52365     
52366      /**
52367      * @cfg {String} createLinkText The default text for the create link prompt
52368      */
52369     createLinkText : 'Please enter the URL for the link:',
52370     /**
52371      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
52372      */
52373     defaultLinkValue : 'http:/'+'/',
52374    
52375     
52376       /**
52377      * @cfg {Array} fontFamilies An array of available font families
52378      */
52379     fontFamilies : [
52380         'Arial',
52381         'Courier New',
52382         'Tahoma',
52383         'Times New Roman',
52384         'Verdana'
52385     ],
52386     
52387     specialChars : [
52388            "&#169;",
52389           "&#174;",     
52390           "&#8482;",    
52391           "&#163;" ,    
52392          // "&#8212;",    
52393           "&#8230;",    
52394           "&#247;" ,    
52395         //  "&#225;" ,     ?? a acute?
52396            "&#8364;"    , //Euro
52397        //   "&#8220;"    ,
52398         //  "&#8221;"    ,
52399         //  "&#8226;"    ,
52400           "&#176;"  //   , // degrees
52401
52402          // "&#233;"     , // e ecute
52403          // "&#250;"     , // u ecute?
52404     ],
52405     
52406     specialElements : [
52407         {
52408             text: "Insert Table",
52409             xtype: 'MenuItem',
52410             xns : Roo.Menu,
52411             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
52412                 
52413         },
52414         {    
52415             text: "Insert Image",
52416             xtype: 'MenuItem',
52417             xns : Roo.Menu,
52418             ihtml : '<img src="about:blank"/>'
52419             
52420         }
52421         
52422          
52423     ],
52424     
52425     
52426     inputElements : [ 
52427             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
52428             "input:submit", "input:button", "select", "textarea", "label" ],
52429     formats : [
52430         ["p"] ,  
52431         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
52432         ["pre"],[ "code"], 
52433         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
52434         ['div'],['span'],
52435         ['sup'],['sub']
52436     ],
52437     
52438     cleanStyles : [
52439         "font-size"
52440     ],
52441      /**
52442      * @cfg {String} defaultFont default font to use.
52443      */
52444     defaultFont: 'tahoma',
52445    
52446     fontSelect : false,
52447     
52448     
52449     formatCombo : false,
52450     
52451     init : function(editor)
52452     {
52453         this.editor = editor;
52454         this.editorcore = editor.editorcore ? editor.editorcore : editor;
52455         var editorcore = this.editorcore;
52456         
52457         var _t = this;
52458         
52459         var fid = editorcore.frameId;
52460         var etb = this;
52461         function btn(id, toggle, handler){
52462             var xid = fid + '-'+ id ;
52463             return {
52464                 id : xid,
52465                 cmd : id,
52466                 cls : 'x-btn-icon x-edit-'+id,
52467                 enableToggle:toggle !== false,
52468                 scope: _t, // was editor...
52469                 handler:handler||_t.relayBtnCmd,
52470                 clickEvent:'mousedown',
52471                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52472                 tabIndex:-1
52473             };
52474         }
52475         
52476         
52477         
52478         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52479         this.tb = tb;
52480          // stop form submits
52481         tb.el.on('click', function(e){
52482             e.preventDefault(); // what does this do?
52483         });
52484
52485         if(!this.disable.font) { // && !Roo.isSafari){
52486             /* why no safari for fonts 
52487             editor.fontSelect = tb.el.createChild({
52488                 tag:'select',
52489                 tabIndex: -1,
52490                 cls:'x-font-select',
52491                 html: this.createFontOptions()
52492             });
52493             
52494             editor.fontSelect.on('change', function(){
52495                 var font = editor.fontSelect.dom.value;
52496                 editor.relayCmd('fontname', font);
52497                 editor.deferFocus();
52498             }, editor);
52499             
52500             tb.add(
52501                 editor.fontSelect.dom,
52502                 '-'
52503             );
52504             */
52505             
52506         };
52507         if(!this.disable.formats){
52508             this.formatCombo = new Roo.form.ComboBox({
52509                 store: new Roo.data.SimpleStore({
52510                     id : 'tag',
52511                     fields: ['tag'],
52512                     data : this.formats // from states.js
52513                 }),
52514                 blockFocus : true,
52515                 name : '',
52516                 //autoCreate : {tag: "div",  size: "20"},
52517                 displayField:'tag',
52518                 typeAhead: false,
52519                 mode: 'local',
52520                 editable : false,
52521                 triggerAction: 'all',
52522                 emptyText:'Add tag',
52523                 selectOnFocus:true,
52524                 width:135,
52525                 listeners : {
52526                     'select': function(c, r, i) {
52527                         editorcore.insertTag(r.get('tag'));
52528                         editor.focus();
52529                     }
52530                 }
52531
52532             });
52533             tb.addField(this.formatCombo);
52534             
52535         }
52536         
52537         if(!this.disable.format){
52538             tb.add(
52539                 btn('bold'),
52540                 btn('italic'),
52541                 btn('underline'),
52542                 btn('strikethrough')
52543             );
52544         };
52545         if(!this.disable.fontSize){
52546             tb.add(
52547                 '-',
52548                 
52549                 
52550                 btn('increasefontsize', false, editorcore.adjustFont),
52551                 btn('decreasefontsize', false, editorcore.adjustFont)
52552             );
52553         };
52554         
52555         
52556         if(!this.disable.colors){
52557             tb.add(
52558                 '-', {
52559                     id:editorcore.frameId +'-forecolor',
52560                     cls:'x-btn-icon x-edit-forecolor',
52561                     clickEvent:'mousedown',
52562                     tooltip: this.buttonTips['forecolor'] || undefined,
52563                     tabIndex:-1,
52564                     menu : new Roo.menu.ColorMenu({
52565                         allowReselect: true,
52566                         focus: Roo.emptyFn,
52567                         value:'000000',
52568                         plain:true,
52569                         selectHandler: function(cp, color){
52570                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
52571                             editor.deferFocus();
52572                         },
52573                         scope: editorcore,
52574                         clickEvent:'mousedown'
52575                     })
52576                 }, {
52577                     id:editorcore.frameId +'backcolor',
52578                     cls:'x-btn-icon x-edit-backcolor',
52579                     clickEvent:'mousedown',
52580                     tooltip: this.buttonTips['backcolor'] || undefined,
52581                     tabIndex:-1,
52582                     menu : new Roo.menu.ColorMenu({
52583                         focus: Roo.emptyFn,
52584                         value:'FFFFFF',
52585                         plain:true,
52586                         allowReselect: true,
52587                         selectHandler: function(cp, color){
52588                             if(Roo.isGecko){
52589                                 editorcore.execCmd('useCSS', false);
52590                                 editorcore.execCmd('hilitecolor', color);
52591                                 editorcore.execCmd('useCSS', true);
52592                                 editor.deferFocus();
52593                             }else{
52594                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
52595                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
52596                                 editor.deferFocus();
52597                             }
52598                         },
52599                         scope:editorcore,
52600                         clickEvent:'mousedown'
52601                     })
52602                 }
52603             );
52604         };
52605         // now add all the items...
52606         
52607
52608         if(!this.disable.alignments){
52609             tb.add(
52610                 '-',
52611                 btn('justifyleft'),
52612                 btn('justifycenter'),
52613                 btn('justifyright')
52614             );
52615         };
52616
52617         //if(!Roo.isSafari){
52618             if(!this.disable.links){
52619                 tb.add(
52620                     '-',
52621                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
52622                 );
52623             };
52624
52625             if(!this.disable.lists){
52626                 tb.add(
52627                     '-',
52628                     btn('insertorderedlist'),
52629                     btn('insertunorderedlist')
52630                 );
52631             }
52632             if(!this.disable.sourceEdit){
52633                 tb.add(
52634                     '-',
52635                     btn('sourceedit', true, function(btn){
52636                         this.toggleSourceEdit(btn.pressed);
52637                     })
52638                 );
52639             }
52640         //}
52641         
52642         var smenu = { };
52643         // special menu.. - needs to be tidied up..
52644         if (!this.disable.special) {
52645             smenu = {
52646                 text: "&#169;",
52647                 cls: 'x-edit-none',
52648                 
52649                 menu : {
52650                     items : []
52651                 }
52652             };
52653             for (var i =0; i < this.specialChars.length; i++) {
52654                 smenu.menu.items.push({
52655                     
52656                     html: this.specialChars[i],
52657                     handler: function(a,b) {
52658                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
52659                         //editor.insertAtCursor(a.html);
52660                         
52661                     },
52662                     tabIndex:-1
52663                 });
52664             }
52665             
52666             
52667             tb.add(smenu);
52668             
52669             
52670         }
52671         
52672         var cmenu = { };
52673         if (!this.disable.cleanStyles) {
52674             cmenu = {
52675                 cls: 'x-btn-icon x-btn-clear',
52676                 
52677                 menu : {
52678                     items : []
52679                 }
52680             };
52681             for (var i =0; i < this.cleanStyles.length; i++) {
52682                 cmenu.menu.items.push({
52683                     actiontype : this.cleanStyles[i],
52684                     html: 'Remove ' + this.cleanStyles[i],
52685                     handler: function(a,b) {
52686 //                        Roo.log(a);
52687 //                        Roo.log(b);
52688                         var c = Roo.get(editorcore.doc.body);
52689                         c.select('[style]').each(function(s) {
52690                             s.dom.style.removeProperty(a.actiontype);
52691                         });
52692                         editorcore.syncValue();
52693                     },
52694                     tabIndex:-1
52695                 });
52696             }
52697             cmenu.menu.items.push({
52698                 actiontype : 'tablewidths',
52699                 html: 'Remove Table Widths',
52700                 handler: function(a,b) {
52701                     editorcore.cleanTableWidths();
52702                     editorcore.syncValue();
52703                 },
52704                 tabIndex:-1
52705             });
52706             cmenu.menu.items.push({
52707                 actiontype : 'word',
52708                 html: 'Remove MS Word Formating',
52709                 handler: function(a,b) {
52710                     editorcore.cleanWord();
52711                     editorcore.syncValue();
52712                 },
52713                 tabIndex:-1
52714             });
52715             
52716             cmenu.menu.items.push({
52717                 actiontype : 'all',
52718                 html: 'Remove All Styles',
52719                 handler: function(a,b) {
52720                     
52721                     var c = Roo.get(editorcore.doc.body);
52722                     c.select('[style]').each(function(s) {
52723                         s.dom.removeAttribute('style');
52724                     });
52725                     editorcore.syncValue();
52726                 },
52727                 tabIndex:-1
52728             });
52729             
52730             cmenu.menu.items.push({
52731                 actiontype : 'all',
52732                 html: 'Remove All CSS Classes',
52733                 handler: function(a,b) {
52734                     
52735                     var c = Roo.get(editorcore.doc.body);
52736                     c.select('[class]').each(function(s) {
52737                         s.dom.removeAttribute('class');
52738                     });
52739                     editorcore.cleanWord();
52740                     editorcore.syncValue();
52741                 },
52742                 tabIndex:-1
52743             });
52744             
52745              cmenu.menu.items.push({
52746                 actiontype : 'tidy',
52747                 html: 'Tidy HTML Source',
52748                 handler: function(a,b) {
52749                     new Roo.htmleditor.Tidy(editorcore.doc.body);
52750                     editorcore.syncValue();
52751                 },
52752                 tabIndex:-1
52753             });
52754             
52755             
52756             tb.add(cmenu);
52757         }
52758          
52759         if (!this.disable.specialElements) {
52760             var semenu = {
52761                 text: "Other;",
52762                 cls: 'x-edit-none',
52763                 menu : {
52764                     items : []
52765                 }
52766             };
52767             for (var i =0; i < this.specialElements.length; i++) {
52768                 semenu.menu.items.push(
52769                     Roo.apply({ 
52770                         handler: function(a,b) {
52771                             editor.insertAtCursor(this.ihtml);
52772                         }
52773                     }, this.specialElements[i])
52774                 );
52775                     
52776             }
52777             
52778             tb.add(semenu);
52779             
52780             
52781         }
52782          
52783         
52784         if (this.btns) {
52785             for(var i =0; i< this.btns.length;i++) {
52786                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
52787                 b.cls =  'x-edit-none';
52788                 
52789                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
52790                     b.cls += ' x-init-enable';
52791                 }
52792                 
52793                 b.scope = editorcore;
52794                 tb.add(b);
52795             }
52796         
52797         }
52798         
52799         
52800         
52801         // disable everything...
52802         
52803         this.tb.items.each(function(item){
52804             
52805            if(
52806                 item.id != editorcore.frameId+ '-sourceedit' && 
52807                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
52808             ){
52809                 
52810                 item.disable();
52811             }
52812         });
52813         this.rendered = true;
52814         
52815         // the all the btns;
52816         editor.on('editorevent', this.updateToolbar, this);
52817         // other toolbars need to implement this..
52818         //editor.on('editmodechange', this.updateToolbar, this);
52819     },
52820     
52821     
52822     relayBtnCmd : function(btn) {
52823         this.editorcore.relayCmd(btn.cmd);
52824     },
52825     // private used internally
52826     createLink : function(){
52827         //Roo.log("create link?");
52828         var ec = this.editorcore;
52829         var ar = ec.getAllAncestors();
52830         var n = false;
52831         for(var i = 0;i< ar.length;i++) {
52832             if (ar[i] && ar[i].nodeName == 'A') {
52833                 n = ar[i];
52834                 break;
52835             }
52836         }
52837         
52838         (function() {
52839             
52840             Roo.MessageBox.show({
52841                 title : "Add / Edit Link URL",
52842                 msg : "Enter the url for the link",
52843                 buttons: Roo.MessageBox.OKCANCEL,
52844                 fn: function(btn, url){
52845                     if (btn != 'ok') {
52846                         return;
52847                     }
52848                     if(url && url != 'http:/'+'/'){
52849                         if (n) {
52850                             n.setAttribute('href', url);
52851                         } else {
52852                             ec.relayCmd('createlink', url);
52853                         }
52854                     }
52855                 },
52856                 minWidth:250,
52857                 prompt:true,
52858                 //multiline: multiline,
52859                 modal : true,
52860                 value :  n  ? n.getAttribute('href') : '' 
52861             });
52862             
52863              
52864         }).defer(100, this); // we have to defer this , otherwise the mouse click gives focus to the main window.
52865         
52866     },
52867
52868     
52869     /**
52870      * Protected method that will not generally be called directly. It triggers
52871      * a toolbar update by reading the markup state of the current selection in the editor.
52872      */
52873     updateToolbar: function(){
52874
52875         if(!this.editorcore.activated){
52876             this.editor.onFirstFocus();
52877             return;
52878         }
52879
52880         var btns = this.tb.items.map, 
52881             doc = this.editorcore.doc,
52882             frameId = this.editorcore.frameId;
52883
52884         if(!this.disable.font && !Roo.isSafari){
52885             /*
52886             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
52887             if(name != this.fontSelect.dom.value){
52888                 this.fontSelect.dom.value = name;
52889             }
52890             */
52891         }
52892         if(!this.disable.format){
52893             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
52894             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
52895             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
52896             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
52897         }
52898         if(!this.disable.alignments){
52899             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
52900             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
52901             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
52902         }
52903         if(!Roo.isSafari && !this.disable.lists){
52904             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
52905             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
52906         }
52907         
52908         var ans = this.editorcore.getAllAncestors();
52909         if (this.formatCombo) {
52910             
52911             
52912             var store = this.formatCombo.store;
52913             this.formatCombo.setValue("");
52914             for (var i =0; i < ans.length;i++) {
52915                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
52916                     // select it..
52917                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
52918                     break;
52919                 }
52920             }
52921         }
52922         
52923         
52924         
52925         // hides menus... - so this cant be on a menu...
52926         Roo.menu.MenuMgr.hideAll();
52927
52928         //this.editorsyncValue();
52929     },
52930    
52931     
52932     createFontOptions : function(){
52933         var buf = [], fs = this.fontFamilies, ff, lc;
52934         
52935         
52936         
52937         for(var i = 0, len = fs.length; i< len; i++){
52938             ff = fs[i];
52939             lc = ff.toLowerCase();
52940             buf.push(
52941                 '<option value="',lc,'" style="font-family:',ff,';"',
52942                     (this.defaultFont == lc ? ' selected="true">' : '>'),
52943                     ff,
52944                 '</option>'
52945             );
52946         }
52947         return buf.join('');
52948     },
52949     
52950     toggleSourceEdit : function(sourceEditMode){
52951         
52952         Roo.log("toolbar toogle");
52953         if(sourceEditMode === undefined){
52954             sourceEditMode = !this.sourceEditMode;
52955         }
52956         this.sourceEditMode = sourceEditMode === true;
52957         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
52958         // just toggle the button?
52959         if(btn.pressed !== this.sourceEditMode){
52960             btn.toggle(this.sourceEditMode);
52961             return;
52962         }
52963         
52964         if(sourceEditMode){
52965             Roo.log("disabling buttons");
52966             this.tb.items.each(function(item){
52967                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
52968                     item.disable();
52969                 }
52970             });
52971           
52972         }else{
52973             Roo.log("enabling buttons");
52974             if(this.editorcore.initialized){
52975                 this.tb.items.each(function(item){
52976                     item.enable();
52977                 });
52978                 // initialize 'blocks'
52979                 Roo.each(Roo.get(this.editorcore.doc.body).query('*[data-block]'), function(e) {
52980                     Roo.htmleditor.Block.factory(e).updateElement(e);
52981                 },this);
52982             
52983             }
52984             
52985         }
52986         Roo.log("calling toggole on editor");
52987         // tell the editor that it's been pressed..
52988         this.editor.toggleSourceEdit(sourceEditMode);
52989        
52990     },
52991      /**
52992      * Object collection of toolbar tooltips for the buttons in the editor. The key
52993      * is the command id associated with that button and the value is a valid QuickTips object.
52994      * For example:
52995 <pre><code>
52996 {
52997     bold : {
52998         title: 'Bold (Ctrl+B)',
52999         text: 'Make the selected text bold.',
53000         cls: 'x-html-editor-tip'
53001     },
53002     italic : {
53003         title: 'Italic (Ctrl+I)',
53004         text: 'Make the selected text italic.',
53005         cls: 'x-html-editor-tip'
53006     },
53007     ...
53008 </code></pre>
53009     * @type Object
53010      */
53011     buttonTips : {
53012         bold : {
53013             title: 'Bold (Ctrl+B)',
53014             text: 'Make the selected text bold.',
53015             cls: 'x-html-editor-tip'
53016         },
53017         italic : {
53018             title: 'Italic (Ctrl+I)',
53019             text: 'Make the selected text italic.',
53020             cls: 'x-html-editor-tip'
53021         },
53022         underline : {
53023             title: 'Underline (Ctrl+U)',
53024             text: 'Underline the selected text.',
53025             cls: 'x-html-editor-tip'
53026         },
53027         strikethrough : {
53028             title: 'Strikethrough',
53029             text: 'Strikethrough the selected text.',
53030             cls: 'x-html-editor-tip'
53031         },
53032         increasefontsize : {
53033             title: 'Grow Text',
53034             text: 'Increase the font size.',
53035             cls: 'x-html-editor-tip'
53036         },
53037         decreasefontsize : {
53038             title: 'Shrink Text',
53039             text: 'Decrease the font size.',
53040             cls: 'x-html-editor-tip'
53041         },
53042         backcolor : {
53043             title: 'Text Highlight Color',
53044             text: 'Change the background color of the selected text.',
53045             cls: 'x-html-editor-tip'
53046         },
53047         forecolor : {
53048             title: 'Font Color',
53049             text: 'Change the color of the selected text.',
53050             cls: 'x-html-editor-tip'
53051         },
53052         justifyleft : {
53053             title: 'Align Text Left',
53054             text: 'Align text to the left.',
53055             cls: 'x-html-editor-tip'
53056         },
53057         justifycenter : {
53058             title: 'Center Text',
53059             text: 'Center text in the editor.',
53060             cls: 'x-html-editor-tip'
53061         },
53062         justifyright : {
53063             title: 'Align Text Right',
53064             text: 'Align text to the right.',
53065             cls: 'x-html-editor-tip'
53066         },
53067         insertunorderedlist : {
53068             title: 'Bullet List',
53069             text: 'Start a bulleted list.',
53070             cls: 'x-html-editor-tip'
53071         },
53072         insertorderedlist : {
53073             title: 'Numbered List',
53074             text: 'Start a numbered list.',
53075             cls: 'x-html-editor-tip'
53076         },
53077         createlink : {
53078             title: 'Hyperlink',
53079             text: 'Make the selected text a hyperlink.',
53080             cls: 'x-html-editor-tip'
53081         },
53082         sourceedit : {
53083             title: 'Source Edit',
53084             text: 'Switch to source editing mode.',
53085             cls: 'x-html-editor-tip'
53086         }
53087     },
53088     // private
53089     onDestroy : function(){
53090         if(this.rendered){
53091             
53092             this.tb.items.each(function(item){
53093                 if(item.menu){
53094                     item.menu.removeAll();
53095                     if(item.menu.el){
53096                         item.menu.el.destroy();
53097                     }
53098                 }
53099                 item.destroy();
53100             });
53101              
53102         }
53103     },
53104     onFirstFocus: function() {
53105         this.tb.items.each(function(item){
53106            item.enable();
53107         });
53108     }
53109 };
53110
53111
53112
53113
53114 // <script type="text/javascript">
53115 /*
53116  * Based on
53117  * Ext JS Library 1.1.1
53118  * Copyright(c) 2006-2007, Ext JS, LLC.
53119  *  
53120  
53121  */
53122
53123  
53124 /**
53125  * @class Roo.form.HtmlEditor.ToolbarContext
53126  * Context Toolbar
53127  * 
53128  * Usage:
53129  *
53130  new Roo.form.HtmlEditor({
53131     ....
53132     toolbars : [
53133         { xtype: 'ToolbarStandard', styles : {} }
53134         { xtype: 'ToolbarContext', disable : {} }
53135     ]
53136 })
53137
53138      
53139  * 
53140  * @config : {Object} disable List of elements to disable.. (not done yet.)
53141  * @config : {Object} styles  Map of styles available.
53142  * 
53143  */
53144
53145 Roo.form.HtmlEditor.ToolbarContext = function(config)
53146 {
53147     
53148     Roo.apply(this, config);
53149     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
53150     // dont call parent... till later.
53151     this.styles = this.styles || {};
53152 }
53153
53154  
53155
53156 Roo.form.HtmlEditor.ToolbarContext.types = {
53157     'IMG' : [
53158         {
53159             name : 'width',
53160             title: "Width",
53161             width: 40
53162         },
53163         {
53164             name : 'height',
53165             title: "Height",
53166             width: 40
53167         },
53168         {
53169             name : 'align',
53170             title: "Align",
53171             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
53172             width : 80
53173             
53174         },
53175         {
53176             name : 'border',
53177             title: "Border",
53178             width: 40
53179         },
53180         {
53181             name : 'alt',
53182             title: "Alt",
53183             width: 120
53184         },
53185         {
53186             name : 'src',
53187             title: "Src",
53188             width: 220
53189         }
53190         
53191     ],
53192     
53193     'FIGURE' : [
53194         {
53195             name : 'align',
53196             title: "Align",
53197             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
53198             width : 80  
53199         }
53200     ],
53201     'A' : [
53202         {
53203             name : 'name',
53204             title: "Name",
53205             width: 50
53206         },
53207         {
53208             name : 'target',
53209             title: "Target",
53210             width: 120
53211         },
53212         {
53213             name : 'href',
53214             title: "Href",
53215             width: 220
53216         } // border?
53217         
53218     ],
53219     
53220     'INPUT' : [
53221         {
53222             name : 'name',
53223             title: "name",
53224             width: 120
53225         },
53226         {
53227             name : 'value',
53228             title: "Value",
53229             width: 120
53230         },
53231         {
53232             name : 'width',
53233             title: "Width",
53234             width: 40
53235         }
53236     ],
53237     'LABEL' : [
53238          {
53239             name : 'for',
53240             title: "For",
53241             width: 120
53242         }
53243     ],
53244     'TEXTAREA' : [
53245         {
53246             name : 'name',
53247             title: "name",
53248             width: 120
53249         },
53250         {
53251             name : 'rows',
53252             title: "Rows",
53253             width: 20
53254         },
53255         {
53256             name : 'cols',
53257             title: "Cols",
53258             width: 20
53259         }
53260     ],
53261     'SELECT' : [
53262         {
53263             name : 'name',
53264             title: "name",
53265             width: 120
53266         },
53267         {
53268             name : 'selectoptions',
53269             title: "Options",
53270             width: 200
53271         }
53272     ],
53273     
53274     // should we really allow this??
53275     // should this just be 
53276     'BODY' : [
53277         
53278         {
53279             name : 'title',
53280             title: "Title",
53281             width: 200,
53282             disabled : true
53283         }
53284     ],
53285  
53286     '*' : [
53287         // empty.
53288     ]
53289
53290 };
53291
53292 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
53293 Roo.form.HtmlEditor.ToolbarContext.stores = false;
53294
53295 Roo.form.HtmlEditor.ToolbarContext.options = {
53296         'font-family'  : [ 
53297                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
53298                 [ 'Courier New', 'Courier New'],
53299                 [ 'Tahoma', 'Tahoma'],
53300                 [ 'Times New Roman,serif', 'Times'],
53301                 [ 'Verdana','Verdana' ]
53302         ]
53303 };
53304
53305 // fixme - these need to be configurable..
53306  
53307
53308 //Roo.form.HtmlEditor.ToolbarContext.types
53309
53310
53311 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
53312     
53313     tb: false,
53314     
53315     rendered: false,
53316     
53317     editor : false,
53318     editorcore : false,
53319     /**
53320      * @cfg {Object} disable  List of toolbar elements to disable
53321          
53322      */
53323     disable : false,
53324     /**
53325      * @cfg {Object} styles List of styles 
53326      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
53327      *
53328      * These must be defined in the page, so they get rendered correctly..
53329      * .headline { }
53330      * TD.underline { }
53331      * 
53332      */
53333     styles : false,
53334     
53335     options: false,
53336     
53337     toolbars : false,
53338     
53339     init : function(editor)
53340     {
53341         this.editor = editor;
53342         this.editorcore = editor.editorcore ? editor.editorcore : editor;
53343         var editorcore = this.editorcore;
53344         
53345         var fid = editorcore.frameId;
53346         var etb = this;
53347         function btn(id, toggle, handler){
53348             var xid = fid + '-'+ id ;
53349             return {
53350                 id : xid,
53351                 cmd : id,
53352                 cls : 'x-btn-icon x-edit-'+id,
53353                 enableToggle:toggle !== false,
53354                 scope: editorcore, // was editor...
53355                 handler:handler||editorcore.relayBtnCmd,
53356                 clickEvent:'mousedown',
53357                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53358                 tabIndex:-1
53359             };
53360         }
53361         // create a new element.
53362         var wdiv = editor.wrap.createChild({
53363                 tag: 'div'
53364             }, editor.wrap.dom.firstChild.nextSibling, true);
53365         
53366         // can we do this more than once??
53367         
53368          // stop form submits
53369       
53370  
53371         // disable everything...
53372         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
53373         this.toolbars = {};
53374         // block toolbars are built in updateToolbar when needed.
53375         for (var i in  ty) {
53376             
53377             this.toolbars[i] = this.buildToolbar(ty[i],i);
53378         }
53379         this.tb = this.toolbars.BODY;
53380         this.tb.el.show();
53381         this.buildFooter();
53382         this.footer.show();
53383         editor.on('hide', function( ) { this.footer.hide() }, this);
53384         editor.on('show', function( ) { this.footer.show() }, this);
53385         
53386          
53387         this.rendered = true;
53388         
53389         // the all the btns;
53390         editor.on('editorevent', this.updateToolbar, this);
53391         // other toolbars need to implement this..
53392         //editor.on('editmodechange', this.updateToolbar, this);
53393     },
53394     
53395     
53396     
53397     /**
53398      * Protected method that will not generally be called directly. It triggers
53399      * a toolbar update by reading the markup state of the current selection in the editor.
53400      *
53401      * Note you can force an update by calling on('editorevent', scope, false)
53402      */
53403     updateToolbar: function(editor ,ev, sel)
53404     {
53405         
53406         if (ev) {
53407             ev.stopEvent(); // se if we can stop this looping with mutiple events.
53408         }
53409         
53410         //Roo.log(ev);
53411         // capture mouse up - this is handy for selecting images..
53412         // perhaps should go somewhere else...
53413         if(!this.editorcore.activated){
53414              this.editor.onFirstFocus();
53415             return;
53416         }
53417         //Roo.log(ev ? ev.target : 'NOTARGET');
53418         
53419         
53420         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
53421         // selectNode - might want to handle IE?
53422         
53423         
53424         
53425         if (ev &&
53426             (ev.type == 'mouseup' || ev.type == 'click' ) &&
53427             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
53428             // they have click on an image...
53429             // let's see if we can change the selection...
53430             sel = ev.target;
53431             
53432             // this triggers looping?
53433             //this.editorcore.selectNode(sel);
53434              
53435         }
53436         
53437         // this forces an id..
53438         Array.from(this.editorcore.doc.body.querySelectorAll('.roo-ed-selection')).forEach(function(e) {
53439              e.classList.remove('roo-ed-selection');
53440         });
53441         //Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
53442         //Roo.get(node).addClass('roo-ed-selection');
53443       
53444         //var updateFooter = sel ? false : true; 
53445         
53446         
53447         var ans = this.editorcore.getAllAncestors();
53448         
53449         // pick
53450         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
53451         
53452         if (!sel) { 
53453             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
53454             sel = sel ? sel : this.editorcore.doc.body;
53455             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
53456             
53457         }
53458         
53459         var tn = sel.tagName.toUpperCase();
53460         var lastSel = this.tb.selectedNode;
53461         this.tb.selectedNode = sel;
53462         var left_label = tn;
53463         
53464         // ok see if we are editing a block?
53465         
53466         var db = false;
53467         // you are not actually selecting the block.
53468         if (sel && sel.hasAttribute('data-block')) {
53469             db = sel;
53470         } else if (sel && sel.closest('[data-block]')) {
53471             
53472             db = sel.closest('[data-block]');
53473             //var cepar = sel.closest('[contenteditable=true]');
53474             //if (db && cepar && cepar.tagName != 'BODY') {
53475             //   db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
53476             //}   
53477         }
53478         
53479         
53480         var block = false;
53481         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
53482         if (db && this.editorcore.enableBlocks) {
53483             block = Roo.htmleditor.Block.factory(db);
53484             
53485             
53486             if (block) {
53487                  db.className = (
53488                         db.classList.length > 0  ? db.className + ' ' : ''
53489                     )  + 'roo-ed-selection';
53490                  
53491                  // since we removed it earlier... its not there..
53492                 tn = 'BLOCK.' + db.getAttribute('data-block');
53493                 
53494                 //this.editorcore.selectNode(db);
53495                 if (typeof(this.toolbars[tn]) == 'undefined') {
53496                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
53497                 }
53498                 this.toolbars[tn].selectedNode = db;
53499                 left_label = block.friendly_name;
53500                 ans = this.editorcore.getAllAncestors();
53501             }
53502             
53503                 
53504             
53505         }
53506         
53507         
53508         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
53509             return; // no change?
53510         }
53511         
53512         
53513           
53514         this.tb.el.hide();
53515         ///console.log("show: " + tn);
53516         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
53517         
53518         this.tb.el.show();
53519         // update name
53520         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
53521         
53522         
53523         // update attributes
53524         if (block && this.tb.fields) {
53525              
53526             this.tb.fields.each(function(e) {
53527                 e.setValue(block[e.name]);
53528             });
53529             
53530             
53531         } else  if (this.tb.fields && this.tb.selectedNode) {
53532             this.tb.fields.each( function(e) {
53533                 if (e.stylename) {
53534                     e.setValue(this.tb.selectedNode.style[e.stylename]);
53535                     return;
53536                 } 
53537                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
53538             }, this);
53539             this.updateToolbarStyles(this.tb.selectedNode);  
53540         }
53541         
53542         
53543        
53544         Roo.menu.MenuMgr.hideAll();
53545
53546         
53547         
53548     
53549         // update the footer
53550         //
53551         this.updateFooter(ans);
53552              
53553     },
53554     
53555     updateToolbarStyles : function(sel)
53556     {
53557         var hasStyles = false;
53558         for(var i in this.styles) {
53559             hasStyles = true;
53560             break;
53561         }
53562         
53563         // update styles
53564         if (hasStyles && this.tb.hasStyles) { 
53565             var st = this.tb.fields.item(0);
53566             
53567             st.store.removeAll();
53568             var cn = sel.className.split(/\s+/);
53569             
53570             var avs = [];
53571             if (this.styles['*']) {
53572                 
53573                 Roo.each(this.styles['*'], function(v) {
53574                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
53575                 });
53576             }
53577             if (this.styles[tn]) { 
53578                 Roo.each(this.styles[tn], function(v) {
53579                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
53580                 });
53581             }
53582             
53583             st.store.loadData(avs);
53584             st.collapse();
53585             st.setValue(cn);
53586         }
53587     },
53588     
53589      
53590     updateFooter : function(ans)
53591     {
53592         var html = '';
53593         if (ans === false) {
53594             this.footDisp.dom.innerHTML = '';
53595             return;
53596         }
53597         
53598         this.footerEls = ans.reverse();
53599         Roo.each(this.footerEls, function(a,i) {
53600             if (!a) { return; }
53601             html += html.length ? ' &gt; '  :  '';
53602             
53603             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
53604             
53605         });
53606        
53607         // 
53608         var sz = this.footDisp.up('td').getSize();
53609         this.footDisp.dom.style.width = (sz.width -10) + 'px';
53610         this.footDisp.dom.style.marginLeft = '5px';
53611         
53612         this.footDisp.dom.style.overflow = 'hidden';
53613         
53614         this.footDisp.dom.innerHTML = html;
53615             
53616         
53617     },
53618    
53619        
53620     // private
53621     onDestroy : function(){
53622         if(this.rendered){
53623             
53624             this.tb.items.each(function(item){
53625                 if(item.menu){
53626                     item.menu.removeAll();
53627                     if(item.menu.el){
53628                         item.menu.el.destroy();
53629                     }
53630                 }
53631                 item.destroy();
53632             });
53633              
53634         }
53635     },
53636     onFirstFocus: function() {
53637         // need to do this for all the toolbars..
53638         this.tb.items.each(function(item){
53639            item.enable();
53640         });
53641     },
53642     buildToolbar: function(tlist, nm, friendly_name, block)
53643     {
53644         var editor = this.editor;
53645         var editorcore = this.editorcore;
53646          // create a new element.
53647         var wdiv = editor.wrap.createChild({
53648                 tag: 'div'
53649             }, editor.wrap.dom.firstChild.nextSibling, true);
53650         
53651        
53652         var tb = new Roo.Toolbar(wdiv);
53653         ///this.tb = tb; // << this sets the active toolbar..
53654         if (tlist === false && block) {
53655             tlist = block.contextMenu(this);
53656         }
53657         
53658         tb.hasStyles = false;
53659         tb.name = nm;
53660         
53661         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
53662         
53663         var styles = Array.from(this.styles);
53664         
53665         
53666         // styles...
53667         if (styles && styles.length) {
53668             tb.hasStyles = true;
53669             // this needs a multi-select checkbox...
53670             tb.addField( new Roo.form.ComboBox({
53671                 store: new Roo.data.SimpleStore({
53672                     id : 'val',
53673                     fields: ['val', 'selected'],
53674                     data : [] 
53675                 }),
53676                 name : '-roo-edit-className',
53677                 attrname : 'className',
53678                 displayField: 'val',
53679                 typeAhead: false,
53680                 mode: 'local',
53681                 editable : false,
53682                 triggerAction: 'all',
53683                 emptyText:'Select Style',
53684                 selectOnFocus:true,
53685                 width: 130,
53686                 listeners : {
53687                     'select': function(c, r, i) {
53688                         // initial support only for on class per el..
53689                         tb.selectedNode.className =  r ? r.get('val') : '';
53690                         editorcore.syncValue();
53691                     }
53692                 }
53693     
53694             }));
53695         }
53696         
53697         var tbc = Roo.form.HtmlEditor.ToolbarContext;
53698         
53699         
53700         for (var i = 0; i < tlist.length; i++) {
53701             
53702             // newer versions will use xtype cfg to create menus.
53703             if (typeof(tlist[i].xtype) != 'undefined') {
53704                 
53705                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
53706                 
53707                 
53708                 continue;
53709             }
53710             
53711             var item = tlist[i];
53712             tb.add(item.title + ":&nbsp;");
53713             
53714             
53715             //optname == used so you can configure the options available..
53716             var opts = item.opts ? item.opts : false;
53717             if (item.optname) { // use the b
53718                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
53719            
53720             }
53721             
53722             if (opts) {
53723                 // opts == pulldown..
53724                 tb.addField( new Roo.form.ComboBox({
53725                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
53726                         id : 'val',
53727                         fields: ['val', 'display'],
53728                         data : opts  
53729                     }),
53730                     name : '-roo-edit-' + tlist[i].name,
53731                     
53732                     attrname : tlist[i].name,
53733                     stylename : item.style ? item.style : false,
53734                     
53735                     displayField: item.displayField ? item.displayField : 'val',
53736                     valueField :  'val',
53737                     typeAhead: false,
53738                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
53739                     editable : false,
53740                     triggerAction: 'all',
53741                     emptyText:'Select',
53742                     selectOnFocus:true,
53743                     width: item.width ? item.width  : 130,
53744                     listeners : {
53745                         'select': function(c, r, i) {
53746                              
53747                             
53748                             if (c.stylename) {
53749                                 tb.selectedNode.style[c.stylename] =  r.get('val');
53750                                 editorcore.syncValue();
53751                                 return;
53752                             }
53753                             if (r === false) {
53754                                 tb.selectedNode.removeAttribute(c.attrname);
53755                                 editorcore.syncValue();
53756                                 return;
53757                             }
53758                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
53759                             editorcore.syncValue();
53760                         }
53761                     }
53762
53763                 }));
53764                 continue;
53765                     
53766                  
53767                 /*
53768                 tb.addField( new Roo.form.TextField({
53769                     name: i,
53770                     width: 100,
53771                     //allowBlank:false,
53772                     value: ''
53773                 }));
53774                 continue;
53775                 */
53776             }
53777             tb.addField( new Roo.form.TextField({
53778                 name: '-roo-edit-' + tlist[i].name,
53779                 attrname : tlist[i].name,
53780                 
53781                 width: item.width,
53782                 //allowBlank:true,
53783                 value: '',
53784                 listeners: {
53785                     'change' : function(f, nv, ov) {
53786                         
53787                          
53788                         tb.selectedNode.setAttribute(f.attrname, nv);
53789                         editorcore.syncValue();
53790                     }
53791                 }
53792             }));
53793              
53794         }
53795         
53796         var _this = this;
53797         var show_delete = !block || block.deleteTitle !== false;
53798         if(nm == 'BODY'){
53799             show_delete = false;
53800             tb.addSeparator();
53801         
53802             tb.addButton( {
53803                 text: 'Stylesheets',
53804
53805                 listeners : {
53806                     click : function ()
53807                     {
53808                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
53809                     }
53810                 }
53811             });
53812         }
53813         
53814         tb.addFill();
53815         if (show_delete) {
53816             tb.addButton({
53817                 text: block && block.deleteTitle ? block.deleteTitle  : 'Remove Block or Formating', // remove the tag, and puts the children outside...
53818         
53819                 listeners : {
53820                     click : function ()
53821                     {
53822                         var sn = tb.selectedNode;
53823                         if (block) {
53824                             sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
53825                             
53826                         }
53827                         if (!sn) {
53828                             return;
53829                         }
53830                         var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
53831                         if (sn.hasAttribute('data-block')) {
53832                             stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
53833                             sn.parentNode.removeChild(sn);
53834                             
53835                         } else if (sn && sn.tagName != 'BODY') {
53836                             // remove and keep parents.
53837                             a = new Roo.htmleditor.FilterKeepChildren({tag : false});
53838                             a.replaceTag(sn);
53839                         }
53840                         
53841                         
53842                         var range = editorcore.createRange();
53843             
53844                         range.setStart(stn,0);
53845                         range.setEnd(stn,0); 
53846                         var selection = editorcore.getSelection();
53847                         selection.removeAllRanges();
53848                         selection.addRange(range);
53849                         
53850                         
53851                         //_this.updateToolbar(null, null, pn);
53852                         _this.updateToolbar(null, null, null);
53853                         _this.updateFooter(false);
53854                         
53855                     }
53856                 }
53857                 
53858                         
53859                     
53860                 
53861             });
53862         }    
53863         
53864         tb.el.on('click', function(e){
53865             e.preventDefault(); // what does this do?
53866         });
53867         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
53868         tb.el.hide();
53869         
53870         // dont need to disable them... as they will get hidden
53871         return tb;
53872          
53873         
53874     },
53875     buildFooter : function()
53876     {
53877         
53878         var fel = this.editor.wrap.createChild();
53879         this.footer = new Roo.Toolbar(fel);
53880         // toolbar has scrolly on left / right?
53881         var footDisp= new Roo.Toolbar.Fill();
53882         var _t = this;
53883         this.footer.add(
53884             {
53885                 text : '&lt;',
53886                 xtype: 'Button',
53887                 handler : function() {
53888                     _t.footDisp.scrollTo('left',0,true)
53889                 }
53890             }
53891         );
53892         this.footer.add( footDisp );
53893         this.footer.add( 
53894             {
53895                 text : '&gt;',
53896                 xtype: 'Button',
53897                 handler : function() {
53898                     // no animation..
53899                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
53900                 }
53901             }
53902         );
53903         var fel = Roo.get(footDisp.el);
53904         fel.addClass('x-editor-context');
53905         this.footDispWrap = fel; 
53906         this.footDispWrap.overflow  = 'hidden';
53907         
53908         this.footDisp = fel.createChild();
53909         this.footDispWrap.on('click', this.onContextClick, this)
53910         
53911         
53912     },
53913     // when the footer contect changes
53914     onContextClick : function (ev,dom)
53915     {
53916         ev.preventDefault();
53917         var  cn = dom.className;
53918         //Roo.log(cn);
53919         if (!cn.match(/x-ed-loc-/)) {
53920             return;
53921         }
53922         var n = cn.split('-').pop();
53923         var ans = this.footerEls;
53924         var sel = ans[n];
53925         
53926         this.editorcore.selectNode(sel);
53927         
53928         
53929         this.updateToolbar(null, null, sel);
53930         
53931         
53932     }
53933     
53934     
53935     
53936     
53937     
53938 });
53939
53940
53941
53942
53943
53944 /*
53945  * Based on:
53946  * Ext JS Library 1.1.1
53947  * Copyright(c) 2006-2007, Ext JS, LLC.
53948  *
53949  * Originally Released Under LGPL - original licence link has changed is not relivant.
53950  *
53951  * Fork - LGPL
53952  * <script type="text/javascript">
53953  */
53954  
53955 /**
53956  * @class Roo.form.BasicForm
53957  * @extends Roo.util.Observable
53958  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
53959  * @constructor
53960  * @param {String/HTMLElement/Roo.Element} el The form element or its id
53961  * @param {Object} config Configuration options
53962  */
53963 Roo.form.BasicForm = function(el, config){
53964     this.allItems = [];
53965     this.childForms = [];
53966     Roo.apply(this, config);
53967     /*
53968      * The Roo.form.Field items in this form.
53969      * @type MixedCollection
53970      */
53971      
53972      
53973     this.items = new Roo.util.MixedCollection(false, function(o){
53974         return o.id || (o.id = Roo.id());
53975     });
53976     this.addEvents({
53977         /**
53978          * @event beforeaction
53979          * Fires before any action is performed. Return false to cancel the action.
53980          * @param {Form} this
53981          * @param {Action} action The action to be performed
53982          */
53983         beforeaction: true,
53984         /**
53985          * @event actionfailed
53986          * Fires when an action fails.
53987          * @param {Form} this
53988          * @param {Action} action The action that failed
53989          */
53990         actionfailed : true,
53991         /**
53992          * @event actioncomplete
53993          * Fires when an action is completed.
53994          * @param {Form} this
53995          * @param {Action} action The action that completed
53996          */
53997         actioncomplete : true
53998     });
53999     if(el){
54000         this.initEl(el);
54001     }
54002     Roo.form.BasicForm.superclass.constructor.call(this);
54003     
54004     Roo.form.BasicForm.popover.apply();
54005 };
54006
54007 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
54008     /**
54009      * @cfg {String} method
54010      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
54011      */
54012     /**
54013      * @cfg {DataReader} reader
54014      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
54015      * This is optional as there is built-in support for processing JSON.
54016      */
54017     /**
54018      * @cfg {DataReader} errorReader
54019      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
54020      * This is completely optional as there is built-in support for processing JSON.
54021      */
54022     /**
54023      * @cfg {String} url
54024      * The URL to use for form actions if one isn't supplied in the action options.
54025      */
54026     /**
54027      * @cfg {Boolean} fileUpload
54028      * Set to true if this form is a file upload.
54029      */
54030      
54031     /**
54032      * @cfg {Object} baseParams
54033      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
54034      */
54035      /**
54036      
54037     /**
54038      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
54039      */
54040     timeout: 30,
54041
54042     // private
54043     activeAction : null,
54044
54045     /**
54046      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
54047      * or setValues() data instead of when the form was first created.
54048      */
54049     trackResetOnLoad : false,
54050     
54051     
54052     /**
54053      * childForms - used for multi-tab forms
54054      * @type {Array}
54055      */
54056     childForms : false,
54057     
54058     /**
54059      * allItems - full list of fields.
54060      * @type {Array}
54061      */
54062     allItems : false,
54063     
54064     /**
54065      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
54066      * element by passing it or its id or mask the form itself by passing in true.
54067      * @type Mixed
54068      */
54069     waitMsgTarget : false,
54070     
54071     /**
54072      * @type Boolean
54073      */
54074     disableMask : false,
54075     
54076     /**
54077      * @cfg {Boolean} errorMask (true|false) default false
54078      */
54079     errorMask : false,
54080     
54081     /**
54082      * @cfg {Number} maskOffset Default 100
54083      */
54084     maskOffset : 100,
54085
54086     // private
54087     initEl : function(el){
54088         this.el = Roo.get(el);
54089         this.id = this.el.id || Roo.id();
54090         this.el.on('submit', this.onSubmit, this);
54091         this.el.addClass('x-form');
54092     },
54093
54094     // private
54095     onSubmit : function(e){
54096         e.stopEvent();
54097     },
54098
54099     /**
54100      * Returns true if client-side validation on the form is successful.
54101      * @return Boolean
54102      */
54103     isValid : function(){
54104         var valid = true;
54105         var target = false;
54106         this.items.each(function(f){
54107             if(f.validate()){
54108                 return;
54109             }
54110             
54111             valid = false;
54112                 
54113             if(!target && f.el.isVisible(true)){
54114                 target = f;
54115             }
54116         });
54117         
54118         if(this.errorMask && !valid){
54119             Roo.form.BasicForm.popover.mask(this, target);
54120         }
54121         
54122         return valid;
54123     },
54124     /**
54125      * Returns array of invalid form fields.
54126      * @return Array
54127      */
54128     
54129     invalidFields : function()
54130     {
54131         var ret = [];
54132         this.items.each(function(f){
54133             if(f.validate()){
54134                 return;
54135             }
54136             ret.push(f);
54137             
54138         });
54139         
54140         return ret;
54141     },
54142     
54143     
54144     /**
54145      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
54146      * @return Boolean
54147      */
54148     isDirty : function(){
54149         var dirty = false;
54150         this.items.each(function(f){
54151            if(f.isDirty()){
54152                dirty = true;
54153                return false;
54154            }
54155         });
54156         return dirty;
54157     },
54158     
54159     /**
54160      * Returns true if any fields in this form have changed since their original load. (New version)
54161      * @return Boolean
54162      */
54163     
54164     hasChanged : function()
54165     {
54166         var dirty = false;
54167         this.items.each(function(f){
54168            if(f.hasChanged()){
54169                dirty = true;
54170                return false;
54171            }
54172         });
54173         return dirty;
54174         
54175     },
54176     /**
54177      * Resets all hasChanged to 'false' -
54178      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
54179      * So hasChanged storage is only to be used for this purpose
54180      * @return Boolean
54181      */
54182     resetHasChanged : function()
54183     {
54184         this.items.each(function(f){
54185            f.resetHasChanged();
54186         });
54187         
54188     },
54189     
54190     
54191     /**
54192      * Performs a predefined action (submit or load) or custom actions you define on this form.
54193      * @param {String} actionName The name of the action type
54194      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
54195      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
54196      * accept other config options):
54197      * <pre>
54198 Property          Type             Description
54199 ----------------  ---------------  ----------------------------------------------------------------------------------
54200 url               String           The url for the action (defaults to the form's url)
54201 method            String           The form method to use (defaults to the form's method, or POST if not defined)
54202 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
54203 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
54204                                    validate the form on the client (defaults to false)
54205      * </pre>
54206      * @return {BasicForm} this
54207      */
54208     doAction : function(action, options){
54209         if(typeof action == 'string'){
54210             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
54211         }
54212         if(this.fireEvent('beforeaction', this, action) !== false){
54213             this.beforeAction(action);
54214             action.run.defer(100, action);
54215         }
54216         return this;
54217     },
54218
54219     /**
54220      * Shortcut to do a submit action.
54221      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
54222      * @return {BasicForm} this
54223      */
54224     submit : function(options){
54225         this.doAction('submit', options);
54226         return this;
54227     },
54228
54229     /**
54230      * Shortcut to do a load action.
54231      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
54232      * @return {BasicForm} this
54233      */
54234     load : function(options){
54235         this.doAction('load', options);
54236         return this;
54237     },
54238
54239     /**
54240      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
54241      * @param {Record} record The record to edit
54242      * @return {BasicForm} this
54243      */
54244     updateRecord : function(record){
54245         record.beginEdit();
54246         var fs = record.fields;
54247         fs.each(function(f){
54248             var field = this.findField(f.name);
54249             if(field){
54250                 record.set(f.name, field.getValue());
54251             }
54252         }, this);
54253         record.endEdit();
54254         return this;
54255     },
54256
54257     /**
54258      * Loads an Roo.data.Record into this form.
54259      * @param {Record} record The record to load
54260      * @return {BasicForm} this
54261      */
54262     loadRecord : function(record){
54263         this.setValues(record.data);
54264         return this;
54265     },
54266
54267     // private
54268     beforeAction : function(action){
54269         var o = action.options;
54270         
54271         if(!this.disableMask) {
54272             if(this.waitMsgTarget === true){
54273                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
54274             }else if(this.waitMsgTarget){
54275                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
54276                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
54277             }else {
54278                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
54279             }
54280         }
54281         
54282          
54283     },
54284
54285     // private
54286     afterAction : function(action, success){
54287         this.activeAction = null;
54288         var o = action.options;
54289         
54290         if(!this.disableMask) {
54291             if(this.waitMsgTarget === true){
54292                 this.el.unmask();
54293             }else if(this.waitMsgTarget){
54294                 this.waitMsgTarget.unmask();
54295             }else{
54296                 Roo.MessageBox.updateProgress(1);
54297                 Roo.MessageBox.hide();
54298             }
54299         }
54300         
54301         if(success){
54302             if(o.reset){
54303                 this.reset();
54304             }
54305             Roo.callback(o.success, o.scope, [this, action]);
54306             this.fireEvent('actioncomplete', this, action);
54307             
54308         }else{
54309             
54310             // failure condition..
54311             // we have a scenario where updates need confirming.
54312             // eg. if a locking scenario exists..
54313             // we look for { errors : { needs_confirm : true }} in the response.
54314             if (
54315                 (typeof(action.result) != 'undefined')  &&
54316                 (typeof(action.result.errors) != 'undefined')  &&
54317                 (typeof(action.result.errors.needs_confirm) != 'undefined')
54318            ){
54319                 var _t = this;
54320                 Roo.MessageBox.confirm(
54321                     "Change requires confirmation",
54322                     action.result.errorMsg,
54323                     function(r) {
54324                         if (r != 'yes') {
54325                             return;
54326                         }
54327                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
54328                     }
54329                     
54330                 );
54331                 
54332                 
54333                 
54334                 return;
54335             }
54336             
54337             Roo.callback(o.failure, o.scope, [this, action]);
54338             // show an error message if no failed handler is set..
54339             if (!this.hasListener('actionfailed')) {
54340                 Roo.MessageBox.alert("Error",
54341                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
54342                         action.result.errorMsg :
54343                         "Saving Failed, please check your entries or try again"
54344                 );
54345             }
54346             
54347             this.fireEvent('actionfailed', this, action);
54348         }
54349         
54350     },
54351
54352     /**
54353      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
54354      * @param {String} id The value to search for
54355      * @return Field
54356      */
54357     findField : function(id){
54358         var field = this.items.get(id);
54359         if(!field){
54360             this.items.each(function(f){
54361                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
54362                     field = f;
54363                     return false;
54364                 }
54365             });
54366         }
54367         return field || null;
54368     },
54369
54370     /**
54371      * Add a secondary form to this one, 
54372      * Used to provide tabbed forms. One form is primary, with hidden values 
54373      * which mirror the elements from the other forms.
54374      * 
54375      * @param {Roo.form.Form} form to add.
54376      * 
54377      */
54378     addForm : function(form)
54379     {
54380        
54381         if (this.childForms.indexOf(form) > -1) {
54382             // already added..
54383             return;
54384         }
54385         this.childForms.push(form);
54386         var n = '';
54387         Roo.each(form.allItems, function (fe) {
54388             
54389             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
54390             if (this.findField(n)) { // already added..
54391                 return;
54392             }
54393             var add = new Roo.form.Hidden({
54394                 name : n
54395             });
54396             add.render(this.el);
54397             
54398             this.add( add );
54399         }, this);
54400         
54401     },
54402     /**
54403      * Mark fields in this form invalid in bulk.
54404      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
54405      * @return {BasicForm} this
54406      */
54407     markInvalid : function(errors){
54408         if(errors instanceof Array){
54409             for(var i = 0, len = errors.length; i < len; i++){
54410                 var fieldError = errors[i];
54411                 var f = this.findField(fieldError.id);
54412                 if(f){
54413                     f.markInvalid(fieldError.msg);
54414                 }
54415             }
54416         }else{
54417             var field, id;
54418             for(id in errors){
54419                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
54420                     field.markInvalid(errors[id]);
54421                 }
54422             }
54423         }
54424         Roo.each(this.childForms || [], function (f) {
54425             f.markInvalid(errors);
54426         });
54427         
54428         return this;
54429     },
54430
54431     /**
54432      * Set values for fields in this form in bulk.
54433      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
54434      * @return {BasicForm} this
54435      */
54436     setValues : function(values){
54437         if(values instanceof Array){ // array of objects
54438             for(var i = 0, len = values.length; i < len; i++){
54439                 var v = values[i];
54440                 var f = this.findField(v.id);
54441                 if(f){
54442                     f.setValue(v.value);
54443                     if(this.trackResetOnLoad){
54444                         f.originalValue = f.getValue();
54445                     }
54446                 }
54447             }
54448         }else{ // object hash
54449             var field, id;
54450             for(id in values){
54451                 if(typeof values[id] != 'function' && (field = this.findField(id))){
54452                     
54453                     
54454                     
54455                     
54456                     if (field.setFromData && 
54457                         field.valueField && 
54458                         field.displayField &&
54459                         // combos' with local stores can 
54460                         // be queried via setValue()
54461                         // to set their value..
54462                         (field.store && !field.store.isLocal)
54463                         ) {
54464                         // it's a combo
54465                         var sd = { };
54466                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
54467                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
54468                         field.setFromData(sd);
54469                         
54470                     } else if (field.inputType && field.inputType == 'radio') {
54471                         
54472                         field.setValue(values[id]);
54473                     } else {
54474                         field.setValue(values[id]);
54475                     }
54476                     
54477                     
54478                     if(this.trackResetOnLoad){
54479                         field.originalValue = field.getValue();
54480                     }
54481                 }
54482             }
54483         }
54484         this.resetHasChanged();
54485         
54486         
54487         Roo.each(this.childForms || [], function (f) {
54488             f.setValues(values);
54489             f.resetHasChanged();
54490         });
54491                 
54492         return this;
54493     },
54494  
54495     /**
54496      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
54497      * they are returned as an array.
54498      * @param {Boolean} asString (def)
54499      * @return {Object}
54500      */
54501     getValues : function(asString)
54502     {
54503         if (this.childForms) {
54504             // copy values from the child forms
54505             Roo.each(this.childForms, function (f) {
54506                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
54507             }, this);
54508         }
54509         
54510         // use formdata
54511         if (typeof(FormData) != 'undefined' && asString !== true) {
54512             // this relies on a 'recent' version of chrome apparently...
54513             try {
54514                 var fd = (new FormData(this.el.dom)).entries();
54515                 var ret = {};
54516                 var ent = fd.next();
54517                 while (!ent.done) {
54518                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
54519                     ent = fd.next();
54520                 };
54521                 return ret;
54522             } catch(e) {
54523                 
54524             }
54525             
54526         }
54527         
54528         
54529         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
54530         if(asString === true){
54531             return fs;
54532         }
54533         return Roo.urlDecode(fs);
54534     },
54535     
54536     /**
54537      * Returns the fields in this form as an object with key/value pairs. 
54538      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
54539      * Normally this will not return readOnly data 
54540      * @param {Boolean} with_readonly return readonly field data.
54541      * @return {Object}
54542      */
54543     getFieldValues : function(with_readonly)
54544     {
54545         if (this.childForms) {
54546             // copy values from the child forms
54547             // should this call getFieldValues - probably not as we do not currently copy
54548             // hidden fields when we generate..
54549             Roo.each(this.childForms, function (f) {
54550                 this.setValues(f.getFieldValues());
54551             }, this);
54552         }
54553         
54554         var ret = {};
54555         this.items.each(function(f){
54556             
54557             if (f.readOnly && with_readonly !== true) {
54558                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
54559                         // if a subform contains a copy of them.
54560                         // if you have subforms with the same editable data, you will need to copy the data back
54561                         // and forth.
54562             }
54563             
54564             if (!f.getName()) {
54565                 return;
54566             }
54567             var v = f.getValue();
54568             if (f.inputType =='radio') {
54569                 if (typeof(ret[f.getName()]) == 'undefined') {
54570                     ret[f.getName()] = ''; // empty..
54571                 }
54572                 
54573                 if (!f.el.dom.checked) {
54574                     return;
54575                     
54576                 }
54577                 v = f.el.dom.value;
54578                 
54579             }
54580             
54581             // not sure if this supported any more..
54582             if ((typeof(v) == 'object') && f.getRawValue) {
54583                 v = f.getRawValue() ; // dates..
54584             }
54585             // combo boxes where name != hiddenName...
54586             if (f.name != f.getName()) {
54587                 ret[f.name] = f.getRawValue();
54588             }
54589             ret[f.getName()] = v;
54590         });
54591         
54592         return ret;
54593     },
54594
54595     /**
54596      * Clears all invalid messages in this form.
54597      * @return {BasicForm} this
54598      */
54599     clearInvalid : function(){
54600         this.items.each(function(f){
54601            f.clearInvalid();
54602         });
54603         
54604         Roo.each(this.childForms || [], function (f) {
54605             f.clearInvalid();
54606         });
54607         
54608         
54609         return this;
54610     },
54611
54612     /**
54613      * Resets this form.
54614      * @return {BasicForm} this
54615      */
54616     reset : function(){
54617         this.items.each(function(f){
54618             f.reset();
54619         });
54620         
54621         Roo.each(this.childForms || [], function (f) {
54622             f.reset();
54623         });
54624         this.resetHasChanged();
54625         
54626         return this;
54627     },
54628
54629     /**
54630      * Add Roo.form components to this form.
54631      * @param {Field} field1
54632      * @param {Field} field2 (optional)
54633      * @param {Field} etc (optional)
54634      * @return {BasicForm} this
54635      */
54636     add : function(){
54637         this.items.addAll(Array.prototype.slice.call(arguments, 0));
54638         return this;
54639     },
54640
54641
54642     /**
54643      * Removes a field from the items collection (does NOT remove its markup).
54644      * @param {Field} field
54645      * @return {BasicForm} this
54646      */
54647     remove : function(field){
54648         this.items.remove(field);
54649         return this;
54650     },
54651
54652     /**
54653      * Looks at the fields in this form, checks them for an id attribute,
54654      * and calls applyTo on the existing dom element with that id.
54655      * @return {BasicForm} this
54656      */
54657     render : function(){
54658         this.items.each(function(f){
54659             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
54660                 f.applyTo(f.id);
54661             }
54662         });
54663         return this;
54664     },
54665
54666     /**
54667      * Calls {@link Ext#apply} for all fields in this form with the passed object.
54668      * @param {Object} values
54669      * @return {BasicForm} this
54670      */
54671     applyToFields : function(o){
54672         this.items.each(function(f){
54673            Roo.apply(f, o);
54674         });
54675         return this;
54676     },
54677
54678     /**
54679      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
54680      * @param {Object} values
54681      * @return {BasicForm} this
54682      */
54683     applyIfToFields : function(o){
54684         this.items.each(function(f){
54685            Roo.applyIf(f, o);
54686         });
54687         return this;
54688     }
54689 });
54690
54691 // back compat
54692 Roo.BasicForm = Roo.form.BasicForm;
54693
54694 Roo.apply(Roo.form.BasicForm, {
54695     
54696     popover : {
54697         
54698         padding : 5,
54699         
54700         isApplied : false,
54701         
54702         isMasked : false,
54703         
54704         form : false,
54705         
54706         target : false,
54707         
54708         intervalID : false,
54709         
54710         maskEl : false,
54711         
54712         apply : function()
54713         {
54714             if(this.isApplied){
54715                 return;
54716             }
54717             
54718             this.maskEl = {
54719                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
54720                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
54721                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
54722                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
54723             };
54724             
54725             this.maskEl.top.enableDisplayMode("block");
54726             this.maskEl.left.enableDisplayMode("block");
54727             this.maskEl.bottom.enableDisplayMode("block");
54728             this.maskEl.right.enableDisplayMode("block");
54729             
54730             Roo.get(document.body).on('click', function(){
54731                 this.unmask();
54732             }, this);
54733             
54734             Roo.get(document.body).on('touchstart', function(){
54735                 this.unmask();
54736             }, this);
54737             
54738             this.isApplied = true
54739         },
54740         
54741         mask : function(form, target)
54742         {
54743             this.form = form;
54744             
54745             this.target = target;
54746             
54747             if(!this.form.errorMask || !target.el){
54748                 return;
54749             }
54750             
54751             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
54752             
54753             var ot = this.target.el.calcOffsetsTo(scrollable);
54754             
54755             var scrollTo = ot[1] - this.form.maskOffset;
54756             
54757             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
54758             
54759             scrollable.scrollTo('top', scrollTo);
54760             
54761             var el = this.target.wrap || this.target.el;
54762             
54763             var box = el.getBox();
54764             
54765             this.maskEl.top.setStyle('position', 'absolute');
54766             this.maskEl.top.setStyle('z-index', 10000);
54767             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
54768             this.maskEl.top.setLeft(0);
54769             this.maskEl.top.setTop(0);
54770             this.maskEl.top.show();
54771             
54772             this.maskEl.left.setStyle('position', 'absolute');
54773             this.maskEl.left.setStyle('z-index', 10000);
54774             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
54775             this.maskEl.left.setLeft(0);
54776             this.maskEl.left.setTop(box.y - this.padding);
54777             this.maskEl.left.show();
54778
54779             this.maskEl.bottom.setStyle('position', 'absolute');
54780             this.maskEl.bottom.setStyle('z-index', 10000);
54781             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
54782             this.maskEl.bottom.setLeft(0);
54783             this.maskEl.bottom.setTop(box.bottom + this.padding);
54784             this.maskEl.bottom.show();
54785
54786             this.maskEl.right.setStyle('position', 'absolute');
54787             this.maskEl.right.setStyle('z-index', 10000);
54788             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
54789             this.maskEl.right.setLeft(box.right + this.padding);
54790             this.maskEl.right.setTop(box.y - this.padding);
54791             this.maskEl.right.show();
54792
54793             this.intervalID = window.setInterval(function() {
54794                 Roo.form.BasicForm.popover.unmask();
54795             }, 10000);
54796
54797             window.onwheel = function(){ return false;};
54798             
54799             (function(){ this.isMasked = true; }).defer(500, this);
54800             
54801         },
54802         
54803         unmask : function()
54804         {
54805             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
54806                 return;
54807             }
54808             
54809             this.maskEl.top.setStyle('position', 'absolute');
54810             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
54811             this.maskEl.top.hide();
54812
54813             this.maskEl.left.setStyle('position', 'absolute');
54814             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
54815             this.maskEl.left.hide();
54816
54817             this.maskEl.bottom.setStyle('position', 'absolute');
54818             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
54819             this.maskEl.bottom.hide();
54820
54821             this.maskEl.right.setStyle('position', 'absolute');
54822             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
54823             this.maskEl.right.hide();
54824             
54825             window.onwheel = function(){ return true;};
54826             
54827             if(this.intervalID){
54828                 window.clearInterval(this.intervalID);
54829                 this.intervalID = false;
54830             }
54831             
54832             this.isMasked = false;
54833             
54834         }
54835         
54836     }
54837     
54838 });/*
54839  * Based on:
54840  * Ext JS Library 1.1.1
54841  * Copyright(c) 2006-2007, Ext JS, LLC.
54842  *
54843  * Originally Released Under LGPL - original licence link has changed is not relivant.
54844  *
54845  * Fork - LGPL
54846  * <script type="text/javascript">
54847  */
54848
54849 /**
54850  * @class Roo.form.Form
54851  * @extends Roo.form.BasicForm
54852  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
54853  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
54854  * @constructor
54855  * @param {Object} config Configuration options
54856  */
54857 Roo.form.Form = function(config){
54858     var xitems =  [];
54859     if (config.items) {
54860         xitems = config.items;
54861         delete config.items;
54862     }
54863    
54864     
54865     Roo.form.Form.superclass.constructor.call(this, null, config);
54866     this.url = this.url || this.action;
54867     if(!this.root){
54868         this.root = new Roo.form.Layout(Roo.applyIf({
54869             id: Roo.id()
54870         }, config));
54871     }
54872     this.active = this.root;
54873     /**
54874      * Array of all the buttons that have been added to this form via {@link addButton}
54875      * @type Array
54876      */
54877     this.buttons = [];
54878     this.allItems = [];
54879     this.addEvents({
54880         /**
54881          * @event clientvalidation
54882          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
54883          * @param {Form} this
54884          * @param {Boolean} valid true if the form has passed client-side validation
54885          */
54886         clientvalidation: true,
54887         /**
54888          * @event rendered
54889          * Fires when the form is rendered
54890          * @param {Roo.form.Form} form
54891          */
54892         rendered : true
54893     });
54894     
54895     if (this.progressUrl) {
54896             // push a hidden field onto the list of fields..
54897             this.addxtype( {
54898                     xns: Roo.form, 
54899                     xtype : 'Hidden', 
54900                     name : 'UPLOAD_IDENTIFIER' 
54901             });
54902         }
54903         
54904     
54905     Roo.each(xitems, this.addxtype, this);
54906     
54907 };
54908
54909 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
54910      /**
54911      * @cfg {Roo.Button} buttons[] buttons at bottom of form
54912      */
54913     
54914     /**
54915      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
54916      */
54917     /**
54918      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
54919      */
54920     /**
54921      * @cfg {String} (left|center|right) buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
54922      */
54923     buttonAlign:'center',
54924
54925     /**
54926      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
54927      */
54928     minButtonWidth:75,
54929
54930     /**
54931      * @cfg {String} labelAlign (left|top|right) Valid values are "left," "top" and "right" (defaults to "left").
54932      * This property cascades to child containers if not set.
54933      */
54934     labelAlign:'left',
54935
54936     /**
54937      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
54938      * fires a looping event with that state. This is required to bind buttons to the valid
54939      * state using the config value formBind:true on the button.
54940      */
54941     monitorValid : false,
54942
54943     /**
54944      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
54945      */
54946     monitorPoll : 200,
54947     
54948     /**
54949      * @cfg {String} progressUrl - Url to return progress data 
54950      */
54951     
54952     progressUrl : false,
54953     /**
54954      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
54955      * sending a formdata with extra parameters - eg uploaded elements.
54956      */
54957     
54958     formData : false,
54959     
54960     /**
54961      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
54962      * fields are added and the column is closed. If no fields are passed the column remains open
54963      * until end() is called.
54964      * @param {Object} config The config to pass to the column
54965      * @param {Field} field1 (optional)
54966      * @param {Field} field2 (optional)
54967      * @param {Field} etc (optional)
54968      * @return Column The column container object
54969      */
54970     column : function(c){
54971         var col = new Roo.form.Column(c);
54972         this.start(col);
54973         if(arguments.length > 1){ // duplicate code required because of Opera
54974             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54975             this.end();
54976         }
54977         return col;
54978     },
54979
54980     /**
54981      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
54982      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
54983      * until end() is called.
54984      * @param {Object} config The config to pass to the fieldset
54985      * @param {Field} field1 (optional)
54986      * @param {Field} field2 (optional)
54987      * @param {Field} etc (optional)
54988      * @return FieldSet The fieldset container object
54989      */
54990     fieldset : function(c){
54991         var fs = new Roo.form.FieldSet(c);
54992         this.start(fs);
54993         if(arguments.length > 1){ // duplicate code required because of Opera
54994             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
54995             this.end();
54996         }
54997         return fs;
54998     },
54999
55000     /**
55001      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
55002      * fields are added and the container is closed. If no fields are passed the container remains open
55003      * until end() is called.
55004      * @param {Object} config The config to pass to the Layout
55005      * @param {Field} field1 (optional)
55006      * @param {Field} field2 (optional)
55007      * @param {Field} etc (optional)
55008      * @return Layout The container object
55009      */
55010     container : function(c){
55011         var l = new Roo.form.Layout(c);
55012         this.start(l);
55013         if(arguments.length > 1){ // duplicate code required because of Opera
55014             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
55015             this.end();
55016         }
55017         return l;
55018     },
55019
55020     /**
55021      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
55022      * @param {Object} container A Roo.form.Layout or subclass of Layout
55023      * @return {Form} this
55024      */
55025     start : function(c){
55026         // cascade label info
55027         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
55028         this.active.stack.push(c);
55029         c.ownerCt = this.active;
55030         this.active = c;
55031         return this;
55032     },
55033
55034     /**
55035      * Closes the current open container
55036      * @return {Form} this
55037      */
55038     end : function(){
55039         if(this.active == this.root){
55040             return this;
55041         }
55042         this.active = this.active.ownerCt;
55043         return this;
55044     },
55045
55046     /**
55047      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
55048      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
55049      * as the label of the field.
55050      * @param {Field} field1
55051      * @param {Field} field2 (optional)
55052      * @param {Field} etc. (optional)
55053      * @return {Form} this
55054      */
55055     add : function(){
55056         this.active.stack.push.apply(this.active.stack, arguments);
55057         this.allItems.push.apply(this.allItems,arguments);
55058         var r = [];
55059         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
55060             if(a[i].isFormField){
55061                 r.push(a[i]);
55062             }
55063         }
55064         if(r.length > 0){
55065             Roo.form.Form.superclass.add.apply(this, r);
55066         }
55067         return this;
55068     },
55069     
55070
55071     
55072     
55073     
55074      /**
55075      * Find any element that has been added to a form, using it's ID or name
55076      * This can include framesets, columns etc. along with regular fields..
55077      * @param {String} id - id or name to find.
55078      
55079      * @return {Element} e - or false if nothing found.
55080      */
55081     findbyId : function(id)
55082     {
55083         var ret = false;
55084         if (!id) {
55085             return ret;
55086         }
55087         Roo.each(this.allItems, function(f){
55088             if (f.id == id || f.name == id ){
55089                 ret = f;
55090                 return false;
55091             }
55092         });
55093         return ret;
55094     },
55095
55096     
55097     
55098     /**
55099      * Render this form into the passed container. This should only be called once!
55100      * @param {String/HTMLElement/Element} container The element this component should be rendered into
55101      * @return {Form} this
55102      */
55103     render : function(ct)
55104     {
55105         
55106         
55107         
55108         ct = Roo.get(ct);
55109         var o = this.autoCreate || {
55110             tag: 'form',
55111             method : this.method || 'POST',
55112             id : this.id || Roo.id()
55113         };
55114         this.initEl(ct.createChild(o));
55115
55116         this.root.render(this.el);
55117         
55118        
55119              
55120         this.items.each(function(f){
55121             f.render('x-form-el-'+f.id);
55122         });
55123
55124         if(this.buttons.length > 0){
55125             // tables are required to maintain order and for correct IE layout
55126             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
55127                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
55128                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
55129             }}, null, true);
55130             var tr = tb.getElementsByTagName('tr')[0];
55131             for(var i = 0, len = this.buttons.length; i < len; i++) {
55132                 var b = this.buttons[i];
55133                 var td = document.createElement('td');
55134                 td.className = 'x-form-btn-td';
55135                 b.render(tr.appendChild(td));
55136             }
55137         }
55138         if(this.monitorValid){ // initialize after render
55139             this.startMonitoring();
55140         }
55141         this.fireEvent('rendered', this);
55142         return this;
55143     },
55144
55145     /**
55146      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
55147      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
55148      * object or a valid Roo.DomHelper element config
55149      * @param {Function} handler The function called when the button is clicked
55150      * @param {Object} scope (optional) The scope of the handler function
55151      * @return {Roo.Button}
55152      */
55153     addButton : function(config, handler, scope){
55154         var bc = {
55155             handler: handler,
55156             scope: scope,
55157             minWidth: this.minButtonWidth,
55158             hideParent:true
55159         };
55160         if(typeof config == "string"){
55161             bc.text = config;
55162         }else{
55163             Roo.apply(bc, config);
55164         }
55165         var btn = new Roo.Button(null, bc);
55166         this.buttons.push(btn);
55167         return btn;
55168     },
55169
55170      /**
55171      * Adds a series of form elements (using the xtype property as the factory method.
55172      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
55173      * @param {Object} config 
55174      */
55175     
55176     addxtype : function()
55177     {
55178         var ar = Array.prototype.slice.call(arguments, 0);
55179         var ret = false;
55180         for(var i = 0; i < ar.length; i++) {
55181             if (!ar[i]) {
55182                 continue; // skip -- if this happends something invalid got sent, we 
55183                 // should ignore it, as basically that interface element will not show up
55184                 // and that should be pretty obvious!!
55185             }
55186             
55187             if (Roo.form[ar[i].xtype]) {
55188                 ar[i].form = this;
55189                 var fe = Roo.factory(ar[i], Roo.form);
55190                 if (!ret) {
55191                     ret = fe;
55192                 }
55193                 fe.form = this;
55194                 if (fe.store) {
55195                     fe.store.form = this;
55196                 }
55197                 if (fe.isLayout) {  
55198                          
55199                     this.start(fe);
55200                     this.allItems.push(fe);
55201                     if (fe.items && fe.addxtype) {
55202                         fe.addxtype.apply(fe, fe.items);
55203                         delete fe.items;
55204                     }
55205                      this.end();
55206                     continue;
55207                 }
55208                 
55209                 
55210                  
55211                 this.add(fe);
55212               //  console.log('adding ' + ar[i].xtype);
55213             }
55214             if (ar[i].xtype == 'Button') {  
55215                 //console.log('adding button');
55216                 //console.log(ar[i]);
55217                 this.addButton(ar[i]);
55218                 this.allItems.push(fe);
55219                 continue;
55220             }
55221             
55222             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
55223                 alert('end is not supported on xtype any more, use items');
55224             //    this.end();
55225             //    //console.log('adding end');
55226             }
55227             
55228         }
55229         return ret;
55230     },
55231     
55232     /**
55233      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
55234      * option "monitorValid"
55235      */
55236     startMonitoring : function(){
55237         if(!this.bound){
55238             this.bound = true;
55239             Roo.TaskMgr.start({
55240                 run : this.bindHandler,
55241                 interval : this.monitorPoll || 200,
55242                 scope: this
55243             });
55244         }
55245     },
55246
55247     /**
55248      * Stops monitoring of the valid state of this form
55249      */
55250     stopMonitoring : function(){
55251         this.bound = false;
55252     },
55253
55254     // private
55255     bindHandler : function(){
55256         if(!this.bound){
55257             return false; // stops binding
55258         }
55259         var valid = true;
55260         this.items.each(function(f){
55261             if(!f.isValid(true)){
55262                 valid = false;
55263                 return false;
55264             }
55265         });
55266         for(var i = 0, len = this.buttons.length; i < len; i++){
55267             var btn = this.buttons[i];
55268             if(btn.formBind === true && btn.disabled === valid){
55269                 btn.setDisabled(!valid);
55270             }
55271         }
55272         this.fireEvent('clientvalidation', this, valid);
55273     }
55274     
55275     
55276     
55277     
55278     
55279     
55280     
55281     
55282 });
55283
55284
55285 // back compat
55286 Roo.Form = Roo.form.Form;
55287 /*
55288  * Based on:
55289  * Ext JS Library 1.1.1
55290  * Copyright(c) 2006-2007, Ext JS, LLC.
55291  *
55292  * Originally Released Under LGPL - original licence link has changed is not relivant.
55293  *
55294  * Fork - LGPL
55295  * <script type="text/javascript">
55296  */
55297
55298 // as we use this in bootstrap.
55299 Roo.namespace('Roo.form');
55300  /**
55301  * @class Roo.form.Action
55302  * Internal Class used to handle form actions
55303  * @constructor
55304  * @param {Roo.form.BasicForm} el The form element or its id
55305  * @param {Object} config Configuration options
55306  */
55307
55308  
55309  
55310 // define the action interface
55311 Roo.form.Action = function(form, options){
55312     this.form = form;
55313     this.options = options || {};
55314 };
55315 /**
55316  * Client Validation Failed
55317  * @const 
55318  */
55319 Roo.form.Action.CLIENT_INVALID = 'client';
55320 /**
55321  * Server Validation Failed
55322  * @const 
55323  */
55324 Roo.form.Action.SERVER_INVALID = 'server';
55325  /**
55326  * Connect to Server Failed
55327  * @const 
55328  */
55329 Roo.form.Action.CONNECT_FAILURE = 'connect';
55330 /**
55331  * Reading Data from Server Failed
55332  * @const 
55333  */
55334 Roo.form.Action.LOAD_FAILURE = 'load';
55335
55336 Roo.form.Action.prototype = {
55337     type : 'default',
55338     failureType : undefined,
55339     response : undefined,
55340     result : undefined,
55341
55342     // interface method
55343     run : function(options){
55344
55345     },
55346
55347     // interface method
55348     success : function(response){
55349
55350     },
55351
55352     // interface method
55353     handleResponse : function(response){
55354
55355     },
55356
55357     // default connection failure
55358     failure : function(response){
55359         
55360         this.response = response;
55361         this.failureType = Roo.form.Action.CONNECT_FAILURE;
55362         this.form.afterAction(this, false);
55363     },
55364
55365     processResponse : function(response){
55366         this.response = response;
55367         if(!response.responseText){
55368             return true;
55369         }
55370         this.result = this.handleResponse(response);
55371         return this.result;
55372     },
55373
55374     // utility functions used internally
55375     getUrl : function(appendParams){
55376         var url = this.options.url || this.form.url || this.form.el.dom.action;
55377         if(appendParams){
55378             var p = this.getParams();
55379             if(p){
55380                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
55381             }
55382         }
55383         return url;
55384     },
55385
55386     getMethod : function(){
55387         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
55388     },
55389
55390     getParams : function(){
55391         var bp = this.form.baseParams;
55392         var p = this.options.params;
55393         if(p){
55394             if(typeof p == "object"){
55395                 p = Roo.urlEncode(Roo.applyIf(p, bp));
55396             }else if(typeof p == 'string' && bp){
55397                 p += '&' + Roo.urlEncode(bp);
55398             }
55399         }else if(bp){
55400             p = Roo.urlEncode(bp);
55401         }
55402         return p;
55403     },
55404
55405     createCallback : function(){
55406         return {
55407             success: this.success,
55408             failure: this.failure,
55409             scope: this,
55410             timeout: (this.form.timeout*1000),
55411             upload: this.form.fileUpload ? this.success : undefined
55412         };
55413     }
55414 };
55415
55416 Roo.form.Action.Submit = function(form, options){
55417     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
55418 };
55419
55420 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
55421     type : 'submit',
55422
55423     haveProgress : false,
55424     uploadComplete : false,
55425     
55426     // uploadProgress indicator.
55427     uploadProgress : function()
55428     {
55429         if (!this.form.progressUrl) {
55430             return;
55431         }
55432         
55433         if (!this.haveProgress) {
55434             Roo.MessageBox.progress("Uploading", "Uploading");
55435         }
55436         if (this.uploadComplete) {
55437            Roo.MessageBox.hide();
55438            return;
55439         }
55440         
55441         this.haveProgress = true;
55442    
55443         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
55444         
55445         var c = new Roo.data.Connection();
55446         c.request({
55447             url : this.form.progressUrl,
55448             params: {
55449                 id : uid
55450             },
55451             method: 'GET',
55452             success : function(req){
55453                //console.log(data);
55454                 var rdata = false;
55455                 var edata;
55456                 try  {
55457                    rdata = Roo.decode(req.responseText)
55458                 } catch (e) {
55459                     Roo.log("Invalid data from server..");
55460                     Roo.log(edata);
55461                     return;
55462                 }
55463                 if (!rdata || !rdata.success) {
55464                     Roo.log(rdata);
55465                     Roo.MessageBox.alert(Roo.encode(rdata));
55466                     return;
55467                 }
55468                 var data = rdata.data;
55469                 
55470                 if (this.uploadComplete) {
55471                    Roo.MessageBox.hide();
55472                    return;
55473                 }
55474                    
55475                 if (data){
55476                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
55477                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
55478                     );
55479                 }
55480                 this.uploadProgress.defer(2000,this);
55481             },
55482        
55483             failure: function(data) {
55484                 Roo.log('progress url failed ');
55485                 Roo.log(data);
55486             },
55487             scope : this
55488         });
55489            
55490     },
55491     
55492     
55493     run : function()
55494     {
55495         // run get Values on the form, so it syncs any secondary forms.
55496         this.form.getValues();
55497         
55498         var o = this.options;
55499         var method = this.getMethod();
55500         var isPost = method == 'POST';
55501         if(o.clientValidation === false || this.form.isValid()){
55502             
55503             if (this.form.progressUrl) {
55504                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
55505                     (new Date() * 1) + '' + Math.random());
55506                     
55507             } 
55508             
55509             
55510             Roo.Ajax.request(Roo.apply(this.createCallback(), {
55511                 form:this.form.el.dom,
55512                 url:this.getUrl(!isPost),
55513                 method: method,
55514                 params:isPost ? this.getParams() : null,
55515                 isUpload: this.form.fileUpload,
55516                 formData : this.form.formData
55517             }));
55518             
55519             this.uploadProgress();
55520
55521         }else if (o.clientValidation !== false){ // client validation failed
55522             this.failureType = Roo.form.Action.CLIENT_INVALID;
55523             this.form.afterAction(this, false);
55524         }
55525     },
55526
55527     success : function(response)
55528     {
55529         this.uploadComplete= true;
55530         if (this.haveProgress) {
55531             Roo.MessageBox.hide();
55532         }
55533         
55534         
55535         var result = this.processResponse(response);
55536         if(result === true || result.success){
55537             this.form.afterAction(this, true);
55538             return;
55539         }
55540         if(result.errors){
55541             this.form.markInvalid(result.errors);
55542             this.failureType = Roo.form.Action.SERVER_INVALID;
55543         }
55544         this.form.afterAction(this, false);
55545     },
55546     failure : function(response)
55547     {
55548         this.uploadComplete= true;
55549         if (this.haveProgress) {
55550             Roo.MessageBox.hide();
55551         }
55552         
55553         this.response = response;
55554         this.failureType = Roo.form.Action.CONNECT_FAILURE;
55555         this.form.afterAction(this, false);
55556     },
55557     
55558     handleResponse : function(response){
55559         if(this.form.errorReader){
55560             var rs = this.form.errorReader.read(response);
55561             var errors = [];
55562             if(rs.records){
55563                 for(var i = 0, len = rs.records.length; i < len; i++) {
55564                     var r = rs.records[i];
55565                     errors[i] = r.data;
55566                 }
55567             }
55568             if(errors.length < 1){
55569                 errors = null;
55570             }
55571             return {
55572                 success : rs.success,
55573                 errors : errors
55574             };
55575         }
55576         var ret = false;
55577         try {
55578             var rt = response.responseText;
55579             if (rt.match(/^\<!--\[CDATA\[/)) {
55580                 rt = rt.replace(/^\<!--\[CDATA\[/,'');
55581                 rt = rt.replace(/\]\]--\>$/,'');
55582             }
55583             
55584             ret = Roo.decode(rt);
55585         } catch (e) {
55586             ret = {
55587                 success: false,
55588                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
55589                 errors : []
55590             };
55591         }
55592         return ret;
55593         
55594     }
55595 });
55596
55597
55598 Roo.form.Action.Load = function(form, options){
55599     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
55600     this.reader = this.form.reader;
55601 };
55602
55603 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
55604     type : 'load',
55605
55606     run : function(){
55607         
55608         Roo.Ajax.request(Roo.apply(
55609                 this.createCallback(), {
55610                     method:this.getMethod(),
55611                     url:this.getUrl(false),
55612                     params:this.getParams()
55613         }));
55614     },
55615
55616     success : function(response){
55617         
55618         var result = this.processResponse(response);
55619         if(result === true || !result.success || !result.data){
55620             this.failureType = Roo.form.Action.LOAD_FAILURE;
55621             this.form.afterAction(this, false);
55622             return;
55623         }
55624         this.form.clearInvalid();
55625         this.form.setValues(result.data);
55626         this.form.afterAction(this, true);
55627     },
55628
55629     handleResponse : function(response){
55630         if(this.form.reader){
55631             var rs = this.form.reader.read(response);
55632             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
55633             return {
55634                 success : rs.success,
55635                 data : data
55636             };
55637         }
55638         return Roo.decode(response.responseText);
55639     }
55640 });
55641
55642 Roo.form.Action.ACTION_TYPES = {
55643     'load' : Roo.form.Action.Load,
55644     'submit' : Roo.form.Action.Submit
55645 };/*
55646  * Based on:
55647  * Ext JS Library 1.1.1
55648  * Copyright(c) 2006-2007, Ext JS, LLC.
55649  *
55650  * Originally Released Under LGPL - original licence link has changed is not relivant.
55651  *
55652  * Fork - LGPL
55653  * <script type="text/javascript">
55654  */
55655  
55656 /**
55657  * @class Roo.form.Layout
55658  * @extends Roo.Component
55659  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55660  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
55661  * @constructor
55662  * @param {Object} config Configuration options
55663  */
55664 Roo.form.Layout = function(config){
55665     var xitems = [];
55666     if (config.items) {
55667         xitems = config.items;
55668         delete config.items;
55669     }
55670     Roo.form.Layout.superclass.constructor.call(this, config);
55671     this.stack = [];
55672     Roo.each(xitems, this.addxtype, this);
55673      
55674 };
55675
55676 Roo.extend(Roo.form.Layout, Roo.Component, {
55677     /**
55678      * @cfg {String/Object} autoCreate
55679      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
55680      */
55681     /**
55682      * @cfg {String/Object/Function} style
55683      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
55684      * a function which returns such a specification.
55685      */
55686     /**
55687      * @cfg {String} labelAlign (left|top|right)
55688      * Valid values are "left," "top" and "right" (defaults to "left")
55689      */
55690     /**
55691      * @cfg {Number} labelWidth
55692      * Fixed width in pixels of all field labels (defaults to undefined)
55693      */
55694     /**
55695      * @cfg {Boolean} clear
55696      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
55697      */
55698     clear : true,
55699     /**
55700      * @cfg {String} labelSeparator
55701      * The separator to use after field labels (defaults to ':')
55702      */
55703     labelSeparator : ':',
55704     /**
55705      * @cfg {Boolean} hideLabels
55706      * True to suppress the display of field labels in this layout (defaults to false)
55707      */
55708     hideLabels : false,
55709
55710     // private
55711     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
55712     
55713     isLayout : true,
55714     
55715     // private
55716     onRender : function(ct, position){
55717         if(this.el){ // from markup
55718             this.el = Roo.get(this.el);
55719         }else {  // generate
55720             var cfg = this.getAutoCreate();
55721             this.el = ct.createChild(cfg, position);
55722         }
55723         if(this.style){
55724             this.el.applyStyles(this.style);
55725         }
55726         if(this.labelAlign){
55727             this.el.addClass('x-form-label-'+this.labelAlign);
55728         }
55729         if(this.hideLabels){
55730             this.labelStyle = "display:none";
55731             this.elementStyle = "padding-left:0;";
55732         }else{
55733             if(typeof this.labelWidth == 'number'){
55734                 this.labelStyle = "width:"+this.labelWidth+"px;";
55735                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
55736             }
55737             if(this.labelAlign == 'top'){
55738                 this.labelStyle = "width:auto;";
55739                 this.elementStyle = "padding-left:0;";
55740             }
55741         }
55742         var stack = this.stack;
55743         var slen = stack.length;
55744         if(slen > 0){
55745             if(!this.fieldTpl){
55746                 var t = new Roo.Template(
55747                     '<div class="x-form-item {5}">',
55748                         '<label for="{0}" style="{2}">{1}{4}</label>',
55749                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55750                         '</div>',
55751                     '</div><div class="x-form-clear-left"></div>'
55752                 );
55753                 t.disableFormats = true;
55754                 t.compile();
55755                 Roo.form.Layout.prototype.fieldTpl = t;
55756             }
55757             for(var i = 0; i < slen; i++) {
55758                 if(stack[i].isFormField){
55759                     this.renderField(stack[i]);
55760                 }else{
55761                     this.renderComponent(stack[i]);
55762                 }
55763             }
55764         }
55765         if(this.clear){
55766             this.el.createChild({cls:'x-form-clear'});
55767         }
55768     },
55769
55770     // private
55771     renderField : function(f){
55772         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
55773                f.id, //0
55774                f.fieldLabel, //1
55775                f.labelStyle||this.labelStyle||'', //2
55776                this.elementStyle||'', //3
55777                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
55778                f.itemCls||this.itemCls||''  //5
55779        ], true).getPrevSibling());
55780     },
55781
55782     // private
55783     renderComponent : function(c){
55784         c.render(c.isLayout ? this.el : this.el.createChild());    
55785     },
55786     /**
55787      * Adds a object form elements (using the xtype property as the factory method.)
55788      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
55789      * @param {Object} config 
55790      */
55791     addxtype : function(o)
55792     {
55793         // create the lement.
55794         o.form = this.form;
55795         var fe = Roo.factory(o, Roo.form);
55796         this.form.allItems.push(fe);
55797         this.stack.push(fe);
55798         
55799         if (fe.isFormField) {
55800             this.form.items.add(fe);
55801         }
55802          
55803         return fe;
55804     }
55805 });
55806
55807
55808 /**
55809  * @class Roo.form.Column
55810  * @extends Roo.form.Layout
55811  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55812  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
55813  * @constructor
55814  * @param {Object} config Configuration options
55815  */
55816 Roo.form.Column = function(config){
55817     Roo.form.Column.superclass.constructor.call(this, config);
55818 };
55819
55820 Roo.extend(Roo.form.Column, Roo.form.Layout, {
55821     /**
55822      * @cfg {Number/String} width
55823      * The fixed width of the column in pixels or CSS value (defaults to "auto")
55824      */
55825     /**
55826      * @cfg {String/Object} autoCreate
55827      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
55828      */
55829
55830     // private
55831     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
55832
55833     // private
55834     onRender : function(ct, position){
55835         Roo.form.Column.superclass.onRender.call(this, ct, position);
55836         if(this.width){
55837             this.el.setWidth(this.width);
55838         }
55839     }
55840 });
55841
55842 /**
55843  * @class Roo.form.Row
55844  * @extends Roo.form.Layout
55845  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
55846  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
55847  * @constructor
55848  * @param {Object} config Configuration options
55849  */
55850
55851  
55852 Roo.form.Row = function(config){
55853     Roo.form.Row.superclass.constructor.call(this, config);
55854 };
55855  
55856 Roo.extend(Roo.form.Row, Roo.form.Layout, {
55857       /**
55858      * @cfg {Number/String} width
55859      * The fixed width of the column in pixels or CSS value (defaults to "auto")
55860      */
55861     /**
55862      * @cfg {Number/String} height
55863      * The fixed height of the column in pixels or CSS value (defaults to "auto")
55864      */
55865     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
55866     
55867     padWidth : 20,
55868     // private
55869     onRender : function(ct, position){
55870         //console.log('row render');
55871         if(!this.rowTpl){
55872             var t = new Roo.Template(
55873                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
55874                     '<label for="{0}" style="{2}">{1}{4}</label>',
55875                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
55876                     '</div>',
55877                 '</div>'
55878             );
55879             t.disableFormats = true;
55880             t.compile();
55881             Roo.form.Layout.prototype.rowTpl = t;
55882         }
55883         this.fieldTpl = this.rowTpl;
55884         
55885         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
55886         var labelWidth = 100;
55887         
55888         if ((this.labelAlign != 'top')) {
55889             if (typeof this.labelWidth == 'number') {
55890                 labelWidth = this.labelWidth
55891             }
55892             this.padWidth =  20 + labelWidth;
55893             
55894         }
55895         
55896         Roo.form.Column.superclass.onRender.call(this, ct, position);
55897         if(this.width){
55898             this.el.setWidth(this.width);
55899         }
55900         if(this.height){
55901             this.el.setHeight(this.height);
55902         }
55903     },
55904     
55905     // private
55906     renderField : function(f){
55907         f.fieldEl = this.fieldTpl.append(this.el, [
55908                f.id, f.fieldLabel,
55909                f.labelStyle||this.labelStyle||'',
55910                this.elementStyle||'',
55911                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
55912                f.itemCls||this.itemCls||'',
55913                f.width ? f.width + this.padWidth : 160 + this.padWidth
55914        ],true);
55915     }
55916 });
55917  
55918
55919 /**
55920  * @class Roo.form.FieldSet
55921  * @extends Roo.form.Layout
55922  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
55923  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
55924  * @constructor
55925  * @param {Object} config Configuration options
55926  */
55927 Roo.form.FieldSet = function(config){
55928     Roo.form.FieldSet.superclass.constructor.call(this, config);
55929 };
55930
55931 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
55932     /**
55933      * @cfg {String} legend
55934      * The text to display as the legend for the FieldSet (defaults to '')
55935      */
55936     /**
55937      * @cfg {String/Object} autoCreate
55938      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
55939      */
55940
55941     // private
55942     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
55943
55944     // private
55945     onRender : function(ct, position){
55946         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
55947         if(this.legend){
55948             this.setLegend(this.legend);
55949         }
55950     },
55951
55952     // private
55953     setLegend : function(text){
55954         if(this.rendered){
55955             this.el.child('legend').update(text);
55956         }
55957     }
55958 });/*
55959  * Based on:
55960  * Ext JS Library 1.1.1
55961  * Copyright(c) 2006-2007, Ext JS, LLC.
55962  *
55963  * Originally Released Under LGPL - original licence link has changed is not relivant.
55964  *
55965  * Fork - LGPL
55966  * <script type="text/javascript">
55967  */
55968 /**
55969  * @class Roo.form.VTypes
55970  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
55971  * @static
55972  */
55973 Roo.form.VTypes = function(){
55974     // closure these in so they are only created once.
55975     var alpha = /^[a-zA-Z_]+$/;
55976     var alphanum = /^[a-zA-Z0-9_]+$/;
55977     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
55978     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
55979
55980     // All these messages and functions are configurable
55981     return {
55982         /**
55983          * The function used to validate email addresses
55984          * @param {String} value The email address
55985          */
55986         email : function(v){
55987             return email.test(v);
55988         },
55989         /**
55990          * The error text to display when the email validation function returns false
55991          * @type String
55992          */
55993         emailText : 'This field should be an e-mail address in the format "user@domain.com"',
55994         /**
55995          * The keystroke filter mask to be applied on email input
55996          * @type RegExp
55997          */
55998         emailMask : /[a-z0-9_\.\-@]/i,
55999
56000         /**
56001          * The function used to validate URLs
56002          * @param {String} value The URL
56003          */
56004         url : function(v){
56005             return url.test(v);
56006         },
56007         /**
56008          * The error text to display when the url validation function returns false
56009          * @type String
56010          */
56011         urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
56012         
56013         /**
56014          * The function used to validate alpha values
56015          * @param {String} value The value
56016          */
56017         alpha : function(v){
56018             return alpha.test(v);
56019         },
56020         /**
56021          * The error text to display when the alpha validation function returns false
56022          * @type String
56023          */
56024         alphaText : 'This field should only contain letters and _',
56025         /**
56026          * The keystroke filter mask to be applied on alpha input
56027          * @type RegExp
56028          */
56029         alphaMask : /[a-z_]/i,
56030
56031         /**
56032          * The function used to validate alphanumeric values
56033          * @param {String} value The value
56034          */
56035         alphanum : function(v){
56036             return alphanum.test(v);
56037         },
56038         /**
56039          * The error text to display when the alphanumeric validation function returns false
56040          * @type String
56041          */
56042         alphanumText : 'This field should only contain letters, numbers and _',
56043         /**
56044          * The keystroke filter mask to be applied on alphanumeric input
56045          * @type RegExp
56046          */
56047         alphanumMask : /[a-z0-9_]/i
56048     };
56049 }();//<script type="text/javascript">
56050
56051 /**
56052  * @class Roo.form.FCKeditor
56053  * @extends Roo.form.TextArea
56054  * Wrapper around the FCKEditor http://www.fckeditor.net
56055  * @constructor
56056  * Creates a new FCKeditor
56057  * @param {Object} config Configuration options
56058  */
56059 Roo.form.FCKeditor = function(config){
56060     Roo.form.FCKeditor.superclass.constructor.call(this, config);
56061     this.addEvents({
56062          /**
56063          * @event editorinit
56064          * Fired when the editor is initialized - you can add extra handlers here..
56065          * @param {FCKeditor} this
56066          * @param {Object} the FCK object.
56067          */
56068         editorinit : true
56069     });
56070     
56071     
56072 };
56073 Roo.form.FCKeditor.editors = { };
56074 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
56075 {
56076     //defaultAutoCreate : {
56077     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
56078     //},
56079     // private
56080     /**
56081      * @cfg {Object} fck options - see fck manual for details.
56082      */
56083     fckconfig : false,
56084     
56085     /**
56086      * @cfg {Object} fck toolbar set (Basic or Default)
56087      */
56088     toolbarSet : 'Basic',
56089     /**
56090      * @cfg {Object} fck BasePath
56091      */ 
56092     basePath : '/fckeditor/',
56093     
56094     
56095     frame : false,
56096     
56097     value : '',
56098     
56099    
56100     onRender : function(ct, position)
56101     {
56102         if(!this.el){
56103             this.defaultAutoCreate = {
56104                 tag: "textarea",
56105                 style:"width:300px;height:60px;",
56106                 autocomplete: "new-password"
56107             };
56108         }
56109         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
56110         /*
56111         if(this.grow){
56112             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
56113             if(this.preventScrollbars){
56114                 this.el.setStyle("overflow", "hidden");
56115             }
56116             this.el.setHeight(this.growMin);
56117         }
56118         */
56119         //console.log('onrender' + this.getId() );
56120         Roo.form.FCKeditor.editors[this.getId()] = this;
56121          
56122
56123         this.replaceTextarea() ;
56124         
56125     },
56126     
56127     getEditor : function() {
56128         return this.fckEditor;
56129     },
56130     /**
56131      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
56132      * @param {Mixed} value The value to set
56133      */
56134     
56135     
56136     setValue : function(value)
56137     {
56138         //console.log('setValue: ' + value);
56139         
56140         if(typeof(value) == 'undefined') { // not sure why this is happending...
56141             return;
56142         }
56143         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
56144         
56145         //if(!this.el || !this.getEditor()) {
56146         //    this.value = value;
56147             //this.setValue.defer(100,this,[value]);    
56148         //    return;
56149         //} 
56150         
56151         if(!this.getEditor()) {
56152             return;
56153         }
56154         
56155         this.getEditor().SetData(value);
56156         
56157         //
56158
56159     },
56160
56161     /**
56162      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
56163      * @return {Mixed} value The field value
56164      */
56165     getValue : function()
56166     {
56167         
56168         if (this.frame && this.frame.dom.style.display == 'none') {
56169             return Roo.form.FCKeditor.superclass.getValue.call(this);
56170         }
56171         
56172         if(!this.el || !this.getEditor()) {
56173            
56174            // this.getValue.defer(100,this); 
56175             return this.value;
56176         }
56177        
56178         
56179         var value=this.getEditor().GetData();
56180         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
56181         return Roo.form.FCKeditor.superclass.getValue.call(this);
56182         
56183
56184     },
56185
56186     /**
56187      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
56188      * @return {Mixed} value The field value
56189      */
56190     getRawValue : function()
56191     {
56192         if (this.frame && this.frame.dom.style.display == 'none') {
56193             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
56194         }
56195         
56196         if(!this.el || !this.getEditor()) {
56197             //this.getRawValue.defer(100,this); 
56198             return this.value;
56199             return;
56200         }
56201         
56202         
56203         
56204         var value=this.getEditor().GetData();
56205         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
56206         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
56207          
56208     },
56209     
56210     setSize : function(w,h) {
56211         
56212         
56213         
56214         //if (this.frame && this.frame.dom.style.display == 'none') {
56215         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
56216         //    return;
56217         //}
56218         //if(!this.el || !this.getEditor()) {
56219         //    this.setSize.defer(100,this, [w,h]); 
56220         //    return;
56221         //}
56222         
56223         
56224         
56225         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
56226         
56227         this.frame.dom.setAttribute('width', w);
56228         this.frame.dom.setAttribute('height', h);
56229         this.frame.setSize(w,h);
56230         
56231     },
56232     
56233     toggleSourceEdit : function(value) {
56234         
56235       
56236          
56237         this.el.dom.style.display = value ? '' : 'none';
56238         this.frame.dom.style.display = value ?  'none' : '';
56239         
56240     },
56241     
56242     
56243     focus: function(tag)
56244     {
56245         if (this.frame.dom.style.display == 'none') {
56246             return Roo.form.FCKeditor.superclass.focus.call(this);
56247         }
56248         if(!this.el || !this.getEditor()) {
56249             this.focus.defer(100,this, [tag]); 
56250             return;
56251         }
56252         
56253         
56254         
56255         
56256         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
56257         this.getEditor().Focus();
56258         if (tgs.length) {
56259             if (!this.getEditor().Selection.GetSelection()) {
56260                 this.focus.defer(100,this, [tag]); 
56261                 return;
56262             }
56263             
56264             
56265             var r = this.getEditor().EditorDocument.createRange();
56266             r.setStart(tgs[0],0);
56267             r.setEnd(tgs[0],0);
56268             this.getEditor().Selection.GetSelection().removeAllRanges();
56269             this.getEditor().Selection.GetSelection().addRange(r);
56270             this.getEditor().Focus();
56271         }
56272         
56273     },
56274     
56275     
56276     
56277     replaceTextarea : function()
56278     {
56279         if ( document.getElementById( this.getId() + '___Frame' ) ) {
56280             return ;
56281         }
56282         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
56283         //{
56284             // We must check the elements firstly using the Id and then the name.
56285         var oTextarea = document.getElementById( this.getId() );
56286         
56287         var colElementsByName = document.getElementsByName( this.getId() ) ;
56288          
56289         oTextarea.style.display = 'none' ;
56290
56291         if ( oTextarea.tabIndex ) {            
56292             this.TabIndex = oTextarea.tabIndex ;
56293         }
56294         
56295         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
56296         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
56297         this.frame = Roo.get(this.getId() + '___Frame')
56298     },
56299     
56300     _getConfigHtml : function()
56301     {
56302         var sConfig = '' ;
56303
56304         for ( var o in this.fckconfig ) {
56305             sConfig += sConfig.length > 0  ? '&amp;' : '';
56306             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
56307         }
56308
56309         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
56310     },
56311     
56312     
56313     _getIFrameHtml : function()
56314     {
56315         var sFile = 'fckeditor.html' ;
56316         /* no idea what this is about..
56317         try
56318         {
56319             if ( (/fcksource=true/i).test( window.top.location.search ) )
56320                 sFile = 'fckeditor.original.html' ;
56321         }
56322         catch (e) { 
56323         */
56324
56325         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
56326         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
56327         
56328         
56329         var html = '<iframe id="' + this.getId() +
56330             '___Frame" src="' + sLink +
56331             '" width="' + this.width +
56332             '" height="' + this.height + '"' +
56333             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
56334             ' frameborder="0" scrolling="no"></iframe>' ;
56335
56336         return html ;
56337     },
56338     
56339     _insertHtmlBefore : function( html, element )
56340     {
56341         if ( element.insertAdjacentHTML )       {
56342             // IE
56343             element.insertAdjacentHTML( 'beforeBegin', html ) ;
56344         } else { // Gecko
56345             var oRange = document.createRange() ;
56346             oRange.setStartBefore( element ) ;
56347             var oFragment = oRange.createContextualFragment( html );
56348             element.parentNode.insertBefore( oFragment, element ) ;
56349         }
56350     }
56351     
56352     
56353   
56354     
56355     
56356     
56357     
56358
56359 });
56360
56361 //Roo.reg('fckeditor', Roo.form.FCKeditor);
56362
56363 function FCKeditor_OnComplete(editorInstance){
56364     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
56365     f.fckEditor = editorInstance;
56366     //console.log("loaded");
56367     f.fireEvent('editorinit', f, editorInstance);
56368
56369   
56370
56371  
56372
56373
56374
56375
56376
56377
56378
56379
56380
56381
56382
56383
56384
56385
56386
56387 //<script type="text/javascript">
56388 /**
56389  * @class Roo.form.GridField
56390  * @extends Roo.form.Field
56391  * Embed a grid (or editable grid into a form)
56392  * STATUS ALPHA
56393  * 
56394  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
56395  * it needs 
56396  * xgrid.store = Roo.data.Store
56397  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
56398  * xgrid.store.reader = Roo.data.JsonReader 
56399  * 
56400  * 
56401  * @constructor
56402  * Creates a new GridField
56403  * @param {Object} config Configuration options
56404  */
56405 Roo.form.GridField = function(config){
56406     Roo.form.GridField.superclass.constructor.call(this, config);
56407      
56408 };
56409
56410 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
56411     /**
56412      * @cfg {Number} width  - used to restrict width of grid..
56413      */
56414     width : 100,
56415     /**
56416      * @cfg {Number} height - used to restrict height of grid..
56417      */
56418     height : 50,
56419      /**
56420      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
56421          * 
56422          *}
56423      */
56424     xgrid : false, 
56425     /**
56426      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56427      * {tag: "input", type: "checkbox", autocomplete: "off"})
56428      */
56429    // defaultAutoCreate : { tag: 'div' },
56430     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
56431     /**
56432      * @cfg {String} addTitle Text to include for adding a title.
56433      */
56434     addTitle : false,
56435     //
56436     onResize : function(){
56437         Roo.form.Field.superclass.onResize.apply(this, arguments);
56438     },
56439
56440     initEvents : function(){
56441         // Roo.form.Checkbox.superclass.initEvents.call(this);
56442         // has no events...
56443        
56444     },
56445
56446
56447     getResizeEl : function(){
56448         return this.wrap;
56449     },
56450
56451     getPositionEl : function(){
56452         return this.wrap;
56453     },
56454
56455     // private
56456     onRender : function(ct, position){
56457         
56458         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
56459         var style = this.style;
56460         delete this.style;
56461         
56462         Roo.form.GridField.superclass.onRender.call(this, ct, position);
56463         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
56464         this.viewEl = this.wrap.createChild({ tag: 'div' });
56465         if (style) {
56466             this.viewEl.applyStyles(style);
56467         }
56468         if (this.width) {
56469             this.viewEl.setWidth(this.width);
56470         }
56471         if (this.height) {
56472             this.viewEl.setHeight(this.height);
56473         }
56474         //if(this.inputValue !== undefined){
56475         //this.setValue(this.value);
56476         
56477         
56478         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
56479         
56480         
56481         this.grid.render();
56482         this.grid.getDataSource().on('remove', this.refreshValue, this);
56483         this.grid.getDataSource().on('update', this.refreshValue, this);
56484         this.grid.on('afteredit', this.refreshValue, this);
56485  
56486     },
56487      
56488     
56489     /**
56490      * Sets the value of the item. 
56491      * @param {String} either an object  or a string..
56492      */
56493     setValue : function(v){
56494         //this.value = v;
56495         v = v || []; // empty set..
56496         // this does not seem smart - it really only affects memoryproxy grids..
56497         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
56498             var ds = this.grid.getDataSource();
56499             // assumes a json reader..
56500             var data = {}
56501             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
56502             ds.loadData( data);
56503         }
56504         // clear selection so it does not get stale.
56505         if (this.grid.sm) { 
56506             this.grid.sm.clearSelections();
56507         }
56508         
56509         Roo.form.GridField.superclass.setValue.call(this, v);
56510         this.refreshValue();
56511         // should load data in the grid really....
56512     },
56513     
56514     // private
56515     refreshValue: function() {
56516          var val = [];
56517         this.grid.getDataSource().each(function(r) {
56518             val.push(r.data);
56519         });
56520         this.el.dom.value = Roo.encode(val);
56521     }
56522     
56523      
56524     
56525     
56526 });/*
56527  * Based on:
56528  * Ext JS Library 1.1.1
56529  * Copyright(c) 2006-2007, Ext JS, LLC.
56530  *
56531  * Originally Released Under LGPL - original licence link has changed is not relivant.
56532  *
56533  * Fork - LGPL
56534  * <script type="text/javascript">
56535  */
56536 /**
56537  * @class Roo.form.DisplayField
56538  * @extends Roo.form.Field
56539  * A generic Field to display non-editable data.
56540  * @cfg {Boolean} closable (true|false) default false
56541  * @constructor
56542  * Creates a new Display Field item.
56543  * @param {Object} config Configuration options
56544  */
56545 Roo.form.DisplayField = function(config){
56546     Roo.form.DisplayField.superclass.constructor.call(this, config);
56547     
56548     this.addEvents({
56549         /**
56550          * @event close
56551          * Fires after the click the close btn
56552              * @param {Roo.form.DisplayField} this
56553              */
56554         close : true
56555     });
56556 };
56557
56558 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
56559     inputType:      'hidden',
56560     allowBlank:     true,
56561     readOnly:         true,
56562     
56563  
56564     /**
56565      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56566      */
56567     focusClass : undefined,
56568     /**
56569      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56570      */
56571     fieldClass: 'x-form-field',
56572     
56573      /**
56574      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
56575      */
56576     valueRenderer: undefined,
56577     
56578     width: 100,
56579     /**
56580      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56581      * {tag: "input", type: "checkbox", autocomplete: "off"})
56582      */
56583      
56584  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
56585  
56586     closable : false,
56587     
56588     onResize : function(){
56589         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
56590         
56591     },
56592
56593     initEvents : function(){
56594         // Roo.form.Checkbox.superclass.initEvents.call(this);
56595         // has no events...
56596         
56597         if(this.closable){
56598             this.closeEl.on('click', this.onClose, this);
56599         }
56600        
56601     },
56602
56603
56604     getResizeEl : function(){
56605         return this.wrap;
56606     },
56607
56608     getPositionEl : function(){
56609         return this.wrap;
56610     },
56611
56612     // private
56613     onRender : function(ct, position){
56614         
56615         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
56616         //if(this.inputValue !== undefined){
56617         this.wrap = this.el.wrap();
56618         
56619         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
56620         
56621         if(this.closable){
56622             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
56623         }
56624         
56625         if (this.bodyStyle) {
56626             this.viewEl.applyStyles(this.bodyStyle);
56627         }
56628         //this.viewEl.setStyle('padding', '2px');
56629         
56630         this.setValue(this.value);
56631         
56632     },
56633 /*
56634     // private
56635     initValue : Roo.emptyFn,
56636
56637   */
56638
56639         // private
56640     onClick : function(){
56641         
56642     },
56643
56644     /**
56645      * Sets the checked state of the checkbox.
56646      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
56647      */
56648     setValue : function(v){
56649         this.value = v;
56650         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
56651         // this might be called before we have a dom element..
56652         if (!this.viewEl) {
56653             return;
56654         }
56655         this.viewEl.dom.innerHTML = html;
56656         Roo.form.DisplayField.superclass.setValue.call(this, v);
56657
56658     },
56659     
56660     onClose : function(e)
56661     {
56662         e.preventDefault();
56663         
56664         this.fireEvent('close', this);
56665     }
56666 });/*
56667  * 
56668  * Licence- LGPL
56669  * 
56670  */
56671
56672 /**
56673  * @class Roo.form.DayPicker
56674  * @extends Roo.form.Field
56675  * A Day picker show [M] [T] [W] ....
56676  * @constructor
56677  * Creates a new Day Picker
56678  * @param {Object} config Configuration options
56679  */
56680 Roo.form.DayPicker= function(config){
56681     Roo.form.DayPicker.superclass.constructor.call(this, config);
56682      
56683 };
56684
56685 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
56686     /**
56687      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
56688      */
56689     focusClass : undefined,
56690     /**
56691      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
56692      */
56693     fieldClass: "x-form-field",
56694    
56695     /**
56696      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
56697      * {tag: "input", type: "checkbox", autocomplete: "off"})
56698      */
56699     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
56700     
56701    
56702     actionMode : 'viewEl', 
56703     //
56704     // private
56705  
56706     inputType : 'hidden',
56707     
56708      
56709     inputElement: false, // real input element?
56710     basedOn: false, // ????
56711     
56712     isFormField: true, // not sure where this is needed!!!!
56713
56714     onResize : function(){
56715         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
56716         if(!this.boxLabel){
56717             this.el.alignTo(this.wrap, 'c-c');
56718         }
56719     },
56720
56721     initEvents : function(){
56722         Roo.form.Checkbox.superclass.initEvents.call(this);
56723         this.el.on("click", this.onClick,  this);
56724         this.el.on("change", this.onClick,  this);
56725     },
56726
56727
56728     getResizeEl : function(){
56729         return this.wrap;
56730     },
56731
56732     getPositionEl : function(){
56733         return this.wrap;
56734     },
56735
56736     
56737     // private
56738     onRender : function(ct, position){
56739         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
56740        
56741         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
56742         
56743         var r1 = '<table><tr>';
56744         var r2 = '<tr class="x-form-daypick-icons">';
56745         for (var i=0; i < 7; i++) {
56746             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
56747             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
56748         }
56749         
56750         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
56751         viewEl.select('img').on('click', this.onClick, this);
56752         this.viewEl = viewEl;   
56753         
56754         
56755         // this will not work on Chrome!!!
56756         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
56757         this.el.on('propertychange', this.setFromHidden,  this);  //ie
56758         
56759         
56760           
56761
56762     },
56763
56764     // private
56765     initValue : Roo.emptyFn,
56766
56767     /**
56768      * Returns the checked state of the checkbox.
56769      * @return {Boolean} True if checked, else false
56770      */
56771     getValue : function(){
56772         return this.el.dom.value;
56773         
56774     },
56775
56776         // private
56777     onClick : function(e){ 
56778         //this.setChecked(!this.checked);
56779         Roo.get(e.target).toggleClass('x-menu-item-checked');
56780         this.refreshValue();
56781         //if(this.el.dom.checked != this.checked){
56782         //    this.setValue(this.el.dom.checked);
56783        // }
56784     },
56785     
56786     // private
56787     refreshValue : function()
56788     {
56789         var val = '';
56790         this.viewEl.select('img',true).each(function(e,i,n)  {
56791             val += e.is(".x-menu-item-checked") ? String(n) : '';
56792         });
56793         this.setValue(val, true);
56794     },
56795
56796     /**
56797      * Sets the checked state of the checkbox.
56798      * On is always based on a string comparison between inputValue and the param.
56799      * @param {Boolean/String} value - the value to set 
56800      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
56801      */
56802     setValue : function(v,suppressEvent){
56803         if (!this.el.dom) {
56804             return;
56805         }
56806         var old = this.el.dom.value ;
56807         this.el.dom.value = v;
56808         if (suppressEvent) {
56809             return ;
56810         }
56811          
56812         // update display..
56813         this.viewEl.select('img',true).each(function(e,i,n)  {
56814             
56815             var on = e.is(".x-menu-item-checked");
56816             var newv = v.indexOf(String(n)) > -1;
56817             if (on != newv) {
56818                 e.toggleClass('x-menu-item-checked');
56819             }
56820             
56821         });
56822         
56823         
56824         this.fireEvent('change', this, v, old);
56825         
56826         
56827     },
56828    
56829     // handle setting of hidden value by some other method!!?!?
56830     setFromHidden: function()
56831     {
56832         if(!this.el){
56833             return;
56834         }
56835         //console.log("SET FROM HIDDEN");
56836         //alert('setFrom hidden');
56837         this.setValue(this.el.dom.value);
56838     },
56839     
56840     onDestroy : function()
56841     {
56842         if(this.viewEl){
56843             Roo.get(this.viewEl).remove();
56844         }
56845          
56846         Roo.form.DayPicker.superclass.onDestroy.call(this);
56847     }
56848
56849 });/*
56850  * RooJS Library 1.1.1
56851  * Copyright(c) 2008-2011  Alan Knowles
56852  *
56853  * License - LGPL
56854  */
56855  
56856
56857 /**
56858  * @class Roo.form.ComboCheck
56859  * @extends Roo.form.ComboBox
56860  * A combobox for multiple select items.
56861  *
56862  * FIXME - could do with a reset button..
56863  * 
56864  * @constructor
56865  * Create a new ComboCheck
56866  * @param {Object} config Configuration options
56867  */
56868 Roo.form.ComboCheck = function(config){
56869     Roo.form.ComboCheck.superclass.constructor.call(this, config);
56870     // should verify some data...
56871     // like
56872     // hiddenName = required..
56873     // displayField = required
56874     // valudField == required
56875     var req= [ 'hiddenName', 'displayField', 'valueField' ];
56876     var _t = this;
56877     Roo.each(req, function(e) {
56878         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
56879             throw "Roo.form.ComboCheck : missing value for: " + e;
56880         }
56881     });
56882     
56883     
56884 };
56885
56886 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
56887      
56888      
56889     editable : false,
56890      
56891     selectedClass: 'x-menu-item-checked', 
56892     
56893     // private
56894     onRender : function(ct, position){
56895         var _t = this;
56896         
56897         
56898         
56899         if(!this.tpl){
56900             var cls = 'x-combo-list';
56901
56902             
56903             this.tpl =  new Roo.Template({
56904                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
56905                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
56906                    '<span>{' + this.displayField + '}</span>' +
56907                     '</div>' 
56908                 
56909             });
56910         }
56911  
56912         
56913         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
56914         this.view.singleSelect = false;
56915         this.view.multiSelect = true;
56916         this.view.toggleSelect = true;
56917         this.pageTb.add(new Roo.Toolbar.Fill(), {
56918             
56919             text: 'Done',
56920             handler: function()
56921             {
56922                 _t.collapse();
56923             }
56924         });
56925     },
56926     
56927     onViewOver : function(e, t){
56928         // do nothing...
56929         return;
56930         
56931     },
56932     
56933     onViewClick : function(doFocus,index){
56934         return;
56935         
56936     },
56937     select: function () {
56938         //Roo.log("SELECT CALLED");
56939     },
56940      
56941     selectByValue : function(xv, scrollIntoView){
56942         var ar = this.getValueArray();
56943         var sels = [];
56944         
56945         Roo.each(ar, function(v) {
56946             if(v === undefined || v === null){
56947                 return;
56948             }
56949             var r = this.findRecord(this.valueField, v);
56950             if(r){
56951                 sels.push(this.store.indexOf(r))
56952                 
56953             }
56954         },this);
56955         this.view.select(sels);
56956         return false;
56957     },
56958     
56959     
56960     
56961     onSelect : function(record, index){
56962        // Roo.log("onselect Called");
56963        // this is only called by the clear button now..
56964         this.view.clearSelections();
56965         this.setValue('[]');
56966         if (this.value != this.valueBefore) {
56967             this.fireEvent('change', this, this.value, this.valueBefore);
56968             this.valueBefore = this.value;
56969         }
56970     },
56971     getValueArray : function()
56972     {
56973         var ar = [] ;
56974         
56975         try {
56976             //Roo.log(this.value);
56977             if (typeof(this.value) == 'undefined') {
56978                 return [];
56979             }
56980             var ar = Roo.decode(this.value);
56981             return  ar instanceof Array ? ar : []; //?? valid?
56982             
56983         } catch(e) {
56984             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
56985             return [];
56986         }
56987          
56988     },
56989     expand : function ()
56990     {
56991         
56992         Roo.form.ComboCheck.superclass.expand.call(this);
56993         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
56994         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
56995         
56996
56997     },
56998     
56999     collapse : function(){
57000         Roo.form.ComboCheck.superclass.collapse.call(this);
57001         var sl = this.view.getSelectedIndexes();
57002         var st = this.store;
57003         var nv = [];
57004         var tv = [];
57005         var r;
57006         Roo.each(sl, function(i) {
57007             r = st.getAt(i);
57008             nv.push(r.get(this.valueField));
57009         },this);
57010         this.setValue(Roo.encode(nv));
57011         if (this.value != this.valueBefore) {
57012
57013             this.fireEvent('change', this, this.value, this.valueBefore);
57014             this.valueBefore = this.value;
57015         }
57016         
57017     },
57018     
57019     setValue : function(v){
57020         // Roo.log(v);
57021         this.value = v;
57022         
57023         var vals = this.getValueArray();
57024         var tv = [];
57025         Roo.each(vals, function(k) {
57026             var r = this.findRecord(this.valueField, k);
57027             if(r){
57028                 tv.push(r.data[this.displayField]);
57029             }else if(this.valueNotFoundText !== undefined){
57030                 tv.push( this.valueNotFoundText );
57031             }
57032         },this);
57033        // Roo.log(tv);
57034         
57035         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
57036         this.hiddenField.value = v;
57037         this.value = v;
57038     }
57039     
57040 });/*
57041  * Based on:
57042  * Ext JS Library 1.1.1
57043  * Copyright(c) 2006-2007, Ext JS, LLC.
57044  *
57045  * Originally Released Under LGPL - original licence link has changed is not relivant.
57046  *
57047  * Fork - LGPL
57048  * <script type="text/javascript">
57049  */
57050  
57051 /**
57052  * @class Roo.form.Signature
57053  * @extends Roo.form.Field
57054  * Signature field.  
57055  * @constructor
57056  * 
57057  * @param {Object} config Configuration options
57058  */
57059
57060 Roo.form.Signature = function(config){
57061     Roo.form.Signature.superclass.constructor.call(this, config);
57062     
57063     this.addEvents({// not in used??
57064          /**
57065          * @event confirm
57066          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
57067              * @param {Roo.form.Signature} combo This combo box
57068              */
57069         'confirm' : true,
57070         /**
57071          * @event reset
57072          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
57073              * @param {Roo.form.ComboBox} combo This combo box
57074              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
57075              */
57076         'reset' : true
57077     });
57078 };
57079
57080 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
57081     /**
57082      * @cfg {Object} labels Label to use when rendering a form.
57083      * defaults to 
57084      * labels : { 
57085      *      clear : "Clear",
57086      *      confirm : "Confirm"
57087      *  }
57088      */
57089     labels : { 
57090         clear : "Clear",
57091         confirm : "Confirm"
57092     },
57093     /**
57094      * @cfg {Number} width The signature panel width (defaults to 300)
57095      */
57096     width: 300,
57097     /**
57098      * @cfg {Number} height The signature panel height (defaults to 100)
57099      */
57100     height : 100,
57101     /**
57102      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
57103      */
57104     allowBlank : false,
57105     
57106     //private
57107     // {Object} signPanel The signature SVG panel element (defaults to {})
57108     signPanel : {},
57109     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
57110     isMouseDown : false,
57111     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
57112     isConfirmed : false,
57113     // {String} signatureTmp SVG mapping string (defaults to empty string)
57114     signatureTmp : '',
57115     
57116     
57117     defaultAutoCreate : { // modified by initCompnoent..
57118         tag: "input",
57119         type:"hidden"
57120     },
57121
57122     // private
57123     onRender : function(ct, position){
57124         
57125         Roo.form.Signature.superclass.onRender.call(this, ct, position);
57126         
57127         this.wrap = this.el.wrap({
57128             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
57129         });
57130         
57131         this.createToolbar(this);
57132         this.signPanel = this.wrap.createChild({
57133                 tag: 'div',
57134                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
57135             }, this.el
57136         );
57137             
57138         this.svgID = Roo.id();
57139         this.svgEl = this.signPanel.createChild({
57140               xmlns : 'http://www.w3.org/2000/svg',
57141               tag : 'svg',
57142               id : this.svgID + "-svg",
57143               width: this.width,
57144               height: this.height,
57145               viewBox: '0 0 '+this.width+' '+this.height,
57146               cn : [
57147                 {
57148                     tag: "rect",
57149                     id: this.svgID + "-svg-r",
57150                     width: this.width,
57151                     height: this.height,
57152                     fill: "#ffa"
57153                 },
57154                 {
57155                     tag: "line",
57156                     id: this.svgID + "-svg-l",
57157                     x1: "0", // start
57158                     y1: (this.height*0.8), // start set the line in 80% of height
57159                     x2: this.width, // end
57160                     y2: (this.height*0.8), // end set the line in 80% of height
57161                     'stroke': "#666",
57162                     'stroke-width': "1",
57163                     'stroke-dasharray': "3",
57164                     'shape-rendering': "crispEdges",
57165                     'pointer-events': "none"
57166                 },
57167                 {
57168                     tag: "path",
57169                     id: this.svgID + "-svg-p",
57170                     'stroke': "navy",
57171                     'stroke-width': "3",
57172                     'fill': "none",
57173                     'pointer-events': 'none'
57174                 }
57175               ]
57176         });
57177         this.createSVG();
57178         this.svgBox = this.svgEl.dom.getScreenCTM();
57179     },
57180     createSVG : function(){ 
57181         var svg = this.signPanel;
57182         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
57183         var t = this;
57184
57185         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
57186         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
57187         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
57188         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
57189         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
57190         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
57191         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
57192         
57193     },
57194     isTouchEvent : function(e){
57195         return e.type.match(/^touch/);
57196     },
57197     getCoords : function (e) {
57198         var pt    = this.svgEl.dom.createSVGPoint();
57199         pt.x = e.clientX; 
57200         pt.y = e.clientY;
57201         if (this.isTouchEvent(e)) {
57202             pt.x =  e.targetTouches[0].clientX;
57203             pt.y = e.targetTouches[0].clientY;
57204         }
57205         var a = this.svgEl.dom.getScreenCTM();
57206         var b = a.inverse();
57207         var mx = pt.matrixTransform(b);
57208         return mx.x + ',' + mx.y;
57209     },
57210     //mouse event headler 
57211     down : function (e) {
57212         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
57213         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
57214         
57215         this.isMouseDown = true;
57216         
57217         e.preventDefault();
57218     },
57219     move : function (e) {
57220         if (this.isMouseDown) {
57221             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
57222             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
57223         }
57224         
57225         e.preventDefault();
57226     },
57227     up : function (e) {
57228         this.isMouseDown = false;
57229         var sp = this.signatureTmp.split(' ');
57230         
57231         if(sp.length > 1){
57232             if(!sp[sp.length-2].match(/^L/)){
57233                 sp.pop();
57234                 sp.pop();
57235                 sp.push("");
57236                 this.signatureTmp = sp.join(" ");
57237             }
57238         }
57239         if(this.getValue() != this.signatureTmp){
57240             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57241             this.isConfirmed = false;
57242         }
57243         e.preventDefault();
57244     },
57245     
57246     /**
57247      * Protected method that will not generally be called directly. It
57248      * is called when the editor creates its toolbar. Override this method if you need to
57249      * add custom toolbar buttons.
57250      * @param {HtmlEditor} editor
57251      */
57252     createToolbar : function(editor){
57253          function btn(id, toggle, handler){
57254             var xid = fid + '-'+ id ;
57255             return {
57256                 id : xid,
57257                 cmd : id,
57258                 cls : 'x-btn-icon x-edit-'+id,
57259                 enableToggle:toggle !== false,
57260                 scope: editor, // was editor...
57261                 handler:handler||editor.relayBtnCmd,
57262                 clickEvent:'mousedown',
57263                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
57264                 tabIndex:-1
57265             };
57266         }
57267         
57268         
57269         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
57270         this.tb = tb;
57271         this.tb.add(
57272            {
57273                 cls : ' x-signature-btn x-signature-'+id,
57274                 scope: editor, // was editor...
57275                 handler: this.reset,
57276                 clickEvent:'mousedown',
57277                 text: this.labels.clear
57278             },
57279             {
57280                  xtype : 'Fill',
57281                  xns: Roo.Toolbar
57282             }, 
57283             {
57284                 cls : '  x-signature-btn x-signature-'+id,
57285                 scope: editor, // was editor...
57286                 handler: this.confirmHandler,
57287                 clickEvent:'mousedown',
57288                 text: this.labels.confirm
57289             }
57290         );
57291     
57292     },
57293     //public
57294     /**
57295      * when user is clicked confirm then show this image.....
57296      * 
57297      * @return {String} Image Data URI
57298      */
57299     getImageDataURI : function(){
57300         var svg = this.svgEl.dom.parentNode.innerHTML;
57301         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
57302         return src; 
57303     },
57304     /**
57305      * 
57306      * @return {Boolean} this.isConfirmed
57307      */
57308     getConfirmed : function(){
57309         return this.isConfirmed;
57310     },
57311     /**
57312      * 
57313      * @return {Number} this.width
57314      */
57315     getWidth : function(){
57316         return this.width;
57317     },
57318     /**
57319      * 
57320      * @return {Number} this.height
57321      */
57322     getHeight : function(){
57323         return this.height;
57324     },
57325     // private
57326     getSignature : function(){
57327         return this.signatureTmp;
57328     },
57329     // private
57330     reset : function(){
57331         this.signatureTmp = '';
57332         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57333         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
57334         this.isConfirmed = false;
57335         Roo.form.Signature.superclass.reset.call(this);
57336     },
57337     setSignature : function(s){
57338         this.signatureTmp = s;
57339         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
57340         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
57341         this.setValue(s);
57342         this.isConfirmed = false;
57343         Roo.form.Signature.superclass.reset.call(this);
57344     }, 
57345     test : function(){
57346 //        Roo.log(this.signPanel.dom.contentWindow.up())
57347     },
57348     //private
57349     setConfirmed : function(){
57350         
57351         
57352         
57353 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
57354     },
57355     // private
57356     confirmHandler : function(){
57357         if(!this.getSignature()){
57358             return;
57359         }
57360         
57361         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
57362         this.setValue(this.getSignature());
57363         this.isConfirmed = true;
57364         
57365         this.fireEvent('confirm', this);
57366     },
57367     // private
57368     // Subclasses should provide the validation implementation by overriding this
57369     validateValue : function(value){
57370         if(this.allowBlank){
57371             return true;
57372         }
57373         
57374         if(this.isConfirmed){
57375             return true;
57376         }
57377         return false;
57378     }
57379 });/*
57380  * Based on:
57381  * Ext JS Library 1.1.1
57382  * Copyright(c) 2006-2007, Ext JS, LLC.
57383  *
57384  * Originally Released Under LGPL - original licence link has changed is not relivant.
57385  *
57386  * Fork - LGPL
57387  * <script type="text/javascript">
57388  */
57389  
57390
57391 /**
57392  * @class Roo.form.ComboBox
57393  * @extends Roo.form.TriggerField
57394  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
57395  * @constructor
57396  * Create a new ComboBox.
57397  * @param {Object} config Configuration options
57398  */
57399 Roo.form.Select = function(config){
57400     Roo.form.Select.superclass.constructor.call(this, config);
57401      
57402 };
57403
57404 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
57405     /**
57406      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
57407      */
57408     /**
57409      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
57410      * rendering into an Roo.Editor, defaults to false)
57411      */
57412     /**
57413      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
57414      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
57415      */
57416     /**
57417      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
57418      */
57419     /**
57420      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
57421      * the dropdown list (defaults to undefined, with no header element)
57422      */
57423
57424      /**
57425      * @cfg {String/Roo.Template} tpl The template to use to render the output
57426      */
57427      
57428     // private
57429     defaultAutoCreate : {tag: "select"  },
57430     /**
57431      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
57432      */
57433     listWidth: undefined,
57434     /**
57435      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
57436      * mode = 'remote' or 'text' if mode = 'local')
57437      */
57438     displayField: undefined,
57439     /**
57440      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
57441      * mode = 'remote' or 'value' if mode = 'local'). 
57442      * Note: use of a valueField requires the user make a selection
57443      * in order for a value to be mapped.
57444      */
57445     valueField: undefined,
57446     
57447     
57448     /**
57449      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
57450      * field's data value (defaults to the underlying DOM element's name)
57451      */
57452     hiddenName: undefined,
57453     /**
57454      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
57455      */
57456     listClass: '',
57457     /**
57458      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
57459      */
57460     selectedClass: 'x-combo-selected',
57461     /**
57462      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
57463      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
57464      * which displays a downward arrow icon).
57465      */
57466     triggerClass : 'x-form-arrow-trigger',
57467     /**
57468      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
57469      */
57470     shadow:'sides',
57471     /**
57472      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
57473      * anchor positions (defaults to 'tl-bl')
57474      */
57475     listAlign: 'tl-bl?',
57476     /**
57477      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
57478      */
57479     maxHeight: 300,
57480     /**
57481      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
57482      * query specified by the allQuery config option (defaults to 'query')
57483      */
57484     triggerAction: 'query',
57485     /**
57486      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
57487      * (defaults to 4, does not apply if editable = false)
57488      */
57489     minChars : 4,
57490     /**
57491      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
57492      * delay (typeAheadDelay) if it matches a known value (defaults to false)
57493      */
57494     typeAhead: false,
57495     /**
57496      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
57497      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
57498      */
57499     queryDelay: 500,
57500     /**
57501      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
57502      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
57503      */
57504     pageSize: 0,
57505     /**
57506      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
57507      * when editable = true (defaults to false)
57508      */
57509     selectOnFocus:false,
57510     /**
57511      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
57512      */
57513     queryParam: 'query',
57514     /**
57515      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
57516      * when mode = 'remote' (defaults to 'Loading...')
57517      */
57518     loadingText: 'Loading...',
57519     /**
57520      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
57521      */
57522     resizable: false,
57523     /**
57524      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
57525      */
57526     handleHeight : 8,
57527     /**
57528      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
57529      * traditional select (defaults to true)
57530      */
57531     editable: true,
57532     /**
57533      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
57534      */
57535     allQuery: '',
57536     /**
57537      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
57538      */
57539     mode: 'remote',
57540     /**
57541      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
57542      * listWidth has a higher value)
57543      */
57544     minListWidth : 70,
57545     /**
57546      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
57547      * allow the user to set arbitrary text into the field (defaults to false)
57548      */
57549     forceSelection:false,
57550     /**
57551      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
57552      * if typeAhead = true (defaults to 250)
57553      */
57554     typeAheadDelay : 250,
57555     /**
57556      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
57557      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
57558      */
57559     valueNotFoundText : undefined,
57560     
57561     /**
57562      * @cfg {String} defaultValue The value displayed after loading the store.
57563      */
57564     defaultValue: '',
57565     
57566     /**
57567      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
57568      */
57569     blockFocus : false,
57570     
57571     /**
57572      * @cfg {Boolean} disableClear Disable showing of clear button.
57573      */
57574     disableClear : false,
57575     /**
57576      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
57577      */
57578     alwaysQuery : false,
57579     
57580     //private
57581     addicon : false,
57582     editicon: false,
57583     
57584     // element that contains real text value.. (when hidden is used..)
57585      
57586     // private
57587     onRender : function(ct, position){
57588         Roo.form.Field.prototype.onRender.call(this, ct, position);
57589         
57590         if(this.store){
57591             this.store.on('beforeload', this.onBeforeLoad, this);
57592             this.store.on('load', this.onLoad, this);
57593             this.store.on('loadexception', this.onLoadException, this);
57594             this.store.load({});
57595         }
57596         
57597         
57598         
57599     },
57600
57601     // private
57602     initEvents : function(){
57603         //Roo.form.ComboBox.superclass.initEvents.call(this);
57604  
57605     },
57606
57607     onDestroy : function(){
57608        
57609         if(this.store){
57610             this.store.un('beforeload', this.onBeforeLoad, this);
57611             this.store.un('load', this.onLoad, this);
57612             this.store.un('loadexception', this.onLoadException, this);
57613         }
57614         //Roo.form.ComboBox.superclass.onDestroy.call(this);
57615     },
57616
57617     // private
57618     fireKey : function(e){
57619         if(e.isNavKeyPress() && !this.list.isVisible()){
57620             this.fireEvent("specialkey", this, e);
57621         }
57622     },
57623
57624     // private
57625     onResize: function(w, h){
57626         
57627         return; 
57628     
57629         
57630     },
57631
57632     /**
57633      * Allow or prevent the user from directly editing the field text.  If false is passed,
57634      * the user will only be able to select from the items defined in the dropdown list.  This method
57635      * is the runtime equivalent of setting the 'editable' config option at config time.
57636      * @param {Boolean} value True to allow the user to directly edit the field text
57637      */
57638     setEditable : function(value){
57639          
57640     },
57641
57642     // private
57643     onBeforeLoad : function(){
57644         
57645         Roo.log("Select before load");
57646         return;
57647     
57648         this.innerList.update(this.loadingText ?
57649                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
57650         //this.restrictHeight();
57651         this.selectedIndex = -1;
57652     },
57653
57654     // private
57655     onLoad : function(){
57656
57657     
57658         var dom = this.el.dom;
57659         dom.innerHTML = '';
57660          var od = dom.ownerDocument;
57661          
57662         if (this.emptyText) {
57663             var op = od.createElement('option');
57664             op.setAttribute('value', '');
57665             op.innerHTML = String.format('{0}', this.emptyText);
57666             dom.appendChild(op);
57667         }
57668         if(this.store.getCount() > 0){
57669            
57670             var vf = this.valueField;
57671             var df = this.displayField;
57672             this.store.data.each(function(r) {
57673                 // which colmsn to use... testing - cdoe / title..
57674                 var op = od.createElement('option');
57675                 op.setAttribute('value', r.data[vf]);
57676                 op.innerHTML = String.format('{0}', r.data[df]);
57677                 dom.appendChild(op);
57678             });
57679             if (typeof(this.defaultValue != 'undefined')) {
57680                 this.setValue(this.defaultValue);
57681             }
57682             
57683              
57684         }else{
57685             //this.onEmptyResults();
57686         }
57687         //this.el.focus();
57688     },
57689     // private
57690     onLoadException : function()
57691     {
57692         dom.innerHTML = '';
57693             
57694         Roo.log("Select on load exception");
57695         return;
57696     
57697         this.collapse();
57698         Roo.log(this.store.reader.jsonData);
57699         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57700             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57701         }
57702         
57703         
57704     },
57705     // private
57706     onTypeAhead : function(){
57707          
57708     },
57709
57710     // private
57711     onSelect : function(record, index){
57712         Roo.log('on select?');
57713         return;
57714         if(this.fireEvent('beforeselect', this, record, index) !== false){
57715             this.setFromData(index > -1 ? record.data : false);
57716             this.collapse();
57717             this.fireEvent('select', this, record, index);
57718         }
57719     },
57720
57721     /**
57722      * Returns the currently selected field value or empty string if no value is set.
57723      * @return {String} value The selected value
57724      */
57725     getValue : function(){
57726         var dom = this.el.dom;
57727         this.value = dom.options[dom.selectedIndex].value;
57728         return this.value;
57729         
57730     },
57731
57732     /**
57733      * Clears any text/value currently set in the field
57734      */
57735     clearValue : function(){
57736         this.value = '';
57737         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
57738         
57739     },
57740
57741     /**
57742      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
57743      * will be displayed in the field.  If the value does not match the data value of an existing item,
57744      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
57745      * Otherwise the field will be blank (although the value will still be set).
57746      * @param {String} value The value to match
57747      */
57748     setValue : function(v){
57749         var d = this.el.dom;
57750         for (var i =0; i < d.options.length;i++) {
57751             if (v == d.options[i].value) {
57752                 d.selectedIndex = i;
57753                 this.value = v;
57754                 return;
57755             }
57756         }
57757         this.clearValue();
57758     },
57759     /**
57760      * @property {Object} the last set data for the element
57761      */
57762     
57763     lastData : false,
57764     /**
57765      * Sets the value of the field based on a object which is related to the record format for the store.
57766      * @param {Object} value the value to set as. or false on reset?
57767      */
57768     setFromData : function(o){
57769         Roo.log('setfrom data?');
57770          
57771         
57772         
57773     },
57774     // private
57775     reset : function(){
57776         this.clearValue();
57777     },
57778     // private
57779     findRecord : function(prop, value){
57780         
57781         return false;
57782     
57783         var record;
57784         if(this.store.getCount() > 0){
57785             this.store.each(function(r){
57786                 if(r.data[prop] == value){
57787                     record = r;
57788                     return false;
57789                 }
57790                 return true;
57791             });
57792         }
57793         return record;
57794     },
57795     
57796     getName: function()
57797     {
57798         // returns hidden if it's set..
57799         if (!this.rendered) {return ''};
57800         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
57801         
57802     },
57803      
57804
57805     
57806
57807     // private
57808     onEmptyResults : function(){
57809         Roo.log('empty results');
57810         //this.collapse();
57811     },
57812
57813     /**
57814      * Returns true if the dropdown list is expanded, else false.
57815      */
57816     isExpanded : function(){
57817         return false;
57818     },
57819
57820     /**
57821      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
57822      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57823      * @param {String} value The data value of the item to select
57824      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57825      * selected item if it is not currently in view (defaults to true)
57826      * @return {Boolean} True if the value matched an item in the list, else false
57827      */
57828     selectByValue : function(v, scrollIntoView){
57829         Roo.log('select By Value');
57830         return false;
57831     
57832         if(v !== undefined && v !== null){
57833             var r = this.findRecord(this.valueField || this.displayField, v);
57834             if(r){
57835                 this.select(this.store.indexOf(r), scrollIntoView);
57836                 return true;
57837             }
57838         }
57839         return false;
57840     },
57841
57842     /**
57843      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
57844      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
57845      * @param {Number} index The zero-based index of the list item to select
57846      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
57847      * selected item if it is not currently in view (defaults to true)
57848      */
57849     select : function(index, scrollIntoView){
57850         Roo.log('select ');
57851         return  ;
57852         
57853         this.selectedIndex = index;
57854         this.view.select(index);
57855         if(scrollIntoView !== false){
57856             var el = this.view.getNode(index);
57857             if(el){
57858                 this.innerList.scrollChildIntoView(el, false);
57859             }
57860         }
57861     },
57862
57863       
57864
57865     // private
57866     validateBlur : function(){
57867         
57868         return;
57869         
57870     },
57871
57872     // private
57873     initQuery : function(){
57874         this.doQuery(this.getRawValue());
57875     },
57876
57877     // private
57878     doForce : function(){
57879         if(this.el.dom.value.length > 0){
57880             this.el.dom.value =
57881                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
57882              
57883         }
57884     },
57885
57886     /**
57887      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
57888      * query allowing the query action to be canceled if needed.
57889      * @param {String} query The SQL query to execute
57890      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
57891      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
57892      * saved in the current store (defaults to false)
57893      */
57894     doQuery : function(q, forceAll){
57895         
57896         Roo.log('doQuery?');
57897         if(q === undefined || q === null){
57898             q = '';
57899         }
57900         var qe = {
57901             query: q,
57902             forceAll: forceAll,
57903             combo: this,
57904             cancel:false
57905         };
57906         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
57907             return false;
57908         }
57909         q = qe.query;
57910         forceAll = qe.forceAll;
57911         if(forceAll === true || (q.length >= this.minChars)){
57912             if(this.lastQuery != q || this.alwaysQuery){
57913                 this.lastQuery = q;
57914                 if(this.mode == 'local'){
57915                     this.selectedIndex = -1;
57916                     if(forceAll){
57917                         this.store.clearFilter();
57918                     }else{
57919                         this.store.filter(this.displayField, q);
57920                     }
57921                     this.onLoad();
57922                 }else{
57923                     this.store.baseParams[this.queryParam] = q;
57924                     this.store.load({
57925                         params: this.getParams(q)
57926                     });
57927                     this.expand();
57928                 }
57929             }else{
57930                 this.selectedIndex = -1;
57931                 this.onLoad();   
57932             }
57933         }
57934     },
57935
57936     // private
57937     getParams : function(q){
57938         var p = {};
57939         //p[this.queryParam] = q;
57940         if(this.pageSize){
57941             p.start = 0;
57942             p.limit = this.pageSize;
57943         }
57944         return p;
57945     },
57946
57947     /**
57948      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
57949      */
57950     collapse : function(){
57951         
57952     },
57953
57954     // private
57955     collapseIf : function(e){
57956         
57957     },
57958
57959     /**
57960      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
57961      */
57962     expand : function(){
57963         
57964     } ,
57965
57966     // private
57967      
57968
57969     /** 
57970     * @cfg {Boolean} grow 
57971     * @hide 
57972     */
57973     /** 
57974     * @cfg {Number} growMin 
57975     * @hide 
57976     */
57977     /** 
57978     * @cfg {Number} growMax 
57979     * @hide 
57980     */
57981     /**
57982      * @hide
57983      * @method autoSize
57984      */
57985     
57986     setWidth : function()
57987     {
57988         
57989     },
57990     getResizeEl : function(){
57991         return this.el;
57992     }
57993 });//<script type="text/javasscript">
57994  
57995
57996 /**
57997  * @class Roo.DDView
57998  * A DnD enabled version of Roo.View.
57999  * @param {Element/String} container The Element in which to create the View.
58000  * @param {String} tpl The template string used to create the markup for each element of the View
58001  * @param {Object} config The configuration properties. These include all the config options of
58002  * {@link Roo.View} plus some specific to this class.<br>
58003  * <p>
58004  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
58005  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
58006  * <p>
58007  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
58008 .x-view-drag-insert-above {
58009         border-top:1px dotted #3366cc;
58010 }
58011 .x-view-drag-insert-below {
58012         border-bottom:1px dotted #3366cc;
58013 }
58014 </code></pre>
58015  * 
58016  */
58017  
58018 Roo.DDView = function(container, tpl, config) {
58019     Roo.DDView.superclass.constructor.apply(this, arguments);
58020     this.getEl().setStyle("outline", "0px none");
58021     this.getEl().unselectable();
58022     if (this.dragGroup) {
58023         this.setDraggable(this.dragGroup.split(","));
58024     }
58025     if (this.dropGroup) {
58026         this.setDroppable(this.dropGroup.split(","));
58027     }
58028     if (this.deletable) {
58029         this.setDeletable();
58030     }
58031     this.isDirtyFlag = false;
58032         this.addEvents({
58033                 "drop" : true
58034         });
58035 };
58036
58037 Roo.extend(Roo.DDView, Roo.View, {
58038 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
58039 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
58040 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
58041 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
58042
58043         isFormField: true,
58044
58045         reset: Roo.emptyFn,
58046         
58047         clearInvalid: Roo.form.Field.prototype.clearInvalid,
58048
58049         validate: function() {
58050                 return true;
58051         },
58052         
58053         destroy: function() {
58054                 this.purgeListeners();
58055                 this.getEl.removeAllListeners();
58056                 this.getEl().remove();
58057                 if (this.dragZone) {
58058                         if (this.dragZone.destroy) {
58059                                 this.dragZone.destroy();
58060                         }
58061                 }
58062                 if (this.dropZone) {
58063                         if (this.dropZone.destroy) {
58064                                 this.dropZone.destroy();
58065                         }
58066                 }
58067         },
58068
58069 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
58070         getName: function() {
58071                 return this.name;
58072         },
58073
58074 /**     Loads the View from a JSON string representing the Records to put into the Store. */
58075         setValue: function(v) {
58076                 if (!this.store) {
58077                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
58078                 }
58079                 var data = {};
58080                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
58081                 this.store.proxy = new Roo.data.MemoryProxy(data);
58082                 this.store.load();
58083         },
58084
58085 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
58086         getValue: function() {
58087                 var result = '(';
58088                 this.store.each(function(rec) {
58089                         result += rec.id + ',';
58090                 });
58091                 return result.substr(0, result.length - 1) + ')';
58092         },
58093         
58094         getIds: function() {
58095                 var i = 0, result = new Array(this.store.getCount());
58096                 this.store.each(function(rec) {
58097                         result[i++] = rec.id;
58098                 });
58099                 return result;
58100         },
58101         
58102         isDirty: function() {
58103                 return this.isDirtyFlag;
58104         },
58105
58106 /**
58107  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
58108  *      whole Element becomes the target, and this causes the drop gesture to append.
58109  */
58110     getTargetFromEvent : function(e) {
58111                 var target = e.getTarget();
58112                 while ((target !== null) && (target.parentNode != this.el.dom)) {
58113                 target = target.parentNode;
58114                 }
58115                 if (!target) {
58116                         target = this.el.dom.lastChild || this.el.dom;
58117                 }
58118                 return target;
58119     },
58120
58121 /**
58122  *      Create the drag data which consists of an object which has the property "ddel" as
58123  *      the drag proxy element. 
58124  */
58125     getDragData : function(e) {
58126         var target = this.findItemFromChild(e.getTarget());
58127                 if(target) {
58128                         this.handleSelection(e);
58129                         var selNodes = this.getSelectedNodes();
58130             var dragData = {
58131                 source: this,
58132                 copy: this.copy || (this.allowCopy && e.ctrlKey),
58133                 nodes: selNodes,
58134                 records: []
58135                         };
58136                         var selectedIndices = this.getSelectedIndexes();
58137                         for (var i = 0; i < selectedIndices.length; i++) {
58138                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
58139                         }
58140                         if (selNodes.length == 1) {
58141                                 dragData.ddel = target.cloneNode(true); // the div element
58142                         } else {
58143                                 var div = document.createElement('div'); // create the multi element drag "ghost"
58144                                 div.className = 'multi-proxy';
58145                                 for (var i = 0, len = selNodes.length; i < len; i++) {
58146                                         div.appendChild(selNodes[i].cloneNode(true));
58147                                 }
58148                                 dragData.ddel = div;
58149                         }
58150             //console.log(dragData)
58151             //console.log(dragData.ddel.innerHTML)
58152                         return dragData;
58153                 }
58154         //console.log('nodragData')
58155                 return false;
58156     },
58157     
58158 /**     Specify to which ddGroup items in this DDView may be dragged. */
58159     setDraggable: function(ddGroup) {
58160         if (ddGroup instanceof Array) {
58161                 Roo.each(ddGroup, this.setDraggable, this);
58162                 return;
58163         }
58164         if (this.dragZone) {
58165                 this.dragZone.addToGroup(ddGroup);
58166         } else {
58167                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
58168                                 containerScroll: true,
58169                                 ddGroup: ddGroup 
58170
58171                         });
58172 //                      Draggability implies selection. DragZone's mousedown selects the element.
58173                         if (!this.multiSelect) { this.singleSelect = true; }
58174
58175 //                      Wire the DragZone's handlers up to methods in *this*
58176                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
58177                 }
58178     },
58179
58180 /**     Specify from which ddGroup this DDView accepts drops. */
58181     setDroppable: function(ddGroup) {
58182         if (ddGroup instanceof Array) {
58183                 Roo.each(ddGroup, this.setDroppable, this);
58184                 return;
58185         }
58186         if (this.dropZone) {
58187                 this.dropZone.addToGroup(ddGroup);
58188         } else {
58189                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
58190                                 containerScroll: true,
58191                                 ddGroup: ddGroup
58192                         });
58193
58194 //                      Wire the DropZone's handlers up to methods in *this*
58195                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
58196                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
58197                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
58198                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
58199                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
58200                 }
58201     },
58202
58203 /**     Decide whether to drop above or below a View node. */
58204     getDropPoint : function(e, n, dd){
58205         if (n == this.el.dom) { return "above"; }
58206                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
58207                 var c = t + (b - t) / 2;
58208                 var y = Roo.lib.Event.getPageY(e);
58209                 if(y <= c) {
58210                         return "above";
58211                 }else{
58212                         return "below";
58213                 }
58214     },
58215
58216     onNodeEnter : function(n, dd, e, data){
58217                 return false;
58218     },
58219     
58220     onNodeOver : function(n, dd, e, data){
58221                 var pt = this.getDropPoint(e, n, dd);
58222                 // set the insert point style on the target node
58223                 var dragElClass = this.dropNotAllowed;
58224                 if (pt) {
58225                         var targetElClass;
58226                         if (pt == "above"){
58227                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
58228                                 targetElClass = "x-view-drag-insert-above";
58229                         } else {
58230                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
58231                                 targetElClass = "x-view-drag-insert-below";
58232                         }
58233                         if (this.lastInsertClass != targetElClass){
58234                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
58235                                 this.lastInsertClass = targetElClass;
58236                         }
58237                 }
58238                 return dragElClass;
58239         },
58240
58241     onNodeOut : function(n, dd, e, data){
58242                 this.removeDropIndicators(n);
58243     },
58244
58245     onNodeDrop : function(n, dd, e, data){
58246         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
58247                 return false;
58248         }
58249         var pt = this.getDropPoint(e, n, dd);
58250                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
58251                 if (pt == "below") { insertAt++; }
58252                 for (var i = 0; i < data.records.length; i++) {
58253                         var r = data.records[i];
58254                         var dup = this.store.getById(r.id);
58255                         if (dup && (dd != this.dragZone)) {
58256                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
58257                         } else {
58258                                 if (data.copy) {
58259                                         this.store.insert(insertAt++, r.copy());
58260                                 } else {
58261                                         data.source.isDirtyFlag = true;
58262                                         r.store.remove(r);
58263                                         this.store.insert(insertAt++, r);
58264                                 }
58265                                 this.isDirtyFlag = true;
58266                         }
58267                 }
58268                 this.dragZone.cachedTarget = null;
58269                 return true;
58270     },
58271
58272     removeDropIndicators : function(n){
58273                 if(n){
58274                         Roo.fly(n).removeClass([
58275                                 "x-view-drag-insert-above",
58276                                 "x-view-drag-insert-below"]);
58277                         this.lastInsertClass = "_noclass";
58278                 }
58279     },
58280
58281 /**
58282  *      Utility method. Add a delete option to the DDView's context menu.
58283  *      @param {String} imageUrl The URL of the "delete" icon image.
58284  */
58285         setDeletable: function(imageUrl) {
58286                 if (!this.singleSelect && !this.multiSelect) {
58287                         this.singleSelect = true;
58288                 }
58289                 var c = this.getContextMenu();
58290                 this.contextMenu.on("itemclick", function(item) {
58291                         switch (item.id) {
58292                                 case "delete":
58293                                         this.remove(this.getSelectedIndexes());
58294                                         break;
58295                         }
58296                 }, this);
58297                 this.contextMenu.add({
58298                         icon: imageUrl,
58299                         id: "delete",
58300                         text: 'Delete'
58301                 });
58302         },
58303         
58304 /**     Return the context menu for this DDView. */
58305         getContextMenu: function() {
58306                 if (!this.contextMenu) {
58307 //                      Create the View's context menu
58308                         this.contextMenu = new Roo.menu.Menu({
58309                                 id: this.id + "-contextmenu"
58310                         });
58311                         this.el.on("contextmenu", this.showContextMenu, this);
58312                 }
58313                 return this.contextMenu;
58314         },
58315         
58316         disableContextMenu: function() {
58317                 if (this.contextMenu) {
58318                         this.el.un("contextmenu", this.showContextMenu, this);
58319                 }
58320         },
58321
58322         showContextMenu: function(e, item) {
58323         item = this.findItemFromChild(e.getTarget());
58324                 if (item) {
58325                         e.stopEvent();
58326                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
58327                         this.contextMenu.showAt(e.getXY());
58328             }
58329     },
58330
58331 /**
58332  *      Remove {@link Roo.data.Record}s at the specified indices.
58333  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
58334  */
58335     remove: function(selectedIndices) {
58336                 selectedIndices = [].concat(selectedIndices);
58337                 for (var i = 0; i < selectedIndices.length; i++) {
58338                         var rec = this.store.getAt(selectedIndices[i]);
58339                         this.store.remove(rec);
58340                 }
58341     },
58342
58343 /**
58344  *      Double click fires the event, but also, if this is draggable, and there is only one other
58345  *      related DropZone, it transfers the selected node.
58346  */
58347     onDblClick : function(e){
58348         var item = this.findItemFromChild(e.getTarget());
58349         if(item){
58350             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
58351                 return false;
58352             }
58353             if (this.dragGroup) {
58354                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
58355                     while (targets.indexOf(this.dropZone) > -1) {
58356                             targets.remove(this.dropZone);
58357                                 }
58358                     if (targets.length == 1) {
58359                                         this.dragZone.cachedTarget = null;
58360                         var el = Roo.get(targets[0].getEl());
58361                         var box = el.getBox(true);
58362                         targets[0].onNodeDrop(el.dom, {
58363                                 target: el.dom,
58364                                 xy: [box.x, box.y + box.height - 1]
58365                         }, null, this.getDragData(e));
58366                     }
58367                 }
58368         }
58369     },
58370     
58371     handleSelection: function(e) {
58372                 this.dragZone.cachedTarget = null;
58373         var item = this.findItemFromChild(e.getTarget());
58374         if (!item) {
58375                 this.clearSelections(true);
58376                 return;
58377         }
58378                 if (item && (this.multiSelect || this.singleSelect)){
58379                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
58380                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
58381                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
58382                                 this.unselect(item);
58383                         } else {
58384                                 this.select(item, this.multiSelect && e.ctrlKey);
58385                                 this.lastSelection = item;
58386                         }
58387                 }
58388     },
58389
58390     onItemClick : function(item, index, e){
58391                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
58392                         return false;
58393                 }
58394                 return true;
58395     },
58396
58397     unselect : function(nodeInfo, suppressEvent){
58398                 var node = this.getNode(nodeInfo);
58399                 if(node && this.isSelected(node)){
58400                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
58401                                 Roo.fly(node).removeClass(this.selectedClass);
58402                                 this.selections.remove(node);
58403                                 if(!suppressEvent){
58404                                         this.fireEvent("selectionchange", this, this.selections);
58405                                 }
58406                         }
58407                 }
58408     }
58409 });
58410 /*
58411  * Based on:
58412  * Ext JS Library 1.1.1
58413  * Copyright(c) 2006-2007, Ext JS, LLC.
58414  *
58415  * Originally Released Under LGPL - original licence link has changed is not relivant.
58416  *
58417  * Fork - LGPL
58418  * <script type="text/javascript">
58419  */
58420  
58421 /**
58422  * @class Roo.LayoutManager
58423  * @extends Roo.util.Observable
58424  * Base class for layout managers.
58425  */
58426 Roo.LayoutManager = function(container, config){
58427     Roo.LayoutManager.superclass.constructor.call(this);
58428     this.el = Roo.get(container);
58429     // ie scrollbar fix
58430     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
58431         document.body.scroll = "no";
58432     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
58433         this.el.position('relative');
58434     }
58435     this.id = this.el.id;
58436     this.el.addClass("x-layout-container");
58437     /** false to disable window resize monitoring @type Boolean */
58438     this.monitorWindowResize = true;
58439     this.regions = {};
58440     this.addEvents({
58441         /**
58442          * @event layout
58443          * Fires when a layout is performed. 
58444          * @param {Roo.LayoutManager} this
58445          */
58446         "layout" : true,
58447         /**
58448          * @event regionresized
58449          * Fires when the user resizes a region. 
58450          * @param {Roo.LayoutRegion} region The resized region
58451          * @param {Number} newSize The new size (width for east/west, height for north/south)
58452          */
58453         "regionresized" : true,
58454         /**
58455          * @event regioncollapsed
58456          * Fires when a region is collapsed. 
58457          * @param {Roo.LayoutRegion} region The collapsed region
58458          */
58459         "regioncollapsed" : true,
58460         /**
58461          * @event regionexpanded
58462          * Fires when a region is expanded.  
58463          * @param {Roo.LayoutRegion} region The expanded region
58464          */
58465         "regionexpanded" : true
58466     });
58467     this.updating = false;
58468     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
58469 };
58470
58471 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
58472     /**
58473      * Returns true if this layout is currently being updated
58474      * @return {Boolean}
58475      */
58476     isUpdating : function(){
58477         return this.updating; 
58478     },
58479     
58480     /**
58481      * Suspend the LayoutManager from doing auto-layouts while
58482      * making multiple add or remove calls
58483      */
58484     beginUpdate : function(){
58485         this.updating = true;    
58486     },
58487     
58488     /**
58489      * Restore auto-layouts and optionally disable the manager from performing a layout
58490      * @param {Boolean} noLayout true to disable a layout update 
58491      */
58492     endUpdate : function(noLayout){
58493         this.updating = false;
58494         if(!noLayout){
58495             this.layout();
58496         }    
58497     },
58498     
58499     layout: function(){
58500         
58501     },
58502     
58503     onRegionResized : function(region, newSize){
58504         this.fireEvent("regionresized", region, newSize);
58505         this.layout();
58506     },
58507     
58508     onRegionCollapsed : function(region){
58509         this.fireEvent("regioncollapsed", region);
58510     },
58511     
58512     onRegionExpanded : function(region){
58513         this.fireEvent("regionexpanded", region);
58514     },
58515         
58516     /**
58517      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
58518      * performs box-model adjustments.
58519      * @return {Object} The size as an object {width: (the width), height: (the height)}
58520      */
58521     getViewSize : function(){
58522         var size;
58523         if(this.el.dom != document.body){
58524             size = this.el.getSize();
58525         }else{
58526             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
58527         }
58528         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
58529         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
58530         return size;
58531     },
58532     
58533     /**
58534      * Returns the Element this layout is bound to.
58535      * @return {Roo.Element}
58536      */
58537     getEl : function(){
58538         return this.el;
58539     },
58540     
58541     /**
58542      * Returns the specified region.
58543      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
58544      * @return {Roo.LayoutRegion}
58545      */
58546     getRegion : function(target){
58547         return this.regions[target.toLowerCase()];
58548     },
58549     
58550     onWindowResize : function(){
58551         if(this.monitorWindowResize){
58552             this.layout();
58553         }
58554     }
58555 });/*
58556  * Based on:
58557  * Ext JS Library 1.1.1
58558  * Copyright(c) 2006-2007, Ext JS, LLC.
58559  *
58560  * Originally Released Under LGPL - original licence link has changed is not relivant.
58561  *
58562  * Fork - LGPL
58563  * <script type="text/javascript">
58564  */
58565 /**
58566  * @class Roo.BorderLayout
58567  * @extends Roo.LayoutManager
58568  * @children Roo.ContentPanel
58569  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
58570  * please see: <br><br>
58571  * <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>
58572  * <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>
58573  * Example:
58574  <pre><code>
58575  var layout = new Roo.BorderLayout(document.body, {
58576     north: {
58577         initialSize: 25,
58578         titlebar: false
58579     },
58580     west: {
58581         split:true,
58582         initialSize: 200,
58583         minSize: 175,
58584         maxSize: 400,
58585         titlebar: true,
58586         collapsible: true
58587     },
58588     east: {
58589         split:true,
58590         initialSize: 202,
58591         minSize: 175,
58592         maxSize: 400,
58593         titlebar: true,
58594         collapsible: true
58595     },
58596     south: {
58597         split:true,
58598         initialSize: 100,
58599         minSize: 100,
58600         maxSize: 200,
58601         titlebar: true,
58602         collapsible: true
58603     },
58604     center: {
58605         titlebar: true,
58606         autoScroll:true,
58607         resizeTabs: true,
58608         minTabWidth: 50,
58609         preferredTabWidth: 150
58610     }
58611 });
58612
58613 // shorthand
58614 var CP = Roo.ContentPanel;
58615
58616 layout.beginUpdate();
58617 layout.add("north", new CP("north", "North"));
58618 layout.add("south", new CP("south", {title: "South", closable: true}));
58619 layout.add("west", new CP("west", {title: "West"}));
58620 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
58621 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
58622 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
58623 layout.getRegion("center").showPanel("center1");
58624 layout.endUpdate();
58625 </code></pre>
58626
58627 <b>The container the layout is rendered into can be either the body element or any other element.
58628 If it is not the body element, the container needs to either be an absolute positioned element,
58629 or you will need to add "position:relative" to the css of the container.  You will also need to specify
58630 the container size if it is not the body element.</b>
58631
58632 * @constructor
58633 * Create a new BorderLayout
58634 * @param {String/HTMLElement/Element} container The container this layout is bound to
58635 * @param {Object} config Configuration options
58636  */
58637 Roo.BorderLayout = function(container, config){
58638     config = config || {};
58639     Roo.BorderLayout.superclass.constructor.call(this, container, config);
58640     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
58641     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
58642         var target = this.factory.validRegions[i];
58643         if(config[target]){
58644             this.addRegion(target, config[target]);
58645         }
58646     }
58647 };
58648
58649 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
58650         
58651         /**
58652          * @cfg {Roo.LayoutRegion} east
58653          */
58654         /**
58655          * @cfg {Roo.LayoutRegion} west
58656          */
58657         /**
58658          * @cfg {Roo.LayoutRegion} north
58659          */
58660         /**
58661          * @cfg {Roo.LayoutRegion} south
58662          */
58663         /**
58664          * @cfg {Roo.LayoutRegion} center
58665          */
58666     /**
58667      * Creates and adds a new region if it doesn't already exist.
58668      * @param {String} target The target region key (north, south, east, west or center).
58669      * @param {Object} config The regions config object
58670      * @return {BorderLayoutRegion} The new region
58671      */
58672     addRegion : function(target, config){
58673         if(!this.regions[target]){
58674             var r = this.factory.create(target, this, config);
58675             this.bindRegion(target, r);
58676         }
58677         return this.regions[target];
58678     },
58679
58680     // private (kinda)
58681     bindRegion : function(name, r){
58682         this.regions[name] = r;
58683         r.on("visibilitychange", this.layout, this);
58684         r.on("paneladded", this.layout, this);
58685         r.on("panelremoved", this.layout, this);
58686         r.on("invalidated", this.layout, this);
58687         r.on("resized", this.onRegionResized, this);
58688         r.on("collapsed", this.onRegionCollapsed, this);
58689         r.on("expanded", this.onRegionExpanded, this);
58690     },
58691
58692     /**
58693      * Performs a layout update.
58694      */
58695     layout : function(){
58696         if(this.updating) {
58697             return;
58698         }
58699         var size = this.getViewSize();
58700         var w = size.width;
58701         var h = size.height;
58702         var centerW = w;
58703         var centerH = h;
58704         var centerY = 0;
58705         var centerX = 0;
58706         //var x = 0, y = 0;
58707
58708         var rs = this.regions;
58709         var north = rs["north"];
58710         var south = rs["south"]; 
58711         var west = rs["west"];
58712         var east = rs["east"];
58713         var center = rs["center"];
58714         //if(this.hideOnLayout){ // not supported anymore
58715             //c.el.setStyle("display", "none");
58716         //}
58717         if(north && north.isVisible()){
58718             var b = north.getBox();
58719             var m = north.getMargins();
58720             b.width = w - (m.left+m.right);
58721             b.x = m.left;
58722             b.y = m.top;
58723             centerY = b.height + b.y + m.bottom;
58724             centerH -= centerY;
58725             north.updateBox(this.safeBox(b));
58726         }
58727         if(south && south.isVisible()){
58728             var b = south.getBox();
58729             var m = south.getMargins();
58730             b.width = w - (m.left+m.right);
58731             b.x = m.left;
58732             var totalHeight = (b.height + m.top + m.bottom);
58733             b.y = h - totalHeight + m.top;
58734             centerH -= totalHeight;
58735             south.updateBox(this.safeBox(b));
58736         }
58737         if(west && west.isVisible()){
58738             var b = west.getBox();
58739             var m = west.getMargins();
58740             b.height = centerH - (m.top+m.bottom);
58741             b.x = m.left;
58742             b.y = centerY + m.top;
58743             var totalWidth = (b.width + m.left + m.right);
58744             centerX += totalWidth;
58745             centerW -= totalWidth;
58746             west.updateBox(this.safeBox(b));
58747         }
58748         if(east && east.isVisible()){
58749             var b = east.getBox();
58750             var m = east.getMargins();
58751             b.height = centerH - (m.top+m.bottom);
58752             var totalWidth = (b.width + m.left + m.right);
58753             b.x = w - totalWidth + m.left;
58754             b.y = centerY + m.top;
58755             centerW -= totalWidth;
58756             east.updateBox(this.safeBox(b));
58757         }
58758         if(center){
58759             var m = center.getMargins();
58760             var centerBox = {
58761                 x: centerX + m.left,
58762                 y: centerY + m.top,
58763                 width: centerW - (m.left+m.right),
58764                 height: centerH - (m.top+m.bottom)
58765             };
58766             //if(this.hideOnLayout){
58767                 //center.el.setStyle("display", "block");
58768             //}
58769             center.updateBox(this.safeBox(centerBox));
58770         }
58771         this.el.repaint();
58772         this.fireEvent("layout", this);
58773     },
58774
58775     // private
58776     safeBox : function(box){
58777         box.width = Math.max(0, box.width);
58778         box.height = Math.max(0, box.height);
58779         return box;
58780     },
58781
58782     /**
58783      * Adds a ContentPanel (or subclass) to this layout.
58784      * @param {String} target The target region key (north, south, east, west or center).
58785      * @param {Roo.ContentPanel} panel The panel to add
58786      * @return {Roo.ContentPanel} The added panel
58787      */
58788     add : function(target, panel){
58789          
58790         target = target.toLowerCase();
58791         return this.regions[target].add(panel);
58792     },
58793
58794     /**
58795      * Remove a ContentPanel (or subclass) to this layout.
58796      * @param {String} target The target region key (north, south, east, west or center).
58797      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
58798      * @return {Roo.ContentPanel} The removed panel
58799      */
58800     remove : function(target, panel){
58801         target = target.toLowerCase();
58802         return this.regions[target].remove(panel);
58803     },
58804
58805     /**
58806      * Searches all regions for a panel with the specified id
58807      * @param {String} panelId
58808      * @return {Roo.ContentPanel} The panel or null if it wasn't found
58809      */
58810     findPanel : function(panelId){
58811         var rs = this.regions;
58812         for(var target in rs){
58813             if(typeof rs[target] != "function"){
58814                 var p = rs[target].getPanel(panelId);
58815                 if(p){
58816                     return p;
58817                 }
58818             }
58819         }
58820         return null;
58821     },
58822
58823     /**
58824      * Searches all regions for a panel with the specified id and activates (shows) it.
58825      * @param {String/ContentPanel} panelId The panels id or the panel itself
58826      * @return {Roo.ContentPanel} The shown panel or null
58827      */
58828     showPanel : function(panelId) {
58829       var rs = this.regions;
58830       for(var target in rs){
58831          var r = rs[target];
58832          if(typeof r != "function"){
58833             if(r.hasPanel(panelId)){
58834                return r.showPanel(panelId);
58835             }
58836          }
58837       }
58838       return null;
58839    },
58840
58841    /**
58842      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
58843      * @param {Roo.state.Provider} provider (optional) An alternate state provider
58844      */
58845     restoreState : function(provider){
58846         if(!provider){
58847             provider = Roo.state.Manager;
58848         }
58849         var sm = new Roo.LayoutStateManager();
58850         sm.init(this, provider);
58851     },
58852
58853     /**
58854      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
58855      * object should contain properties for each region to add ContentPanels to, and each property's value should be
58856      * a valid ContentPanel config object.  Example:
58857      * <pre><code>
58858 // Create the main layout
58859 var layout = new Roo.BorderLayout('main-ct', {
58860     west: {
58861         split:true,
58862         minSize: 175,
58863         titlebar: true
58864     },
58865     center: {
58866         title:'Components'
58867     }
58868 }, 'main-ct');
58869
58870 // Create and add multiple ContentPanels at once via configs
58871 layout.batchAdd({
58872    west: {
58873        id: 'source-files',
58874        autoCreate:true,
58875        title:'Ext Source Files',
58876        autoScroll:true,
58877        fitToFrame:true
58878    },
58879    center : {
58880        el: cview,
58881        autoScroll:true,
58882        fitToFrame:true,
58883        toolbar: tb,
58884        resizeEl:'cbody'
58885    }
58886 });
58887 </code></pre>
58888      * @param {Object} regions An object containing ContentPanel configs by region name
58889      */
58890     batchAdd : function(regions){
58891         this.beginUpdate();
58892         for(var rname in regions){
58893             var lr = this.regions[rname];
58894             if(lr){
58895                 this.addTypedPanels(lr, regions[rname]);
58896             }
58897         }
58898         this.endUpdate();
58899     },
58900
58901     // private
58902     addTypedPanels : function(lr, ps){
58903         if(typeof ps == 'string'){
58904             lr.add(new Roo.ContentPanel(ps));
58905         }
58906         else if(ps instanceof Array){
58907             for(var i =0, len = ps.length; i < len; i++){
58908                 this.addTypedPanels(lr, ps[i]);
58909             }
58910         }
58911         else if(!ps.events){ // raw config?
58912             var el = ps.el;
58913             delete ps.el; // prevent conflict
58914             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
58915         }
58916         else {  // panel object assumed!
58917             lr.add(ps);
58918         }
58919     },
58920     /**
58921      * Adds a xtype elements to the layout.
58922      * <pre><code>
58923
58924 layout.addxtype({
58925        xtype : 'ContentPanel',
58926        region: 'west',
58927        items: [ .... ]
58928    }
58929 );
58930
58931 layout.addxtype({
58932         xtype : 'NestedLayoutPanel',
58933         region: 'west',
58934         layout: {
58935            center: { },
58936            west: { }   
58937         },
58938         items : [ ... list of content panels or nested layout panels.. ]
58939    }
58940 );
58941 </code></pre>
58942      * @param {Object} cfg Xtype definition of item to add.
58943      */
58944     addxtype : function(cfg)
58945     {
58946         // basically accepts a pannel...
58947         // can accept a layout region..!?!?
58948         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
58949         
58950         if (!cfg.xtype.match(/Panel$/)) {
58951             return false;
58952         }
58953         var ret = false;
58954         
58955         if (typeof(cfg.region) == 'undefined') {
58956             Roo.log("Failed to add Panel, region was not set");
58957             Roo.log(cfg);
58958             return false;
58959         }
58960         var region = cfg.region;
58961         delete cfg.region;
58962         
58963           
58964         var xitems = [];
58965         if (cfg.items) {
58966             xitems = cfg.items;
58967             delete cfg.items;
58968         }
58969         var nb = false;
58970         
58971         switch(cfg.xtype) 
58972         {
58973             case 'ContentPanel':  // ContentPanel (el, cfg)
58974             case 'ScrollPanel':  // ContentPanel (el, cfg)
58975             case 'ViewPanel': 
58976                 if(cfg.autoCreate) {
58977                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58978                 } else {
58979                     var el = this.el.createChild();
58980                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
58981                 }
58982                 
58983                 this.add(region, ret);
58984                 break;
58985             
58986             
58987             case 'TreePanel': // our new panel!
58988                 cfg.el = this.el.createChild();
58989                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
58990                 this.add(region, ret);
58991                 break;
58992             
58993             case 'NestedLayoutPanel': 
58994                 // create a new Layout (which is  a Border Layout...
58995                 var el = this.el.createChild();
58996                 var clayout = cfg.layout;
58997                 delete cfg.layout;
58998                 clayout.items   = clayout.items  || [];
58999                 // replace this exitems with the clayout ones..
59000                 xitems = clayout.items;
59001                  
59002                 
59003                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
59004                     cfg.background = false;
59005                 }
59006                 var layout = new Roo.BorderLayout(el, clayout);
59007                 
59008                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
59009                 //console.log('adding nested layout panel '  + cfg.toSource());
59010                 this.add(region, ret);
59011                 nb = {}; /// find first...
59012                 break;
59013                 
59014             case 'GridPanel': 
59015             
59016                 // needs grid and region
59017                 
59018                 //var el = this.getRegion(region).el.createChild();
59019                 var el = this.el.createChild();
59020                 // create the grid first...
59021                 
59022                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
59023                 delete cfg.grid;
59024                 if (region == 'center' && this.active ) {
59025                     cfg.background = false;
59026                 }
59027                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
59028                 
59029                 this.add(region, ret);
59030                 if (cfg.background) {
59031                     ret.on('activate', function(gp) {
59032                         if (!gp.grid.rendered) {
59033                             gp.grid.render();
59034                         }
59035                     });
59036                 } else {
59037                     grid.render();
59038                 }
59039                 break;
59040            
59041            
59042            
59043                 
59044                 
59045                 
59046             default:
59047                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
59048                     
59049                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
59050                     this.add(region, ret);
59051                 } else {
59052                 
59053                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
59054                     return null;
59055                 }
59056                 
59057              // GridPanel (grid, cfg)
59058             
59059         }
59060         this.beginUpdate();
59061         // add children..
59062         var region = '';
59063         var abn = {};
59064         Roo.each(xitems, function(i)  {
59065             region = nb && i.region ? i.region : false;
59066             
59067             var add = ret.addxtype(i);
59068            
59069             if (region) {
59070                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
59071                 if (!i.background) {
59072                     abn[region] = nb[region] ;
59073                 }
59074             }
59075             
59076         });
59077         this.endUpdate();
59078
59079         // make the last non-background panel active..
59080         //if (nb) { Roo.log(abn); }
59081         if (nb) {
59082             
59083             for(var r in abn) {
59084                 region = this.getRegion(r);
59085                 if (region) {
59086                     // tried using nb[r], but it does not work..
59087                      
59088                     region.showPanel(abn[r]);
59089                    
59090                 }
59091             }
59092         }
59093         return ret;
59094         
59095     }
59096 });
59097
59098 /**
59099  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
59100  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
59101  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
59102  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
59103  * <pre><code>
59104 // shorthand
59105 var CP = Roo.ContentPanel;
59106
59107 var layout = Roo.BorderLayout.create({
59108     north: {
59109         initialSize: 25,
59110         titlebar: false,
59111         panels: [new CP("north", "North")]
59112     },
59113     west: {
59114         split:true,
59115         initialSize: 200,
59116         minSize: 175,
59117         maxSize: 400,
59118         titlebar: true,
59119         collapsible: true,
59120         panels: [new CP("west", {title: "West"})]
59121     },
59122     east: {
59123         split:true,
59124         initialSize: 202,
59125         minSize: 175,
59126         maxSize: 400,
59127         titlebar: true,
59128         collapsible: true,
59129         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
59130     },
59131     south: {
59132         split:true,
59133         initialSize: 100,
59134         minSize: 100,
59135         maxSize: 200,
59136         titlebar: true,
59137         collapsible: true,
59138         panels: [new CP("south", {title: "South", closable: true})]
59139     },
59140     center: {
59141         titlebar: true,
59142         autoScroll:true,
59143         resizeTabs: true,
59144         minTabWidth: 50,
59145         preferredTabWidth: 150,
59146         panels: [
59147             new CP("center1", {title: "Close Me", closable: true}),
59148             new CP("center2", {title: "Center Panel", closable: false})
59149         ]
59150     }
59151 }, document.body);
59152
59153 layout.getRegion("center").showPanel("center1");
59154 </code></pre>
59155  * @param config
59156  * @param targetEl
59157  */
59158 Roo.BorderLayout.create = function(config, targetEl){
59159     var layout = new Roo.BorderLayout(targetEl || document.body, config);
59160     layout.beginUpdate();
59161     var regions = Roo.BorderLayout.RegionFactory.validRegions;
59162     for(var j = 0, jlen = regions.length; j < jlen; j++){
59163         var lr = regions[j];
59164         if(layout.regions[lr] && config[lr].panels){
59165             var r = layout.regions[lr];
59166             var ps = config[lr].panels;
59167             layout.addTypedPanels(r, ps);
59168         }
59169     }
59170     layout.endUpdate();
59171     return layout;
59172 };
59173
59174 // private
59175 Roo.BorderLayout.RegionFactory = {
59176     // private
59177     validRegions : ["north","south","east","west","center"],
59178
59179     // private
59180     create : function(target, mgr, config){
59181         target = target.toLowerCase();
59182         if(config.lightweight || config.basic){
59183             return new Roo.BasicLayoutRegion(mgr, config, target);
59184         }
59185         switch(target){
59186             case "north":
59187                 return new Roo.NorthLayoutRegion(mgr, config);
59188             case "south":
59189                 return new Roo.SouthLayoutRegion(mgr, config);
59190             case "east":
59191                 return new Roo.EastLayoutRegion(mgr, config);
59192             case "west":
59193                 return new Roo.WestLayoutRegion(mgr, config);
59194             case "center":
59195                 return new Roo.CenterLayoutRegion(mgr, config);
59196         }
59197         throw 'Layout region "'+target+'" not supported.';
59198     }
59199 };/*
59200  * Based on:
59201  * Ext JS Library 1.1.1
59202  * Copyright(c) 2006-2007, Ext JS, LLC.
59203  *
59204  * Originally Released Under LGPL - original licence link has changed is not relivant.
59205  *
59206  * Fork - LGPL
59207  * <script type="text/javascript">
59208  */
59209  
59210 /**
59211  * @class Roo.BasicLayoutRegion
59212  * @extends Roo.util.Observable
59213  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
59214  * and does not have a titlebar, tabs or any other features. All it does is size and position 
59215  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
59216  */
59217 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
59218     this.mgr = mgr;
59219     this.position  = pos;
59220     this.events = {
59221         /**
59222          * @scope Roo.BasicLayoutRegion
59223          */
59224         
59225         /**
59226          * @event beforeremove
59227          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
59228          * @param {Roo.LayoutRegion} this
59229          * @param {Roo.ContentPanel} panel The panel
59230          * @param {Object} e The cancel event object
59231          */
59232         "beforeremove" : true,
59233         /**
59234          * @event invalidated
59235          * Fires when the layout for this region is changed.
59236          * @param {Roo.LayoutRegion} this
59237          */
59238         "invalidated" : true,
59239         /**
59240          * @event visibilitychange
59241          * Fires when this region is shown or hidden 
59242          * @param {Roo.LayoutRegion} this
59243          * @param {Boolean} visibility true or false
59244          */
59245         "visibilitychange" : true,
59246         /**
59247          * @event paneladded
59248          * Fires when a panel is added. 
59249          * @param {Roo.LayoutRegion} this
59250          * @param {Roo.ContentPanel} panel The panel
59251          */
59252         "paneladded" : true,
59253         /**
59254          * @event panelremoved
59255          * Fires when a panel is removed. 
59256          * @param {Roo.LayoutRegion} this
59257          * @param {Roo.ContentPanel} panel The panel
59258          */
59259         "panelremoved" : true,
59260         /**
59261          * @event beforecollapse
59262          * Fires when this region before collapse.
59263          * @param {Roo.LayoutRegion} this
59264          */
59265         "beforecollapse" : true,
59266         /**
59267          * @event collapsed
59268          * Fires when this region is collapsed.
59269          * @param {Roo.LayoutRegion} this
59270          */
59271         "collapsed" : true,
59272         /**
59273          * @event expanded
59274          * Fires when this region is expanded.
59275          * @param {Roo.LayoutRegion} this
59276          */
59277         "expanded" : true,
59278         /**
59279          * @event slideshow
59280          * Fires when this region is slid into view.
59281          * @param {Roo.LayoutRegion} this
59282          */
59283         "slideshow" : true,
59284         /**
59285          * @event slidehide
59286          * Fires when this region slides out of view. 
59287          * @param {Roo.LayoutRegion} this
59288          */
59289         "slidehide" : true,
59290         /**
59291          * @event panelactivated
59292          * Fires when a panel is activated. 
59293          * @param {Roo.LayoutRegion} this
59294          * @param {Roo.ContentPanel} panel The activated panel
59295          */
59296         "panelactivated" : true,
59297         /**
59298          * @event resized
59299          * Fires when the user resizes this region. 
59300          * @param {Roo.LayoutRegion} this
59301          * @param {Number} newSize The new size (width for east/west, height for north/south)
59302          */
59303         "resized" : true
59304     };
59305     /** A collection of panels in this region. @type Roo.util.MixedCollection */
59306     this.panels = new Roo.util.MixedCollection();
59307     this.panels.getKey = this.getPanelId.createDelegate(this);
59308     this.box = null;
59309     this.activePanel = null;
59310     // ensure listeners are added...
59311     
59312     if (config.listeners || config.events) {
59313         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
59314             listeners : config.listeners || {},
59315             events : config.events || {}
59316         });
59317     }
59318     
59319     if(skipConfig !== true){
59320         this.applyConfig(config);
59321     }
59322 };
59323
59324 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
59325     getPanelId : function(p){
59326         return p.getId();
59327     },
59328     
59329     applyConfig : function(config){
59330         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59331         this.config = config;
59332         
59333     },
59334     
59335     /**
59336      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
59337      * the width, for horizontal (north, south) the height.
59338      * @param {Number} newSize The new width or height
59339      */
59340     resizeTo : function(newSize){
59341         var el = this.el ? this.el :
59342                  (this.activePanel ? this.activePanel.getEl() : null);
59343         if(el){
59344             switch(this.position){
59345                 case "east":
59346                 case "west":
59347                     el.setWidth(newSize);
59348                     this.fireEvent("resized", this, newSize);
59349                 break;
59350                 case "north":
59351                 case "south":
59352                     el.setHeight(newSize);
59353                     this.fireEvent("resized", this, newSize);
59354                 break;                
59355             }
59356         }
59357     },
59358     
59359     getBox : function(){
59360         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
59361     },
59362     
59363     getMargins : function(){
59364         return this.margins;
59365     },
59366     
59367     updateBox : function(box){
59368         this.box = box;
59369         var el = this.activePanel.getEl();
59370         el.dom.style.left = box.x + "px";
59371         el.dom.style.top = box.y + "px";
59372         this.activePanel.setSize(box.width, box.height);
59373     },
59374     
59375     /**
59376      * Returns the container element for this region.
59377      * @return {Roo.Element}
59378      */
59379     getEl : function(){
59380         return this.activePanel;
59381     },
59382     
59383     /**
59384      * Returns true if this region is currently visible.
59385      * @return {Boolean}
59386      */
59387     isVisible : function(){
59388         return this.activePanel ? true : false;
59389     },
59390     
59391     setActivePanel : function(panel){
59392         panel = this.getPanel(panel);
59393         if(this.activePanel && this.activePanel != panel){
59394             this.activePanel.setActiveState(false);
59395             this.activePanel.getEl().setLeftTop(-10000,-10000);
59396         }
59397         this.activePanel = panel;
59398         panel.setActiveState(true);
59399         if(this.box){
59400             panel.setSize(this.box.width, this.box.height);
59401         }
59402         this.fireEvent("panelactivated", this, panel);
59403         this.fireEvent("invalidated");
59404     },
59405     
59406     /**
59407      * Show the specified panel.
59408      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
59409      * @return {Roo.ContentPanel} The shown panel or null
59410      */
59411     showPanel : function(panel){
59412         if(panel = this.getPanel(panel)){
59413             this.setActivePanel(panel);
59414         }
59415         return panel;
59416     },
59417     
59418     /**
59419      * Get the active panel for this region.
59420      * @return {Roo.ContentPanel} The active panel or null
59421      */
59422     getActivePanel : function(){
59423         return this.activePanel;
59424     },
59425     
59426     /**
59427      * Add the passed ContentPanel(s)
59428      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
59429      * @return {Roo.ContentPanel} The panel added (if only one was added)
59430      */
59431     add : function(panel){
59432         if(arguments.length > 1){
59433             for(var i = 0, len = arguments.length; i < len; i++) {
59434                 this.add(arguments[i]);
59435             }
59436             return null;
59437         }
59438         if(this.hasPanel(panel)){
59439             this.showPanel(panel);
59440             return panel;
59441         }
59442         var el = panel.getEl();
59443         if(el.dom.parentNode != this.mgr.el.dom){
59444             this.mgr.el.dom.appendChild(el.dom);
59445         }
59446         if(panel.setRegion){
59447             panel.setRegion(this);
59448         }
59449         this.panels.add(panel);
59450         el.setStyle("position", "absolute");
59451         if(!panel.background){
59452             this.setActivePanel(panel);
59453             if(this.config.initialSize && this.panels.getCount()==1){
59454                 this.resizeTo(this.config.initialSize);
59455             }
59456         }
59457         this.fireEvent("paneladded", this, panel);
59458         return panel;
59459     },
59460     
59461     /**
59462      * Returns true if the panel is in this region.
59463      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59464      * @return {Boolean}
59465      */
59466     hasPanel : function(panel){
59467         if(typeof panel == "object"){ // must be panel obj
59468             panel = panel.getId();
59469         }
59470         return this.getPanel(panel) ? true : false;
59471     },
59472     
59473     /**
59474      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
59475      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59476      * @param {Boolean} preservePanel Overrides the config preservePanel option
59477      * @return {Roo.ContentPanel} The panel that was removed
59478      */
59479     remove : function(panel, preservePanel){
59480         panel = this.getPanel(panel);
59481         if(!panel){
59482             return null;
59483         }
59484         var e = {};
59485         this.fireEvent("beforeremove", this, panel, e);
59486         if(e.cancel === true){
59487             return null;
59488         }
59489         var panelId = panel.getId();
59490         this.panels.removeKey(panelId);
59491         return panel;
59492     },
59493     
59494     /**
59495      * Returns the panel specified or null if it's not in this region.
59496      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
59497      * @return {Roo.ContentPanel}
59498      */
59499     getPanel : function(id){
59500         if(typeof id == "object"){ // must be panel obj
59501             return id;
59502         }
59503         return this.panels.get(id);
59504     },
59505     
59506     /**
59507      * Returns this regions position (north/south/east/west/center).
59508      * @return {String} 
59509      */
59510     getPosition: function(){
59511         return this.position;    
59512     }
59513 });/*
59514  * Based on:
59515  * Ext JS Library 1.1.1
59516  * Copyright(c) 2006-2007, Ext JS, LLC.
59517  *
59518  * Originally Released Under LGPL - original licence link has changed is not relivant.
59519  *
59520  * Fork - LGPL
59521  * <script type="text/javascript">
59522  */
59523  
59524 /**
59525  * @class Roo.LayoutRegion
59526  * @extends Roo.BasicLayoutRegion
59527  * This class represents a region in a layout manager.
59528  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
59529  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
59530  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
59531  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
59532  * @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})
59533  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
59534  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
59535  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
59536  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
59537  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
59538  * @cfg {String}    title           The title for the region (overrides panel titles)
59539  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
59540  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
59541  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
59542  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
59543  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
59544  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
59545  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
59546  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
59547  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
59548  * @cfg {Boolean}   showPin         True to show a pin button
59549  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
59550  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
59551  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
59552  * @cfg {Number}    width           For East/West panels
59553  * @cfg {Number}    height          For North/South panels
59554  * @cfg {Boolean}   split           To show the splitter
59555  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
59556  */
59557 Roo.LayoutRegion = function(mgr, config, pos){
59558     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
59559     var dh = Roo.DomHelper;
59560     /** This region's container element 
59561     * @type Roo.Element */
59562     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
59563     /** This region's title element 
59564     * @type Roo.Element */
59565
59566     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
59567         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
59568         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
59569     ]}, true);
59570     this.titleEl.enableDisplayMode();
59571     /** This region's title text element 
59572     * @type HTMLElement */
59573     this.titleTextEl = this.titleEl.dom.firstChild;
59574     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
59575     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
59576     this.closeBtn.enableDisplayMode();
59577     this.closeBtn.on("click", this.closeClicked, this);
59578     this.closeBtn.hide();
59579
59580     this.createBody(config);
59581     this.visible = true;
59582     this.collapsed = false;
59583
59584     if(config.hideWhenEmpty){
59585         this.hide();
59586         this.on("paneladded", this.validateVisibility, this);
59587         this.on("panelremoved", this.validateVisibility, this);
59588     }
59589     this.applyConfig(config);
59590 };
59591
59592 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
59593
59594     createBody : function(){
59595         /** This region's body element 
59596         * @type Roo.Element */
59597         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
59598     },
59599
59600     applyConfig : function(c){
59601         if(c.collapsible && this.position != "center" && !this.collapsedEl){
59602             var dh = Roo.DomHelper;
59603             if(c.titlebar !== false){
59604                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
59605                 this.collapseBtn.on("click", this.collapse, this);
59606                 this.collapseBtn.enableDisplayMode();
59607
59608                 if(c.showPin === true || this.showPin){
59609                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
59610                     this.stickBtn.enableDisplayMode();
59611                     this.stickBtn.on("click", this.expand, this);
59612                     this.stickBtn.hide();
59613                 }
59614             }
59615             /** This region's collapsed element
59616             * @type Roo.Element */
59617             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
59618                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
59619             ]}, true);
59620             if(c.floatable !== false){
59621                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
59622                this.collapsedEl.on("click", this.collapseClick, this);
59623             }
59624
59625             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
59626                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
59627                    id: "message", unselectable: "on", style:{"float":"left"}});
59628                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
59629              }
59630             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
59631             this.expandBtn.on("click", this.expand, this);
59632         }
59633         if(this.collapseBtn){
59634             this.collapseBtn.setVisible(c.collapsible == true);
59635         }
59636         this.cmargins = c.cmargins || this.cmargins ||
59637                          (this.position == "west" || this.position == "east" ?
59638                              {top: 0, left: 2, right:2, bottom: 0} :
59639                              {top: 2, left: 0, right:0, bottom: 2});
59640         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
59641         this.bottomTabs = c.tabPosition != "top";
59642         this.autoScroll = c.autoScroll || false;
59643         if(this.autoScroll){
59644             this.bodyEl.setStyle("overflow", "auto");
59645         }else{
59646             this.bodyEl.setStyle("overflow", "hidden");
59647         }
59648         //if(c.titlebar !== false){
59649             if((!c.titlebar && !c.title) || c.titlebar === false){
59650                 this.titleEl.hide();
59651             }else{
59652                 this.titleEl.show();
59653                 if(c.title){
59654                     this.titleTextEl.innerHTML = c.title;
59655                 }
59656             }
59657         //}
59658         this.duration = c.duration || .30;
59659         this.slideDuration = c.slideDuration || .45;
59660         this.config = c;
59661         if(c.collapsed){
59662             this.collapse(true);
59663         }
59664         if(c.hidden){
59665             this.hide();
59666         }
59667     },
59668     /**
59669      * Returns true if this region is currently visible.
59670      * @return {Boolean}
59671      */
59672     isVisible : function(){
59673         return this.visible;
59674     },
59675
59676     /**
59677      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
59678      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
59679      */
59680     setCollapsedTitle : function(title){
59681         title = title || "&#160;";
59682         if(this.collapsedTitleTextEl){
59683             this.collapsedTitleTextEl.innerHTML = title;
59684         }
59685     },
59686
59687     getBox : function(){
59688         var b;
59689         if(!this.collapsed){
59690             b = this.el.getBox(false, true);
59691         }else{
59692             b = this.collapsedEl.getBox(false, true);
59693         }
59694         return b;
59695     },
59696
59697     getMargins : function(){
59698         return this.collapsed ? this.cmargins : this.margins;
59699     },
59700
59701     highlight : function(){
59702         this.el.addClass("x-layout-panel-dragover");
59703     },
59704
59705     unhighlight : function(){
59706         this.el.removeClass("x-layout-panel-dragover");
59707     },
59708
59709     updateBox : function(box){
59710         this.box = box;
59711         if(!this.collapsed){
59712             this.el.dom.style.left = box.x + "px";
59713             this.el.dom.style.top = box.y + "px";
59714             this.updateBody(box.width, box.height);
59715         }else{
59716             this.collapsedEl.dom.style.left = box.x + "px";
59717             this.collapsedEl.dom.style.top = box.y + "px";
59718             this.collapsedEl.setSize(box.width, box.height);
59719         }
59720         if(this.tabs){
59721             this.tabs.autoSizeTabs();
59722         }
59723     },
59724
59725     updateBody : function(w, h){
59726         if(w !== null){
59727             this.el.setWidth(w);
59728             w -= this.el.getBorderWidth("rl");
59729             if(this.config.adjustments){
59730                 w += this.config.adjustments[0];
59731             }
59732         }
59733         if(h !== null){
59734             this.el.setHeight(h);
59735             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
59736             h -= this.el.getBorderWidth("tb");
59737             if(this.config.adjustments){
59738                 h += this.config.adjustments[1];
59739             }
59740             this.bodyEl.setHeight(h);
59741             if(this.tabs){
59742                 h = this.tabs.syncHeight(h);
59743             }
59744         }
59745         if(this.panelSize){
59746             w = w !== null ? w : this.panelSize.width;
59747             h = h !== null ? h : this.panelSize.height;
59748         }
59749         if(this.activePanel){
59750             var el = this.activePanel.getEl();
59751             w = w !== null ? w : el.getWidth();
59752             h = h !== null ? h : el.getHeight();
59753             this.panelSize = {width: w, height: h};
59754             this.activePanel.setSize(w, h);
59755         }
59756         if(Roo.isIE && this.tabs){
59757             this.tabs.el.repaint();
59758         }
59759     },
59760
59761     /**
59762      * Returns the container element for this region.
59763      * @return {Roo.Element}
59764      */
59765     getEl : function(){
59766         return this.el;
59767     },
59768
59769     /**
59770      * Hides this region.
59771      */
59772     hide : function(){
59773         if(!this.collapsed){
59774             this.el.dom.style.left = "-2000px";
59775             this.el.hide();
59776         }else{
59777             this.collapsedEl.dom.style.left = "-2000px";
59778             this.collapsedEl.hide();
59779         }
59780         this.visible = false;
59781         this.fireEvent("visibilitychange", this, false);
59782     },
59783
59784     /**
59785      * Shows this region if it was previously hidden.
59786      */
59787     show : function(){
59788         if(!this.collapsed){
59789             this.el.show();
59790         }else{
59791             this.collapsedEl.show();
59792         }
59793         this.visible = true;
59794         this.fireEvent("visibilitychange", this, true);
59795     },
59796
59797     closeClicked : function(){
59798         if(this.activePanel){
59799             this.remove(this.activePanel);
59800         }
59801     },
59802
59803     collapseClick : function(e){
59804         if(this.isSlid){
59805            e.stopPropagation();
59806            this.slideIn();
59807         }else{
59808            e.stopPropagation();
59809            this.slideOut();
59810         }
59811     },
59812
59813     /**
59814      * Collapses this region.
59815      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
59816      */
59817     collapse : function(skipAnim, skipCheck){
59818         if(this.collapsed) {
59819             return;
59820         }
59821         
59822         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
59823             
59824             this.collapsed = true;
59825             if(this.split){
59826                 this.split.el.hide();
59827             }
59828             if(this.config.animate && skipAnim !== true){
59829                 this.fireEvent("invalidated", this);
59830                 this.animateCollapse();
59831             }else{
59832                 this.el.setLocation(-20000,-20000);
59833                 this.el.hide();
59834                 this.collapsedEl.show();
59835                 this.fireEvent("collapsed", this);
59836                 this.fireEvent("invalidated", this);
59837             }
59838         }
59839         
59840     },
59841
59842     animateCollapse : function(){
59843         // overridden
59844     },
59845
59846     /**
59847      * Expands this region if it was previously collapsed.
59848      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
59849      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
59850      */
59851     expand : function(e, skipAnim){
59852         if(e) {
59853             e.stopPropagation();
59854         }
59855         if(!this.collapsed || this.el.hasActiveFx()) {
59856             return;
59857         }
59858         if(this.isSlid){
59859             this.afterSlideIn();
59860             skipAnim = true;
59861         }
59862         this.collapsed = false;
59863         if(this.config.animate && skipAnim !== true){
59864             this.animateExpand();
59865         }else{
59866             this.el.show();
59867             if(this.split){
59868                 this.split.el.show();
59869             }
59870             this.collapsedEl.setLocation(-2000,-2000);
59871             this.collapsedEl.hide();
59872             this.fireEvent("invalidated", this);
59873             this.fireEvent("expanded", this);
59874         }
59875     },
59876
59877     animateExpand : function(){
59878         // overridden
59879     },
59880
59881     initTabs : function()
59882     {
59883         this.bodyEl.setStyle("overflow", "hidden");
59884         var ts = new Roo.TabPanel(
59885                 this.bodyEl.dom,
59886                 {
59887                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
59888                     disableTooltips: this.config.disableTabTips,
59889                     toolbar : this.config.toolbar
59890                 }
59891         );
59892         if(this.config.hideTabs){
59893             ts.stripWrap.setDisplayed(false);
59894         }
59895         this.tabs = ts;
59896         ts.resizeTabs = this.config.resizeTabs === true;
59897         ts.minTabWidth = this.config.minTabWidth || 40;
59898         ts.maxTabWidth = this.config.maxTabWidth || 250;
59899         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
59900         ts.monitorResize = false;
59901         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
59902         ts.bodyEl.addClass('x-layout-tabs-body');
59903         this.panels.each(this.initPanelAsTab, this);
59904     },
59905
59906     initPanelAsTab : function(panel){
59907         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
59908                     this.config.closeOnTab && panel.isClosable());
59909         if(panel.tabTip !== undefined){
59910             ti.setTooltip(panel.tabTip);
59911         }
59912         ti.on("activate", function(){
59913               this.setActivePanel(panel);
59914         }, this);
59915         if(this.config.closeOnTab){
59916             ti.on("beforeclose", function(t, e){
59917                 e.cancel = true;
59918                 this.remove(panel);
59919             }, this);
59920         }
59921         return ti;
59922     },
59923
59924     updatePanelTitle : function(panel, title){
59925         if(this.activePanel == panel){
59926             this.updateTitle(title);
59927         }
59928         if(this.tabs){
59929             var ti = this.tabs.getTab(panel.getEl().id);
59930             ti.setText(title);
59931             if(panel.tabTip !== undefined){
59932                 ti.setTooltip(panel.tabTip);
59933             }
59934         }
59935     },
59936
59937     updateTitle : function(title){
59938         if(this.titleTextEl && !this.config.title){
59939             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
59940         }
59941     },
59942
59943     setActivePanel : function(panel){
59944         panel = this.getPanel(panel);
59945         if(this.activePanel && this.activePanel != panel){
59946             this.activePanel.setActiveState(false);
59947         }
59948         this.activePanel = panel;
59949         panel.setActiveState(true);
59950         if(this.panelSize){
59951             panel.setSize(this.panelSize.width, this.panelSize.height);
59952         }
59953         if(this.closeBtn){
59954             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
59955         }
59956         this.updateTitle(panel.getTitle());
59957         if(this.tabs){
59958             this.fireEvent("invalidated", this);
59959         }
59960         this.fireEvent("panelactivated", this, panel);
59961     },
59962
59963     /**
59964      * Shows the specified panel.
59965      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
59966      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
59967      */
59968     showPanel : function(panel)
59969     {
59970         panel = this.getPanel(panel);
59971         if(panel){
59972             if(this.tabs){
59973                 var tab = this.tabs.getTab(panel.getEl().id);
59974                 if(tab.isHidden()){
59975                     this.tabs.unhideTab(tab.id);
59976                 }
59977                 tab.activate();
59978             }else{
59979                 this.setActivePanel(panel);
59980             }
59981         }
59982         return panel;
59983     },
59984
59985     /**
59986      * Get the active panel for this region.
59987      * @return {Roo.ContentPanel} The active panel or null
59988      */
59989     getActivePanel : function(){
59990         return this.activePanel;
59991     },
59992
59993     validateVisibility : function(){
59994         if(this.panels.getCount() < 1){
59995             this.updateTitle("&#160;");
59996             this.closeBtn.hide();
59997             this.hide();
59998         }else{
59999             if(!this.isVisible()){
60000                 this.show();
60001             }
60002         }
60003     },
60004
60005     /**
60006      * Adds the passed ContentPanel(s) to this region.
60007      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
60008      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
60009      */
60010     add : function(panel){
60011         if(arguments.length > 1){
60012             for(var i = 0, len = arguments.length; i < len; i++) {
60013                 this.add(arguments[i]);
60014             }
60015             return null;
60016         }
60017         if(this.hasPanel(panel)){
60018             this.showPanel(panel);
60019             return panel;
60020         }
60021         panel.setRegion(this);
60022         this.panels.add(panel);
60023         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
60024             this.bodyEl.dom.appendChild(panel.getEl().dom);
60025             if(panel.background !== true){
60026                 this.setActivePanel(panel);
60027             }
60028             this.fireEvent("paneladded", this, panel);
60029             return panel;
60030         }
60031         if(!this.tabs){
60032             this.initTabs();
60033         }else{
60034             this.initPanelAsTab(panel);
60035         }
60036         if(panel.background !== true){
60037             this.tabs.activate(panel.getEl().id);
60038         }
60039         this.fireEvent("paneladded", this, panel);
60040         return panel;
60041     },
60042
60043     /**
60044      * Hides the tab for the specified panel.
60045      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
60046      */
60047     hidePanel : function(panel){
60048         if(this.tabs && (panel = this.getPanel(panel))){
60049             this.tabs.hideTab(panel.getEl().id);
60050         }
60051     },
60052
60053     /**
60054      * Unhides the tab for a previously hidden panel.
60055      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
60056      */
60057     unhidePanel : function(panel){
60058         if(this.tabs && (panel = this.getPanel(panel))){
60059             this.tabs.unhideTab(panel.getEl().id);
60060         }
60061     },
60062
60063     clearPanels : function(){
60064         while(this.panels.getCount() > 0){
60065              this.remove(this.panels.first());
60066         }
60067     },
60068
60069     /**
60070      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
60071      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
60072      * @param {Boolean} preservePanel Overrides the config preservePanel option
60073      * @return {Roo.ContentPanel} The panel that was removed
60074      */
60075     remove : function(panel, preservePanel){
60076         panel = this.getPanel(panel);
60077         if(!panel){
60078             return null;
60079         }
60080         var e = {};
60081         this.fireEvent("beforeremove", this, panel, e);
60082         if(e.cancel === true){
60083             return null;
60084         }
60085         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
60086         var panelId = panel.getId();
60087         this.panels.removeKey(panelId);
60088         if(preservePanel){
60089             document.body.appendChild(panel.getEl().dom);
60090         }
60091         if(this.tabs){
60092             this.tabs.removeTab(panel.getEl().id);
60093         }else if (!preservePanel){
60094             this.bodyEl.dom.removeChild(panel.getEl().dom);
60095         }
60096         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
60097             var p = this.panels.first();
60098             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
60099             tempEl.appendChild(p.getEl().dom);
60100             this.bodyEl.update("");
60101             this.bodyEl.dom.appendChild(p.getEl().dom);
60102             tempEl = null;
60103             this.updateTitle(p.getTitle());
60104             this.tabs = null;
60105             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
60106             this.setActivePanel(p);
60107         }
60108         panel.setRegion(null);
60109         if(this.activePanel == panel){
60110             this.activePanel = null;
60111         }
60112         if(this.config.autoDestroy !== false && preservePanel !== true){
60113             try{panel.destroy();}catch(e){}
60114         }
60115         this.fireEvent("panelremoved", this, panel);
60116         return panel;
60117     },
60118
60119     /**
60120      * Returns the TabPanel component used by this region
60121      * @return {Roo.TabPanel}
60122      */
60123     getTabs : function(){
60124         return this.tabs;
60125     },
60126
60127     createTool : function(parentEl, className){
60128         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
60129             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
60130         btn.addClassOnOver("x-layout-tools-button-over");
60131         return btn;
60132     }
60133 });/*
60134  * Based on:
60135  * Ext JS Library 1.1.1
60136  * Copyright(c) 2006-2007, Ext JS, LLC.
60137  *
60138  * Originally Released Under LGPL - original licence link has changed is not relivant.
60139  *
60140  * Fork - LGPL
60141  * <script type="text/javascript">
60142  */
60143  
60144
60145
60146 /**
60147  * @class Roo.SplitLayoutRegion
60148  * @extends Roo.LayoutRegion
60149  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
60150  */
60151 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
60152     this.cursor = cursor;
60153     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
60154 };
60155
60156 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
60157     splitTip : "Drag to resize.",
60158     collapsibleSplitTip : "Drag to resize. Double click to hide.",
60159     useSplitTips : false,
60160
60161     applyConfig : function(config){
60162         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
60163         if(config.split){
60164             if(!this.split){
60165                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
60166                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
60167                 /** The SplitBar for this region 
60168                 * @type Roo.SplitBar */
60169                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
60170                 this.split.on("moved", this.onSplitMove, this);
60171                 this.split.useShim = config.useShim === true;
60172                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
60173                 if(this.useSplitTips){
60174                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
60175                 }
60176                 if(config.collapsible){
60177                     this.split.el.on("dblclick", this.collapse,  this);
60178                 }
60179             }
60180             if(typeof config.minSize != "undefined"){
60181                 this.split.minSize = config.minSize;
60182             }
60183             if(typeof config.maxSize != "undefined"){
60184                 this.split.maxSize = config.maxSize;
60185             }
60186             if(config.hideWhenEmpty || config.hidden || config.collapsed){
60187                 this.hideSplitter();
60188             }
60189         }
60190     },
60191
60192     getHMaxSize : function(){
60193          var cmax = this.config.maxSize || 10000;
60194          var center = this.mgr.getRegion("center");
60195          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
60196     },
60197
60198     getVMaxSize : function(){
60199          var cmax = this.config.maxSize || 10000;
60200          var center = this.mgr.getRegion("center");
60201          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
60202     },
60203
60204     onSplitMove : function(split, newSize){
60205         this.fireEvent("resized", this, newSize);
60206     },
60207     
60208     /** 
60209      * Returns the {@link Roo.SplitBar} for this region.
60210      * @return {Roo.SplitBar}
60211      */
60212     getSplitBar : function(){
60213         return this.split;
60214     },
60215     
60216     hide : function(){
60217         this.hideSplitter();
60218         Roo.SplitLayoutRegion.superclass.hide.call(this);
60219     },
60220
60221     hideSplitter : function(){
60222         if(this.split){
60223             this.split.el.setLocation(-2000,-2000);
60224             this.split.el.hide();
60225         }
60226     },
60227
60228     show : function(){
60229         if(this.split){
60230             this.split.el.show();
60231         }
60232         Roo.SplitLayoutRegion.superclass.show.call(this);
60233     },
60234     
60235     beforeSlide: function(){
60236         if(Roo.isGecko){// firefox overflow auto bug workaround
60237             this.bodyEl.clip();
60238             if(this.tabs) {
60239                 this.tabs.bodyEl.clip();
60240             }
60241             if(this.activePanel){
60242                 this.activePanel.getEl().clip();
60243                 
60244                 if(this.activePanel.beforeSlide){
60245                     this.activePanel.beforeSlide();
60246                 }
60247             }
60248         }
60249     },
60250     
60251     afterSlide : function(){
60252         if(Roo.isGecko){// firefox overflow auto bug workaround
60253             this.bodyEl.unclip();
60254             if(this.tabs) {
60255                 this.tabs.bodyEl.unclip();
60256             }
60257             if(this.activePanel){
60258                 this.activePanel.getEl().unclip();
60259                 if(this.activePanel.afterSlide){
60260                     this.activePanel.afterSlide();
60261                 }
60262             }
60263         }
60264     },
60265
60266     initAutoHide : function(){
60267         if(this.autoHide !== false){
60268             if(!this.autoHideHd){
60269                 var st = new Roo.util.DelayedTask(this.slideIn, this);
60270                 this.autoHideHd = {
60271                     "mouseout": function(e){
60272                         if(!e.within(this.el, true)){
60273                             st.delay(500);
60274                         }
60275                     },
60276                     "mouseover" : function(e){
60277                         st.cancel();
60278                     },
60279                     scope : this
60280                 };
60281             }
60282             this.el.on(this.autoHideHd);
60283         }
60284     },
60285
60286     clearAutoHide : function(){
60287         if(this.autoHide !== false){
60288             this.el.un("mouseout", this.autoHideHd.mouseout);
60289             this.el.un("mouseover", this.autoHideHd.mouseover);
60290         }
60291     },
60292
60293     clearMonitor : function(){
60294         Roo.get(document).un("click", this.slideInIf, this);
60295     },
60296
60297     // these names are backwards but not changed for compat
60298     slideOut : function(){
60299         if(this.isSlid || this.el.hasActiveFx()){
60300             return;
60301         }
60302         this.isSlid = true;
60303         if(this.collapseBtn){
60304             this.collapseBtn.hide();
60305         }
60306         this.closeBtnState = this.closeBtn.getStyle('display');
60307         this.closeBtn.hide();
60308         if(this.stickBtn){
60309             this.stickBtn.show();
60310         }
60311         this.el.show();
60312         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
60313         this.beforeSlide();
60314         this.el.setStyle("z-index", 10001);
60315         this.el.slideIn(this.getSlideAnchor(), {
60316             callback: function(){
60317                 this.afterSlide();
60318                 this.initAutoHide();
60319                 Roo.get(document).on("click", this.slideInIf, this);
60320                 this.fireEvent("slideshow", this);
60321             },
60322             scope: this,
60323             block: true
60324         });
60325     },
60326
60327     afterSlideIn : function(){
60328         this.clearAutoHide();
60329         this.isSlid = false;
60330         this.clearMonitor();
60331         this.el.setStyle("z-index", "");
60332         if(this.collapseBtn){
60333             this.collapseBtn.show();
60334         }
60335         this.closeBtn.setStyle('display', this.closeBtnState);
60336         if(this.stickBtn){
60337             this.stickBtn.hide();
60338         }
60339         this.fireEvent("slidehide", this);
60340     },
60341
60342     slideIn : function(cb){
60343         if(!this.isSlid || this.el.hasActiveFx()){
60344             Roo.callback(cb);
60345             return;
60346         }
60347         this.isSlid = false;
60348         this.beforeSlide();
60349         this.el.slideOut(this.getSlideAnchor(), {
60350             callback: function(){
60351                 this.el.setLeftTop(-10000, -10000);
60352                 this.afterSlide();
60353                 this.afterSlideIn();
60354                 Roo.callback(cb);
60355             },
60356             scope: this,
60357             block: true
60358         });
60359     },
60360     
60361     slideInIf : function(e){
60362         if(!e.within(this.el)){
60363             this.slideIn();
60364         }
60365     },
60366
60367     animateCollapse : function(){
60368         this.beforeSlide();
60369         this.el.setStyle("z-index", 20000);
60370         var anchor = this.getSlideAnchor();
60371         this.el.slideOut(anchor, {
60372             callback : function(){
60373                 this.el.setStyle("z-index", "");
60374                 this.collapsedEl.slideIn(anchor, {duration:.3});
60375                 this.afterSlide();
60376                 this.el.setLocation(-10000,-10000);
60377                 this.el.hide();
60378                 this.fireEvent("collapsed", this);
60379             },
60380             scope: this,
60381             block: true
60382         });
60383     },
60384
60385     animateExpand : function(){
60386         this.beforeSlide();
60387         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
60388         this.el.setStyle("z-index", 20000);
60389         this.collapsedEl.hide({
60390             duration:.1
60391         });
60392         this.el.slideIn(this.getSlideAnchor(), {
60393             callback : function(){
60394                 this.el.setStyle("z-index", "");
60395                 this.afterSlide();
60396                 if(this.split){
60397                     this.split.el.show();
60398                 }
60399                 this.fireEvent("invalidated", this);
60400                 this.fireEvent("expanded", this);
60401             },
60402             scope: this,
60403             block: true
60404         });
60405     },
60406
60407     anchors : {
60408         "west" : "left",
60409         "east" : "right",
60410         "north" : "top",
60411         "south" : "bottom"
60412     },
60413
60414     sanchors : {
60415         "west" : "l",
60416         "east" : "r",
60417         "north" : "t",
60418         "south" : "b"
60419     },
60420
60421     canchors : {
60422         "west" : "tl-tr",
60423         "east" : "tr-tl",
60424         "north" : "tl-bl",
60425         "south" : "bl-tl"
60426     },
60427
60428     getAnchor : function(){
60429         return this.anchors[this.position];
60430     },
60431
60432     getCollapseAnchor : function(){
60433         return this.canchors[this.position];
60434     },
60435
60436     getSlideAnchor : function(){
60437         return this.sanchors[this.position];
60438     },
60439
60440     getAlignAdj : function(){
60441         var cm = this.cmargins;
60442         switch(this.position){
60443             case "west":
60444                 return [0, 0];
60445             break;
60446             case "east":
60447                 return [0, 0];
60448             break;
60449             case "north":
60450                 return [0, 0];
60451             break;
60452             case "south":
60453                 return [0, 0];
60454             break;
60455         }
60456     },
60457
60458     getExpandAdj : function(){
60459         var c = this.collapsedEl, cm = this.cmargins;
60460         switch(this.position){
60461             case "west":
60462                 return [-(cm.right+c.getWidth()+cm.left), 0];
60463             break;
60464             case "east":
60465                 return [cm.right+c.getWidth()+cm.left, 0];
60466             break;
60467             case "north":
60468                 return [0, -(cm.top+cm.bottom+c.getHeight())];
60469             break;
60470             case "south":
60471                 return [0, cm.top+cm.bottom+c.getHeight()];
60472             break;
60473         }
60474     }
60475 });/*
60476  * Based on:
60477  * Ext JS Library 1.1.1
60478  * Copyright(c) 2006-2007, Ext JS, LLC.
60479  *
60480  * Originally Released Under LGPL - original licence link has changed is not relivant.
60481  *
60482  * Fork - LGPL
60483  * <script type="text/javascript">
60484  */
60485 /*
60486  * These classes are private internal classes
60487  */
60488 Roo.CenterLayoutRegion = function(mgr, config){
60489     Roo.LayoutRegion.call(this, mgr, config, "center");
60490     this.visible = true;
60491     this.minWidth = config.minWidth || 20;
60492     this.minHeight = config.minHeight || 20;
60493 };
60494
60495 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
60496     hide : function(){
60497         // center panel can't be hidden
60498     },
60499     
60500     show : function(){
60501         // center panel can't be hidden
60502     },
60503     
60504     getMinWidth: function(){
60505         return this.minWidth;
60506     },
60507     
60508     getMinHeight: function(){
60509         return this.minHeight;
60510     }
60511 });
60512
60513
60514 Roo.NorthLayoutRegion = function(mgr, config){
60515     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
60516     if(this.split){
60517         this.split.placement = Roo.SplitBar.TOP;
60518         this.split.orientation = Roo.SplitBar.VERTICAL;
60519         this.split.el.addClass("x-layout-split-v");
60520     }
60521     var size = config.initialSize || config.height;
60522     if(typeof size != "undefined"){
60523         this.el.setHeight(size);
60524     }
60525 };
60526 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
60527     orientation: Roo.SplitBar.VERTICAL,
60528     getBox : function(){
60529         if(this.collapsed){
60530             return this.collapsedEl.getBox();
60531         }
60532         var box = this.el.getBox();
60533         if(this.split){
60534             box.height += this.split.el.getHeight();
60535         }
60536         return box;
60537     },
60538     
60539     updateBox : function(box){
60540         if(this.split && !this.collapsed){
60541             box.height -= this.split.el.getHeight();
60542             this.split.el.setLeft(box.x);
60543             this.split.el.setTop(box.y+box.height);
60544             this.split.el.setWidth(box.width);
60545         }
60546         if(this.collapsed){
60547             this.updateBody(box.width, null);
60548         }
60549         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60550     }
60551 });
60552
60553 Roo.SouthLayoutRegion = function(mgr, config){
60554     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
60555     if(this.split){
60556         this.split.placement = Roo.SplitBar.BOTTOM;
60557         this.split.orientation = Roo.SplitBar.VERTICAL;
60558         this.split.el.addClass("x-layout-split-v");
60559     }
60560     var size = config.initialSize || config.height;
60561     if(typeof size != "undefined"){
60562         this.el.setHeight(size);
60563     }
60564 };
60565 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
60566     orientation: Roo.SplitBar.VERTICAL,
60567     getBox : function(){
60568         if(this.collapsed){
60569             return this.collapsedEl.getBox();
60570         }
60571         var box = this.el.getBox();
60572         if(this.split){
60573             var sh = this.split.el.getHeight();
60574             box.height += sh;
60575             box.y -= sh;
60576         }
60577         return box;
60578     },
60579     
60580     updateBox : function(box){
60581         if(this.split && !this.collapsed){
60582             var sh = this.split.el.getHeight();
60583             box.height -= sh;
60584             box.y += sh;
60585             this.split.el.setLeft(box.x);
60586             this.split.el.setTop(box.y-sh);
60587             this.split.el.setWidth(box.width);
60588         }
60589         if(this.collapsed){
60590             this.updateBody(box.width, null);
60591         }
60592         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60593     }
60594 });
60595
60596 Roo.EastLayoutRegion = function(mgr, config){
60597     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
60598     if(this.split){
60599         this.split.placement = Roo.SplitBar.RIGHT;
60600         this.split.orientation = Roo.SplitBar.HORIZONTAL;
60601         this.split.el.addClass("x-layout-split-h");
60602     }
60603     var size = config.initialSize || config.width;
60604     if(typeof size != "undefined"){
60605         this.el.setWidth(size);
60606     }
60607 };
60608 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
60609     orientation: Roo.SplitBar.HORIZONTAL,
60610     getBox : function(){
60611         if(this.collapsed){
60612             return this.collapsedEl.getBox();
60613         }
60614         var box = this.el.getBox();
60615         if(this.split){
60616             var sw = this.split.el.getWidth();
60617             box.width += sw;
60618             box.x -= sw;
60619         }
60620         return box;
60621     },
60622
60623     updateBox : function(box){
60624         if(this.split && !this.collapsed){
60625             var sw = this.split.el.getWidth();
60626             box.width -= sw;
60627             this.split.el.setLeft(box.x);
60628             this.split.el.setTop(box.y);
60629             this.split.el.setHeight(box.height);
60630             box.x += sw;
60631         }
60632         if(this.collapsed){
60633             this.updateBody(null, box.height);
60634         }
60635         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60636     }
60637 });
60638
60639 Roo.WestLayoutRegion = function(mgr, config){
60640     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
60641     if(this.split){
60642         this.split.placement = Roo.SplitBar.LEFT;
60643         this.split.orientation = Roo.SplitBar.HORIZONTAL;
60644         this.split.el.addClass("x-layout-split-h");
60645     }
60646     var size = config.initialSize || config.width;
60647     if(typeof size != "undefined"){
60648         this.el.setWidth(size);
60649     }
60650 };
60651 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
60652     orientation: Roo.SplitBar.HORIZONTAL,
60653     getBox : function(){
60654         if(this.collapsed){
60655             return this.collapsedEl.getBox();
60656         }
60657         var box = this.el.getBox();
60658         if(this.split){
60659             box.width += this.split.el.getWidth();
60660         }
60661         return box;
60662     },
60663     
60664     updateBox : function(box){
60665         if(this.split && !this.collapsed){
60666             var sw = this.split.el.getWidth();
60667             box.width -= sw;
60668             this.split.el.setLeft(box.x+box.width);
60669             this.split.el.setTop(box.y);
60670             this.split.el.setHeight(box.height);
60671         }
60672         if(this.collapsed){
60673             this.updateBody(null, box.height);
60674         }
60675         Roo.LayoutRegion.prototype.updateBox.call(this, box);
60676     }
60677 });
60678 /*
60679  * Based on:
60680  * Ext JS Library 1.1.1
60681  * Copyright(c) 2006-2007, Ext JS, LLC.
60682  *
60683  * Originally Released Under LGPL - original licence link has changed is not relivant.
60684  *
60685  * Fork - LGPL
60686  * <script type="text/javascript">
60687  */
60688  
60689  
60690 /*
60691  * Private internal class for reading and applying state
60692  */
60693 Roo.LayoutStateManager = function(layout){
60694      // default empty state
60695      this.state = {
60696         north: {},
60697         south: {},
60698         east: {},
60699         west: {}       
60700     };
60701 };
60702
60703 Roo.LayoutStateManager.prototype = {
60704     init : function(layout, provider){
60705         this.provider = provider;
60706         var state = provider.get(layout.id+"-layout-state");
60707         if(state){
60708             var wasUpdating = layout.isUpdating();
60709             if(!wasUpdating){
60710                 layout.beginUpdate();
60711             }
60712             for(var key in state){
60713                 if(typeof state[key] != "function"){
60714                     var rstate = state[key];
60715                     var r = layout.getRegion(key);
60716                     if(r && rstate){
60717                         if(rstate.size){
60718                             r.resizeTo(rstate.size);
60719                         }
60720                         if(rstate.collapsed == true){
60721                             r.collapse(true);
60722                         }else{
60723                             r.expand(null, true);
60724                         }
60725                     }
60726                 }
60727             }
60728             if(!wasUpdating){
60729                 layout.endUpdate();
60730             }
60731             this.state = state; 
60732         }
60733         this.layout = layout;
60734         layout.on("regionresized", this.onRegionResized, this);
60735         layout.on("regioncollapsed", this.onRegionCollapsed, this);
60736         layout.on("regionexpanded", this.onRegionExpanded, this);
60737     },
60738     
60739     storeState : function(){
60740         this.provider.set(this.layout.id+"-layout-state", this.state);
60741     },
60742     
60743     onRegionResized : function(region, newSize){
60744         this.state[region.getPosition()].size = newSize;
60745         this.storeState();
60746     },
60747     
60748     onRegionCollapsed : function(region){
60749         this.state[region.getPosition()].collapsed = true;
60750         this.storeState();
60751     },
60752     
60753     onRegionExpanded : function(region){
60754         this.state[region.getPosition()].collapsed = false;
60755         this.storeState();
60756     }
60757 };/*
60758  * Based on:
60759  * Ext JS Library 1.1.1
60760  * Copyright(c) 2006-2007, Ext JS, LLC.
60761  *
60762  * Originally Released Under LGPL - original licence link has changed is not relivant.
60763  *
60764  * Fork - LGPL
60765  * <script type="text/javascript">
60766  */
60767 /**
60768  * @class Roo.ContentPanel
60769  * @extends Roo.util.Observable
60770  * @children Roo.form.Form Roo.JsonView Roo.View
60771  * @parent Roo.BorderLayout Roo.LayoutDialog builder
60772  * A basic ContentPanel element.
60773  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
60774  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
60775  * @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
60776  * @cfg {Boolean}   closable      True if the panel can be closed/removed
60777  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
60778  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
60779  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
60780  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
60781  * @cfg {String} title          The title for this panel
60782  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
60783  * @cfg {String} url            Calls {@link #setUrl} with this value
60784  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
60785  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
60786  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
60787  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
60788  * @cfg {String}    style  Extra style to add to the content panel
60789  * @cfg {Roo.menu.Menu} menu  popup menu
60790
60791  * @constructor
60792  * Create a new ContentPanel.
60793  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
60794  * @param {String/Object} config A string to set only the title or a config object
60795  * @param {String} content (optional) Set the HTML content for this panel
60796  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
60797  */
60798 Roo.ContentPanel = function(el, config, content){
60799     
60800     /*
60801     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
60802         config = el;
60803         el = Roo.id();
60804     }
60805     if (config && config.parentLayout) { 
60806         el = config.parentLayout.el.createChild(); 
60807     }
60808     */
60809     if(el.autoCreate){ // xtype is available if this is called from factory
60810         config = el;
60811         el = Roo.id();
60812     }
60813     this.el = Roo.get(el);
60814     if(!this.el && config && config.autoCreate){
60815         if(typeof config.autoCreate == "object"){
60816             if(!config.autoCreate.id){
60817                 config.autoCreate.id = config.id||el;
60818             }
60819             this.el = Roo.DomHelper.append(document.body,
60820                         config.autoCreate, true);
60821         }else{
60822             this.el = Roo.DomHelper.append(document.body,
60823                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
60824         }
60825     }
60826     
60827     
60828     this.closable = false;
60829     this.loaded = false;
60830     this.active = false;
60831     if(typeof config == "string"){
60832         this.title = config;
60833     }else{
60834         Roo.apply(this, config);
60835     }
60836     
60837     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
60838         this.wrapEl = this.el.wrap();
60839         this.toolbar.container = this.el.insertSibling(false, 'before');
60840         this.toolbar = new Roo.Toolbar(this.toolbar);
60841     }
60842     
60843     // xtype created footer. - not sure if will work as we normally have to render first..
60844     if (this.footer && !this.footer.el && this.footer.xtype) {
60845         if (!this.wrapEl) {
60846             this.wrapEl = this.el.wrap();
60847         }
60848     
60849         this.footer.container = this.wrapEl.createChild();
60850          
60851         this.footer = Roo.factory(this.footer, Roo);
60852         
60853     }
60854     
60855     if(this.resizeEl){
60856         this.resizeEl = Roo.get(this.resizeEl, true);
60857     }else{
60858         this.resizeEl = this.el;
60859     }
60860     // handle view.xtype
60861     
60862  
60863     
60864     
60865     this.addEvents({
60866         /**
60867          * @event activate
60868          * Fires when this panel is activated. 
60869          * @param {Roo.ContentPanel} this
60870          */
60871         "activate" : true,
60872         /**
60873          * @event deactivate
60874          * Fires when this panel is activated. 
60875          * @param {Roo.ContentPanel} this
60876          */
60877         "deactivate" : true,
60878
60879         /**
60880          * @event resize
60881          * Fires when this panel is resized if fitToFrame is true.
60882          * @param {Roo.ContentPanel} this
60883          * @param {Number} width The width after any component adjustments
60884          * @param {Number} height The height after any component adjustments
60885          */
60886         "resize" : true,
60887         
60888          /**
60889          * @event render
60890          * Fires when this tab is created
60891          * @param {Roo.ContentPanel} this
60892          */
60893         "render" : true
60894          
60895         
60896     });
60897     
60898
60899     
60900     
60901     if(this.autoScroll){
60902         this.resizeEl.setStyle("overflow", "auto");
60903     } else {
60904         // fix randome scrolling
60905         this.el.on('scroll', function() {
60906             Roo.log('fix random scolling');
60907             this.scrollTo('top',0); 
60908         });
60909     }
60910     content = content || this.content;
60911     if(content){
60912         this.setContent(content);
60913     }
60914     if(config && config.url){
60915         this.setUrl(this.url, this.params, this.loadOnce);
60916     }
60917     
60918     
60919     
60920     Roo.ContentPanel.superclass.constructor.call(this);
60921     
60922     if (this.view && typeof(this.view.xtype) != 'undefined') {
60923         this.view.el = this.el.appendChild(document.createElement("div"));
60924         this.view = Roo.factory(this.view); 
60925         this.view.render  &&  this.view.render(false, '');  
60926     }
60927     
60928     
60929     this.fireEvent('render', this);
60930 };
60931
60932 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
60933     tabTip:'',
60934     setRegion : function(region){
60935         this.region = region;
60936         if(region){
60937            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
60938         }else{
60939            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
60940         } 
60941     },
60942     
60943     /**
60944      * Returns the toolbar for this Panel if one was configured. 
60945      * @return {Roo.Toolbar} 
60946      */
60947     getToolbar : function(){
60948         return this.toolbar;
60949     },
60950     
60951     setActiveState : function(active){
60952         this.active = active;
60953         if(!active){
60954             this.fireEvent("deactivate", this);
60955         }else{
60956             this.fireEvent("activate", this);
60957         }
60958     },
60959     /**
60960      * Updates this panel's element
60961      * @param {String} content The new content
60962      * @param {Boolean} loadScripts (optional) true to look for and process scripts
60963     */
60964     setContent : function(content, loadScripts){
60965         this.el.update(content, loadScripts);
60966     },
60967
60968     ignoreResize : function(w, h){
60969         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
60970             return true;
60971         }else{
60972             this.lastSize = {width: w, height: h};
60973             return false;
60974         }
60975     },
60976     /**
60977      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
60978      * @return {Roo.UpdateManager} The UpdateManager
60979      */
60980     getUpdateManager : function(){
60981         return this.el.getUpdateManager();
60982     },
60983      /**
60984      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
60985      * @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:
60986 <pre><code>
60987 panel.load({
60988     url: "your-url.php",
60989     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
60990     callback: yourFunction,
60991     scope: yourObject, //(optional scope)
60992     discardUrl: false,
60993     nocache: false,
60994     text: "Loading...",
60995     timeout: 30,
60996     scripts: false
60997 });
60998 </code></pre>
60999      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
61000      * 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.
61001      * @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}
61002      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
61003      * @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.
61004      * @return {Roo.ContentPanel} this
61005      */
61006     load : function(){
61007         var um = this.el.getUpdateManager();
61008         um.update.apply(um, arguments);
61009         return this;
61010     },
61011
61012
61013     /**
61014      * 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.
61015      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
61016      * @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)
61017      * @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)
61018      * @return {Roo.UpdateManager} The UpdateManager
61019      */
61020     setUrl : function(url, params, loadOnce){
61021         if(this.refreshDelegate){
61022             this.removeListener("activate", this.refreshDelegate);
61023         }
61024         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
61025         this.on("activate", this.refreshDelegate);
61026         return this.el.getUpdateManager();
61027     },
61028     
61029     _handleRefresh : function(url, params, loadOnce){
61030         if(!loadOnce || !this.loaded){
61031             var updater = this.el.getUpdateManager();
61032             updater.update(url, params, this._setLoaded.createDelegate(this));
61033         }
61034     },
61035     
61036     _setLoaded : function(){
61037         this.loaded = true;
61038     }, 
61039     
61040     /**
61041      * Returns this panel's id
61042      * @return {String} 
61043      */
61044     getId : function(){
61045         return this.el.id;
61046     },
61047     
61048     /** 
61049      * Returns this panel's element - used by regiosn to add.
61050      * @return {Roo.Element} 
61051      */
61052     getEl : function(){
61053         return this.wrapEl || this.el;
61054     },
61055     
61056     adjustForComponents : function(width, height)
61057     {
61058         //Roo.log('adjustForComponents ');
61059         if(this.resizeEl != this.el){
61060             width -= this.el.getFrameWidth('lr');
61061             height -= this.el.getFrameWidth('tb');
61062         }
61063         if(this.toolbar){
61064             var te = this.toolbar.getEl();
61065             height -= te.getHeight();
61066             te.setWidth(width);
61067         }
61068         if(this.footer){
61069             var te = this.footer.getEl();
61070             //Roo.log("footer:" + te.getHeight());
61071             
61072             height -= te.getHeight();
61073             te.setWidth(width);
61074         }
61075         
61076         
61077         if(this.adjustments){
61078             width += this.adjustments[0];
61079             height += this.adjustments[1];
61080         }
61081         return {"width": width, "height": height};
61082     },
61083     
61084     setSize : function(width, height){
61085         if(this.fitToFrame && !this.ignoreResize(width, height)){
61086             if(this.fitContainer && this.resizeEl != this.el){
61087                 this.el.setSize(width, height);
61088             }
61089             var size = this.adjustForComponents(width, height);
61090             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
61091             this.fireEvent('resize', this, size.width, size.height);
61092         }
61093     },
61094     
61095     /**
61096      * Returns this panel's title
61097      * @return {String} 
61098      */
61099     getTitle : function(){
61100         return this.title;
61101     },
61102     
61103     /**
61104      * Set this panel's title
61105      * @param {String} title
61106      */
61107     setTitle : function(title){
61108         this.title = title;
61109         if(this.region){
61110             this.region.updatePanelTitle(this, title);
61111         }
61112     },
61113     
61114     /**
61115      * Returns true is this panel was configured to be closable
61116      * @return {Boolean} 
61117      */
61118     isClosable : function(){
61119         return this.closable;
61120     },
61121     
61122     beforeSlide : function(){
61123         this.el.clip();
61124         this.resizeEl.clip();
61125     },
61126     
61127     afterSlide : function(){
61128         this.el.unclip();
61129         this.resizeEl.unclip();
61130     },
61131     
61132     /**
61133      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
61134      *   Will fail silently if the {@link #setUrl} method has not been called.
61135      *   This does not activate the panel, just updates its content.
61136      */
61137     refresh : function(){
61138         if(this.refreshDelegate){
61139            this.loaded = false;
61140            this.refreshDelegate();
61141         }
61142     },
61143     
61144     /**
61145      * Destroys this panel
61146      */
61147     destroy : function(){
61148         this.el.removeAllListeners();
61149         var tempEl = document.createElement("span");
61150         tempEl.appendChild(this.el.dom);
61151         tempEl.innerHTML = "";
61152         this.el.remove();
61153         this.el = null;
61154     },
61155     
61156     /**
61157      * form - if the content panel contains a form - this is a reference to it.
61158      * @type {Roo.form.Form}
61159      */
61160     form : false,
61161     /**
61162      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
61163      *    This contains a reference to it.
61164      * @type {Roo.View}
61165      */
61166     view : false,
61167     
61168       /**
61169      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
61170      * <pre><code>
61171
61172 layout.addxtype({
61173        xtype : 'Form',
61174        items: [ .... ]
61175    }
61176 );
61177
61178 </code></pre>
61179      * @param {Object} cfg Xtype definition of item to add.
61180      */
61181     
61182     addxtype : function(cfg) {
61183         if(cfg.xtype.match(/^UploadCropbox$/)) {
61184
61185             this.cropbox = new Roo.factory(cfg);
61186
61187             this.cropbox.render(this.el);
61188
61189             return this.cropbox;
61190         }
61191         // add form..
61192         if (cfg.xtype.match(/^Form$/)) {
61193             
61194             var el;
61195             //if (this.footer) {
61196             //    el = this.footer.container.insertSibling(false, 'before');
61197             //} else {
61198                 el = this.el.createChild();
61199             //}
61200
61201             this.form = new  Roo.form.Form(cfg);
61202             
61203             
61204             if ( this.form.allItems.length) {
61205                 this.form.render(el.dom);
61206             }
61207             return this.form;
61208         }
61209         // should only have one of theses..
61210         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
61211             // views.. should not be just added - used named prop 'view''
61212             
61213             cfg.el = this.el.appendChild(document.createElement("div"));
61214             // factory?
61215             
61216             var ret = new Roo.factory(cfg);
61217              
61218              ret.render && ret.render(false, ''); // render blank..
61219             this.view = ret;
61220             return ret;
61221         }
61222         return false;
61223     }
61224 });
61225
61226
61227
61228
61229
61230
61231
61232
61233
61234
61235
61236
61237 /**
61238  * @class Roo.GridPanel
61239  * @extends Roo.ContentPanel
61240  * @parent Roo.BorderLayout Roo.LayoutDialog builder
61241  * @constructor
61242  * Create a new GridPanel.
61243  * @cfg {Roo.grid.Grid} grid The grid for this panel
61244  */
61245 Roo.GridPanel = function(grid, config){
61246     
61247     // universal ctor...
61248     if (typeof(grid.grid) != 'undefined') {
61249         config = grid;
61250         grid = config.grid;
61251     }
61252     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
61253         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
61254         
61255     this.wrapper.dom.appendChild(grid.getGridEl().dom);
61256     
61257     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
61258     
61259     if(this.toolbar){
61260         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
61261     }
61262     // xtype created footer. - not sure if will work as we normally have to render first..
61263     if (this.footer && !this.footer.el && this.footer.xtype) {
61264         
61265         this.footer.container = this.grid.getView().getFooterPanel(true);
61266         this.footer.dataSource = this.grid.dataSource;
61267         this.footer = Roo.factory(this.footer, Roo);
61268         
61269     }
61270     
61271     grid.monitorWindowResize = false; // turn off autosizing
61272     grid.autoHeight = false;
61273     grid.autoWidth = false;
61274     this.grid = grid;
61275     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
61276 };
61277
61278 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
61279     getId : function(){
61280         return this.grid.id;
61281     },
61282     
61283     /**
61284      * Returns the grid for this panel
61285      * @return {Roo.grid.Grid} 
61286      */
61287     getGrid : function(){
61288         return this.grid;    
61289     },
61290     
61291     setSize : function(width, height){
61292         if(!this.ignoreResize(width, height)){
61293             var grid = this.grid;
61294             var size = this.adjustForComponents(width, height);
61295             grid.getGridEl().setSize(size.width, size.height);
61296             grid.autoSize();
61297         }
61298     },
61299     
61300     beforeSlide : function(){
61301         this.grid.getView().scroller.clip();
61302     },
61303     
61304     afterSlide : function(){
61305         this.grid.getView().scroller.unclip();
61306     },
61307     
61308     destroy : function(){
61309         this.grid.destroy();
61310         delete this.grid;
61311         Roo.GridPanel.superclass.destroy.call(this); 
61312     }
61313 });
61314
61315
61316 /**
61317  * @class Roo.NestedLayoutPanel
61318  * @extends Roo.ContentPanel
61319  * @parent Roo.BorderLayout Roo.LayoutDialog builder
61320  * @cfg {Roo.BorderLayout} layout   [required] The layout for this panel
61321  *
61322  * 
61323  * @constructor
61324  * Create a new NestedLayoutPanel.
61325  * 
61326  * 
61327  * @param {Roo.BorderLayout} layout [required] The layout for this panel
61328  * @param {String/Object} config A string to set only the title or a config object
61329  */
61330 Roo.NestedLayoutPanel = function(layout, config)
61331 {
61332     // construct with only one argument..
61333     /* FIXME - implement nicer consturctors
61334     if (layout.layout) {
61335         config = layout;
61336         layout = config.layout;
61337         delete config.layout;
61338     }
61339     if (layout.xtype && !layout.getEl) {
61340         // then layout needs constructing..
61341         layout = Roo.factory(layout, Roo);
61342     }
61343     */
61344     
61345     
61346     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
61347     
61348     layout.monitorWindowResize = false; // turn off autosizing
61349     this.layout = layout;
61350     this.layout.getEl().addClass("x-layout-nested-layout");
61351     
61352     
61353     
61354     
61355 };
61356
61357 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
61358
61359     layout : false,
61360
61361     setSize : function(width, height){
61362         if(!this.ignoreResize(width, height)){
61363             var size = this.adjustForComponents(width, height);
61364             var el = this.layout.getEl();
61365             el.setSize(size.width, size.height);
61366             var touch = el.dom.offsetWidth;
61367             this.layout.layout();
61368             // ie requires a double layout on the first pass
61369             if(Roo.isIE && !this.initialized){
61370                 this.initialized = true;
61371                 this.layout.layout();
61372             }
61373         }
61374     },
61375     
61376     // activate all subpanels if not currently active..
61377     
61378     setActiveState : function(active){
61379         this.active = active;
61380         if(!active){
61381             this.fireEvent("deactivate", this);
61382             return;
61383         }
61384         
61385         this.fireEvent("activate", this);
61386         // not sure if this should happen before or after..
61387         if (!this.layout) {
61388             return; // should not happen..
61389         }
61390         var reg = false;
61391         for (var r in this.layout.regions) {
61392             reg = this.layout.getRegion(r);
61393             if (reg.getActivePanel()) {
61394                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
61395                 reg.setActivePanel(reg.getActivePanel());
61396                 continue;
61397             }
61398             if (!reg.panels.length) {
61399                 continue;
61400             }
61401             reg.showPanel(reg.getPanel(0));
61402         }
61403         
61404         
61405         
61406         
61407     },
61408     
61409     /**
61410      * Returns the nested BorderLayout for this panel
61411      * @return {Roo.BorderLayout}
61412      */
61413     getLayout : function(){
61414         return this.layout;
61415     },
61416     
61417      /**
61418      * Adds a xtype elements to the layout of the nested panel
61419      * <pre><code>
61420
61421 panel.addxtype({
61422        xtype : 'ContentPanel',
61423        region: 'west',
61424        items: [ .... ]
61425    }
61426 );
61427
61428 panel.addxtype({
61429         xtype : 'NestedLayoutPanel',
61430         region: 'west',
61431         layout: {
61432            center: { },
61433            west: { }   
61434         },
61435         items : [ ... list of content panels or nested layout panels.. ]
61436    }
61437 );
61438 </code></pre>
61439      * @param {Object} cfg Xtype definition of item to add.
61440      */
61441     addxtype : function(cfg) {
61442         return this.layout.addxtype(cfg);
61443     
61444     }
61445 });
61446
61447 Roo.ScrollPanel = function(el, config, content){
61448     config = config || {};
61449     config.fitToFrame = true;
61450     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
61451     
61452     this.el.dom.style.overflow = "hidden";
61453     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
61454     this.el.removeClass("x-layout-inactive-content");
61455     this.el.on("mousewheel", this.onWheel, this);
61456
61457     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
61458     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
61459     up.unselectable(); down.unselectable();
61460     up.on("click", this.scrollUp, this);
61461     down.on("click", this.scrollDown, this);
61462     up.addClassOnOver("x-scroller-btn-over");
61463     down.addClassOnOver("x-scroller-btn-over");
61464     up.addClassOnClick("x-scroller-btn-click");
61465     down.addClassOnClick("x-scroller-btn-click");
61466     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
61467
61468     this.resizeEl = this.el;
61469     this.el = wrap; this.up = up; this.down = down;
61470 };
61471
61472 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
61473     increment : 100,
61474     wheelIncrement : 5,
61475     scrollUp : function(){
61476         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
61477     },
61478
61479     scrollDown : function(){
61480         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
61481     },
61482
61483     afterScroll : function(){
61484         var el = this.resizeEl;
61485         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
61486         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61487         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
61488     },
61489
61490     setSize : function(){
61491         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
61492         this.afterScroll();
61493     },
61494
61495     onWheel : function(e){
61496         var d = e.getWheelDelta();
61497         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
61498         this.afterScroll();
61499         e.stopEvent();
61500     },
61501
61502     setContent : function(content, loadScripts){
61503         this.resizeEl.update(content, loadScripts);
61504     }
61505
61506 });
61507
61508
61509
61510 /**
61511  * @class Roo.TreePanel
61512  * @extends Roo.ContentPanel
61513  * @parent Roo.BorderLayout Roo.LayoutDialog builder
61514  * Treepanel component
61515  * 
61516  * @constructor
61517  * Create a new TreePanel. - defaults to fit/scoll contents.
61518  * @param {String/Object} config A string to set only the panel's title, or a config object
61519  */
61520 Roo.TreePanel = function(config){
61521     var el = config.el;
61522     var tree = config.tree;
61523     delete config.tree; 
61524     delete config.el; // hopefull!
61525     
61526     // wrapper for IE7 strict & safari scroll issue
61527     
61528     var treeEl = el.createChild();
61529     config.resizeEl = treeEl;
61530     
61531     
61532     
61533     Roo.TreePanel.superclass.constructor.call(this, el, config);
61534  
61535  
61536     this.tree = new Roo.tree.TreePanel(treeEl , tree);
61537     //console.log(tree);
61538     this.on('activate', function()
61539     {
61540         if (this.tree.rendered) {
61541             return;
61542         }
61543         //console.log('render tree');
61544         this.tree.render();
61545     });
61546     // this should not be needed.. - it's actually the 'el' that resizes?
61547     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
61548     
61549     //this.on('resize',  function (cp, w, h) {
61550     //        this.tree.innerCt.setWidth(w);
61551     //        this.tree.innerCt.setHeight(h);
61552     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
61553     //});
61554
61555         
61556     
61557 };
61558
61559 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
61560     fitToFrame : true,
61561     autoScroll : true,
61562     /*
61563      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
61564      */
61565     tree : false
61566
61567 });
61568 /*
61569  * Based on:
61570  * Ext JS Library 1.1.1
61571  * Copyright(c) 2006-2007, Ext JS, LLC.
61572  *
61573  * Originally Released Under LGPL - original licence link has changed is not relivant.
61574  *
61575  * Fork - LGPL
61576  * <script type="text/javascript">
61577  */
61578  
61579
61580 /**
61581  * @class Roo.ReaderLayout
61582  * @extends Roo.BorderLayout
61583  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
61584  * center region containing two nested regions (a top one for a list view and one for item preview below),
61585  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
61586  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
61587  * expedites the setup of the overall layout and regions for this common application style.
61588  * Example:
61589  <pre><code>
61590 var reader = new Roo.ReaderLayout();
61591 var CP = Roo.ContentPanel;  // shortcut for adding
61592
61593 reader.beginUpdate();
61594 reader.add("north", new CP("north", "North"));
61595 reader.add("west", new CP("west", {title: "West"}));
61596 reader.add("east", new CP("east", {title: "East"}));
61597
61598 reader.regions.listView.add(new CP("listView", "List"));
61599 reader.regions.preview.add(new CP("preview", "Preview"));
61600 reader.endUpdate();
61601 </code></pre>
61602 * @constructor
61603 * Create a new ReaderLayout
61604 * @param {Object} config Configuration options
61605 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
61606 * document.body if omitted)
61607 */
61608 Roo.ReaderLayout = function(config, renderTo){
61609     var c = config || {size:{}};
61610     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
61611         north: c.north !== false ? Roo.apply({
61612             split:false,
61613             initialSize: 32,
61614             titlebar: false
61615         }, c.north) : false,
61616         west: c.west !== false ? Roo.apply({
61617             split:true,
61618             initialSize: 200,
61619             minSize: 175,
61620             maxSize: 400,
61621             titlebar: true,
61622             collapsible: true,
61623             animate: true,
61624             margins:{left:5,right:0,bottom:5,top:5},
61625             cmargins:{left:5,right:5,bottom:5,top:5}
61626         }, c.west) : false,
61627         east: c.east !== false ? Roo.apply({
61628             split:true,
61629             initialSize: 200,
61630             minSize: 175,
61631             maxSize: 400,
61632             titlebar: true,
61633             collapsible: true,
61634             animate: true,
61635             margins:{left:0,right:5,bottom:5,top:5},
61636             cmargins:{left:5,right:5,bottom:5,top:5}
61637         }, c.east) : false,
61638         center: Roo.apply({
61639             tabPosition: 'top',
61640             autoScroll:false,
61641             closeOnTab: true,
61642             titlebar:false,
61643             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
61644         }, c.center)
61645     });
61646
61647     this.el.addClass('x-reader');
61648
61649     this.beginUpdate();
61650
61651     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
61652         south: c.preview !== false ? Roo.apply({
61653             split:true,
61654             initialSize: 200,
61655             minSize: 100,
61656             autoScroll:true,
61657             collapsible:true,
61658             titlebar: true,
61659             cmargins:{top:5,left:0, right:0, bottom:0}
61660         }, c.preview) : false,
61661         center: Roo.apply({
61662             autoScroll:false,
61663             titlebar:false,
61664             minHeight:200
61665         }, c.listView)
61666     });
61667     this.add('center', new Roo.NestedLayoutPanel(inner,
61668             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
61669
61670     this.endUpdate();
61671
61672     this.regions.preview = inner.getRegion('south');
61673     this.regions.listView = inner.getRegion('center');
61674 };
61675
61676 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
61677  * Based on:
61678  * Ext JS Library 1.1.1
61679  * Copyright(c) 2006-2007, Ext JS, LLC.
61680  *
61681  * Originally Released Under LGPL - original licence link has changed is not relivant.
61682  *
61683  * Fork - LGPL
61684  * <script type="text/javascript">
61685  */
61686  
61687 /**
61688  * @class Roo.grid.Grid
61689  * @extends Roo.util.Observable
61690  * This class represents the primary interface of a component based grid control.
61691  * <br><br>Usage:<pre><code>
61692  var grid = new Roo.grid.Grid("my-container-id", {
61693      ds: myDataStore,
61694      cm: myColModel,
61695      selModel: mySelectionModel,
61696      autoSizeColumns: true,
61697      monitorWindowResize: false,
61698      trackMouseOver: true
61699  });
61700  // set any options
61701  grid.render();
61702  * </code></pre>
61703  * <b>Common Problems:</b><br/>
61704  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
61705  * element will correct this<br/>
61706  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
61707  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
61708  * are unpredictable.<br/>
61709  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
61710  * grid to calculate dimensions/offsets.<br/>
61711   * @constructor
61712  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61713  * The container MUST have some type of size defined for the grid to fill. The container will be
61714  * automatically set to position relative if it isn't already.
61715  * @param {Object} config A config object that sets properties on this grid.
61716  */
61717 Roo.grid.Grid = function(container, config){
61718         // initialize the container
61719         this.container = Roo.get(container);
61720         this.container.update("");
61721         this.container.setStyle("overflow", "hidden");
61722     this.container.addClass('x-grid-container');
61723
61724     this.id = this.container.id;
61725
61726     Roo.apply(this, config);
61727     // check and correct shorthanded configs
61728     if(this.ds){
61729         this.dataSource = this.ds;
61730         delete this.ds;
61731     }
61732     if(this.cm){
61733         this.colModel = this.cm;
61734         delete this.cm;
61735     }
61736     if(this.sm){
61737         this.selModel = this.sm;
61738         delete this.sm;
61739     }
61740
61741     if (this.selModel) {
61742         this.selModel = Roo.factory(this.selModel, Roo.grid);
61743         this.sm = this.selModel;
61744         this.sm.xmodule = this.xmodule || false;
61745     }
61746     if (typeof(this.colModel.config) == 'undefined') {
61747         this.colModel = new Roo.grid.ColumnModel(this.colModel);
61748         this.cm = this.colModel;
61749         this.cm.xmodule = this.xmodule || false;
61750     }
61751     if (this.dataSource) {
61752         this.dataSource= Roo.factory(this.dataSource, Roo.data);
61753         this.ds = this.dataSource;
61754         this.ds.xmodule = this.xmodule || false;
61755          
61756     }
61757     
61758     
61759     
61760     if(this.width){
61761         this.container.setWidth(this.width);
61762     }
61763
61764     if(this.height){
61765         this.container.setHeight(this.height);
61766     }
61767     /** @private */
61768         this.addEvents({
61769         // raw events
61770         /**
61771          * @event click
61772          * The raw click event for the entire grid.
61773          * @param {Roo.EventObject} e
61774          */
61775         "click" : true,
61776         /**
61777          * @event dblclick
61778          * The raw dblclick event for the entire grid.
61779          * @param {Roo.EventObject} e
61780          */
61781         "dblclick" : true,
61782         /**
61783          * @event contextmenu
61784          * The raw contextmenu event for the entire grid.
61785          * @param {Roo.EventObject} e
61786          */
61787         "contextmenu" : true,
61788         /**
61789          * @event mousedown
61790          * The raw mousedown event for the entire grid.
61791          * @param {Roo.EventObject} e
61792          */
61793         "mousedown" : true,
61794         /**
61795          * @event mouseup
61796          * The raw mouseup event for the entire grid.
61797          * @param {Roo.EventObject} e
61798          */
61799         "mouseup" : true,
61800         /**
61801          * @event mouseover
61802          * The raw mouseover event for the entire grid.
61803          * @param {Roo.EventObject} e
61804          */
61805         "mouseover" : true,
61806         /**
61807          * @event mouseout
61808          * The raw mouseout event for the entire grid.
61809          * @param {Roo.EventObject} e
61810          */
61811         "mouseout" : true,
61812         /**
61813          * @event keypress
61814          * The raw keypress event for the entire grid.
61815          * @param {Roo.EventObject} e
61816          */
61817         "keypress" : true,
61818         /**
61819          * @event keydown
61820          * The raw keydown event for the entire grid.
61821          * @param {Roo.EventObject} e
61822          */
61823         "keydown" : true,
61824
61825         // custom events
61826
61827         /**
61828          * @event cellclick
61829          * Fires when a cell is clicked
61830          * @param {Grid} this
61831          * @param {Number} rowIndex
61832          * @param {Number} columnIndex
61833          * @param {Roo.EventObject} e
61834          */
61835         "cellclick" : true,
61836         /**
61837          * @event celldblclick
61838          * Fires when a cell is double clicked
61839          * @param {Grid} this
61840          * @param {Number} rowIndex
61841          * @param {Number} columnIndex
61842          * @param {Roo.EventObject} e
61843          */
61844         "celldblclick" : true,
61845         /**
61846          * @event rowclick
61847          * Fires when a row is clicked
61848          * @param {Grid} this
61849          * @param {Number} rowIndex
61850          * @param {Roo.EventObject} e
61851          */
61852         "rowclick" : true,
61853         /**
61854          * @event rowdblclick
61855          * Fires when a row is double clicked
61856          * @param {Grid} this
61857          * @param {Number} rowIndex
61858          * @param {Roo.EventObject} e
61859          */
61860         "rowdblclick" : true,
61861         /**
61862          * @event headerclick
61863          * Fires when a header is clicked
61864          * @param {Grid} this
61865          * @param {Number} columnIndex
61866          * @param {Roo.EventObject} e
61867          */
61868         "headerclick" : true,
61869         /**
61870          * @event headerdblclick
61871          * Fires when a header cell is double clicked
61872          * @param {Grid} this
61873          * @param {Number} columnIndex
61874          * @param {Roo.EventObject} e
61875          */
61876         "headerdblclick" : true,
61877         /**
61878          * @event rowcontextmenu
61879          * Fires when a row is right clicked
61880          * @param {Grid} this
61881          * @param {Number} rowIndex
61882          * @param {Roo.EventObject} e
61883          */
61884         "rowcontextmenu" : true,
61885         /**
61886          * @event cellcontextmenu
61887          * Fires when a cell is right clicked
61888          * @param {Grid} this
61889          * @param {Number} rowIndex
61890          * @param {Number} cellIndex
61891          * @param {Roo.EventObject} e
61892          */
61893          "cellcontextmenu" : true,
61894         /**
61895          * @event headercontextmenu
61896          * Fires when a header is right clicked
61897          * @param {Grid} this
61898          * @param {Number} columnIndex
61899          * @param {Roo.EventObject} e
61900          */
61901         "headercontextmenu" : true,
61902         /**
61903          * @event bodyscroll
61904          * Fires when the body element is scrolled
61905          * @param {Number} scrollLeft
61906          * @param {Number} scrollTop
61907          */
61908         "bodyscroll" : true,
61909         /**
61910          * @event columnresize
61911          * Fires when the user resizes a column
61912          * @param {Number} columnIndex
61913          * @param {Number} newSize
61914          */
61915         "columnresize" : true,
61916         /**
61917          * @event columnmove
61918          * Fires when the user moves a column
61919          * @param {Number} oldIndex
61920          * @param {Number} newIndex
61921          */
61922         "columnmove" : true,
61923         /**
61924          * @event startdrag
61925          * Fires when row(s) start being dragged
61926          * @param {Grid} this
61927          * @param {Roo.GridDD} dd The drag drop object
61928          * @param {event} e The raw browser event
61929          */
61930         "startdrag" : true,
61931         /**
61932          * @event enddrag
61933          * Fires when a drag operation is complete
61934          * @param {Grid} this
61935          * @param {Roo.GridDD} dd The drag drop object
61936          * @param {event} e The raw browser event
61937          */
61938         "enddrag" : true,
61939         /**
61940          * @event dragdrop
61941          * Fires when dragged row(s) are dropped on a valid DD target
61942          * @param {Grid} this
61943          * @param {Roo.GridDD} dd The drag drop object
61944          * @param {String} targetId The target drag drop object
61945          * @param {event} e The raw browser event
61946          */
61947         "dragdrop" : true,
61948         /**
61949          * @event dragover
61950          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61951          * @param {Grid} this
61952          * @param {Roo.GridDD} dd The drag drop object
61953          * @param {String} targetId The target drag drop object
61954          * @param {event} e The raw browser event
61955          */
61956         "dragover" : true,
61957         /**
61958          * @event dragenter
61959          *  Fires when the dragged row(s) first cross another DD target while being dragged
61960          * @param {Grid} this
61961          * @param {Roo.GridDD} dd The drag drop object
61962          * @param {String} targetId The target drag drop object
61963          * @param {event} e The raw browser event
61964          */
61965         "dragenter" : true,
61966         /**
61967          * @event dragout
61968          * Fires when the dragged row(s) leave another DD target while being dragged
61969          * @param {Grid} this
61970          * @param {Roo.GridDD} dd The drag drop object
61971          * @param {String} targetId The target drag drop object
61972          * @param {event} e The raw browser event
61973          */
61974         "dragout" : true,
61975         /**
61976          * @event rowclass
61977          * Fires when a row is rendered, so you can change add a style to it.
61978          * @param {GridView} gridview   The grid view
61979          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
61980          */
61981         'rowclass' : true,
61982
61983         /**
61984          * @event render
61985          * Fires when the grid is rendered
61986          * @param {Grid} grid
61987          */
61988         'render' : true
61989     });
61990
61991     Roo.grid.Grid.superclass.constructor.call(this);
61992 };
61993 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
61994     
61995     /**
61996          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
61997          */
61998         /**
61999          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
62000          */
62001         /**
62002          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
62003          */
62004         /**
62005          * @cfg {Roo.data.Store} ds The data store for the grid
62006          */
62007         /**
62008          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
62009          */
62010          
62011          /**
62012          * @cfg {Roo.PagingToolbar} footer the paging toolbar
62013          */
62014         
62015         /**
62016      * @cfg {String} ddGroup - drag drop group.
62017      */
62018       /**
62019      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
62020      */
62021
62022     /**
62023      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
62024      */
62025     minColumnWidth : 25,
62026
62027     /**
62028      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
62029      * <b>on initial render.</b> It is more efficient to explicitly size the columns
62030      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
62031      */
62032     autoSizeColumns : false,
62033
62034     /**
62035      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
62036      */
62037     autoSizeHeaders : true,
62038
62039     /**
62040      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
62041      */
62042     monitorWindowResize : true,
62043
62044     /**
62045      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
62046      * rows measured to get a columns size. Default is 0 (all rows).
62047      */
62048     maxRowsToMeasure : 0,
62049
62050     /**
62051      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
62052      */
62053     trackMouseOver : true,
62054
62055     /**
62056     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
62057     */
62058       /**
62059     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
62060     */
62061     
62062     /**
62063     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
62064     */
62065     enableDragDrop : false,
62066     
62067     /**
62068     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
62069     */
62070     enableColumnMove : true,
62071     
62072     /**
62073     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
62074     */
62075     enableColumnHide : true,
62076     
62077     /**
62078     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
62079     */
62080     enableRowHeightSync : false,
62081     
62082     /**
62083     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
62084     */
62085     stripeRows : true,
62086     
62087     /**
62088     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
62089     */
62090     autoHeight : false,
62091
62092     /**
62093      * @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.
62094      */
62095     autoExpandColumn : false,
62096
62097     /**
62098     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
62099     * Default is 50.
62100     */
62101     autoExpandMin : 50,
62102
62103     /**
62104     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
62105     */
62106     autoExpandMax : 1000,
62107
62108     /**
62109     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
62110     */
62111     view : null,
62112
62113     /**
62114     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
62115     */
62116     loadMask : false,
62117     /**
62118     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
62119     */
62120     dropTarget: false,
62121      /**
62122     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
62123     */ 
62124     sortColMenu : false,
62125     
62126     // private
62127     rendered : false,
62128
62129     /**
62130     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
62131     * of a fixed width. Default is false.
62132     */
62133     /**
62134     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
62135     */
62136     
62137     
62138     /**
62139     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
62140     * %0 is replaced with the number of selected rows.
62141     */
62142     ddText : "{0} selected row{1}",
62143     
62144     
62145     /**
62146      * Called once after all setup has been completed and the grid is ready to be rendered.
62147      * @return {Roo.grid.Grid} this
62148      */
62149     render : function()
62150     {
62151         var c = this.container;
62152         // try to detect autoHeight/width mode
62153         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
62154             this.autoHeight = true;
62155         }
62156         var view = this.getView();
62157         view.init(this);
62158
62159         c.on("click", this.onClick, this);
62160         c.on("dblclick", this.onDblClick, this);
62161         c.on("contextmenu", this.onContextMenu, this);
62162         c.on("keydown", this.onKeyDown, this);
62163         if (Roo.isTouch) {
62164             c.on("touchstart", this.onTouchStart, this);
62165         }
62166
62167         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
62168
62169         this.getSelectionModel().init(this);
62170
62171         view.render();
62172
62173         if(this.loadMask){
62174             this.loadMask = new Roo.LoadMask(this.container,
62175                     Roo.apply({store:this.dataSource}, this.loadMask));
62176         }
62177         
62178         
62179         if (this.toolbar && this.toolbar.xtype) {
62180             this.toolbar.container = this.getView().getHeaderPanel(true);
62181             this.toolbar = new Roo.Toolbar(this.toolbar);
62182         }
62183         if (this.footer && this.footer.xtype) {
62184             this.footer.dataSource = this.getDataSource();
62185             this.footer.container = this.getView().getFooterPanel(true);
62186             this.footer = Roo.factory(this.footer, Roo);
62187         }
62188         if (this.dropTarget && this.dropTarget.xtype) {
62189             delete this.dropTarget.xtype;
62190             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
62191         }
62192         
62193         
62194         this.rendered = true;
62195         this.fireEvent('render', this);
62196         return this;
62197     },
62198
62199     /**
62200      * Reconfigures the grid to use a different Store and Column Model.
62201      * The View will be bound to the new objects and refreshed.
62202      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
62203      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
62204      */
62205     reconfigure : function(dataSource, colModel){
62206         if(this.loadMask){
62207             this.loadMask.destroy();
62208             this.loadMask = new Roo.LoadMask(this.container,
62209                     Roo.apply({store:dataSource}, this.loadMask));
62210         }
62211         this.view.bind(dataSource, colModel);
62212         this.dataSource = dataSource;
62213         this.colModel = colModel;
62214         this.view.refresh(true);
62215     },
62216     /**
62217      * addColumns
62218      * Add's a column, default at the end..
62219      
62220      * @param {int} position to add (default end)
62221      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
62222      */
62223     addColumns : function(pos, ar)
62224     {
62225         
62226         for (var i =0;i< ar.length;i++) {
62227             var cfg = ar[i];
62228             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
62229             this.cm.lookup[cfg.id] = cfg;
62230         }
62231         
62232         
62233         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
62234             pos = this.cm.config.length; //this.cm.config.push(cfg);
62235         } 
62236         pos = Math.max(0,pos);
62237         ar.unshift(0);
62238         ar.unshift(pos);
62239         this.cm.config.splice.apply(this.cm.config, ar);
62240         
62241         
62242         
62243         this.view.generateRules(this.cm);
62244         this.view.refresh(true);
62245         
62246     },
62247     
62248     
62249     
62250     
62251     // private
62252     onKeyDown : function(e){
62253         this.fireEvent("keydown", e);
62254     },
62255
62256     /**
62257      * Destroy this grid.
62258      * @param {Boolean} removeEl True to remove the element
62259      */
62260     destroy : function(removeEl, keepListeners){
62261         if(this.loadMask){
62262             this.loadMask.destroy();
62263         }
62264         var c = this.container;
62265         c.removeAllListeners();
62266         this.view.destroy();
62267         this.colModel.purgeListeners();
62268         if(!keepListeners){
62269             this.purgeListeners();
62270         }
62271         c.update("");
62272         if(removeEl === true){
62273             c.remove();
62274         }
62275     },
62276
62277     // private
62278     processEvent : function(name, e){
62279         // does this fire select???
62280         //Roo.log('grid:processEvent '  + name);
62281         
62282         if (name != 'touchstart' ) {
62283             this.fireEvent(name, e);    
62284         }
62285         
62286         var t = e.getTarget();
62287         var v = this.view;
62288         var header = v.findHeaderIndex(t);
62289         if(header !== false){
62290             var ename = name == 'touchstart' ? 'click' : name;
62291              
62292             this.fireEvent("header" + ename, this, header, e);
62293         }else{
62294             var row = v.findRowIndex(t);
62295             var cell = v.findCellIndex(t);
62296             if (name == 'touchstart') {
62297                 // first touch is always a click.
62298                 // hopefull this happens after selection is updated.?
62299                 name = false;
62300                 
62301                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
62302                     var cs = this.selModel.getSelectedCell();
62303                     if (row == cs[0] && cell == cs[1]){
62304                         name = 'dblclick';
62305                     }
62306                 }
62307                 if (typeof(this.selModel.getSelections) != 'undefined') {
62308                     var cs = this.selModel.getSelections();
62309                     var ds = this.dataSource;
62310                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
62311                         name = 'dblclick';
62312                     }
62313                 }
62314                 if (!name) {
62315                     return;
62316                 }
62317             }
62318             
62319             
62320             if(row !== false){
62321                 this.fireEvent("row" + name, this, row, e);
62322                 if(cell !== false){
62323                     this.fireEvent("cell" + name, this, row, cell, e);
62324                 }
62325             }
62326         }
62327     },
62328
62329     // private
62330     onClick : function(e){
62331         this.processEvent("click", e);
62332     },
62333    // private
62334     onTouchStart : function(e){
62335         this.processEvent("touchstart", e);
62336     },
62337
62338     // private
62339     onContextMenu : function(e, t){
62340         this.processEvent("contextmenu", e);
62341     },
62342
62343     // private
62344     onDblClick : function(e){
62345         this.processEvent("dblclick", e);
62346     },
62347
62348     // private
62349     walkCells : function(row, col, step, fn, scope){
62350         var cm = this.colModel, clen = cm.getColumnCount();
62351         var ds = this.dataSource, rlen = ds.getCount(), first = true;
62352         if(step < 0){
62353             if(col < 0){
62354                 row--;
62355                 first = false;
62356             }
62357             while(row >= 0){
62358                 if(!first){
62359                     col = clen-1;
62360                 }
62361                 first = false;
62362                 while(col >= 0){
62363                     if(fn.call(scope || this, row, col, cm) === true){
62364                         return [row, col];
62365                     }
62366                     col--;
62367                 }
62368                 row--;
62369             }
62370         } else {
62371             if(col >= clen){
62372                 row++;
62373                 first = false;
62374             }
62375             while(row < rlen){
62376                 if(!first){
62377                     col = 0;
62378                 }
62379                 first = false;
62380                 while(col < clen){
62381                     if(fn.call(scope || this, row, col, cm) === true){
62382                         return [row, col];
62383                     }
62384                     col++;
62385                 }
62386                 row++;
62387             }
62388         }
62389         return null;
62390     },
62391
62392     // private
62393     getSelections : function(){
62394         return this.selModel.getSelections();
62395     },
62396
62397     /**
62398      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
62399      * but if manual update is required this method will initiate it.
62400      */
62401     autoSize : function(){
62402         if(this.rendered){
62403             this.view.layout();
62404             if(this.view.adjustForScroll){
62405                 this.view.adjustForScroll();
62406             }
62407         }
62408     },
62409
62410     /**
62411      * Returns the grid's underlying element.
62412      * @return {Element} The element
62413      */
62414     getGridEl : function(){
62415         return this.container;
62416     },
62417
62418     // private for compatibility, overridden by editor grid
62419     stopEditing : function(){},
62420
62421     /**
62422      * Returns the grid's SelectionModel.
62423      * @return {SelectionModel}
62424      */
62425     getSelectionModel : function(){
62426         if(!this.selModel){
62427             this.selModel = new Roo.grid.RowSelectionModel();
62428         }
62429         return this.selModel;
62430     },
62431
62432     /**
62433      * Returns the grid's DataSource.
62434      * @return {DataSource}
62435      */
62436     getDataSource : function(){
62437         return this.dataSource;
62438     },
62439
62440     /**
62441      * Returns the grid's ColumnModel.
62442      * @return {ColumnModel}
62443      */
62444     getColumnModel : function(){
62445         return this.colModel;
62446     },
62447
62448     /**
62449      * Returns the grid's GridView object.
62450      * @return {GridView}
62451      */
62452     getView : function(){
62453         if(!this.view){
62454             this.view = new Roo.grid.GridView(this.viewConfig);
62455             this.relayEvents(this.view, [
62456                 "beforerowremoved", "beforerowsinserted",
62457                 "beforerefresh", "rowremoved",
62458                 "rowsinserted", "rowupdated" ,"refresh"
62459             ]);
62460         }
62461         return this.view;
62462     },
62463     /**
62464      * Called to get grid's drag proxy text, by default returns this.ddText.
62465      * Override this to put something different in the dragged text.
62466      * @return {String}
62467      */
62468     getDragDropText : function(){
62469         var count = this.selModel.getCount();
62470         return String.format(this.ddText, count, count == 1 ? '' : 's');
62471     }
62472 });
62473 /*
62474  * Based on:
62475  * Ext JS Library 1.1.1
62476  * Copyright(c) 2006-2007, Ext JS, LLC.
62477  *
62478  * Originally Released Under LGPL - original licence link has changed is not relivant.
62479  *
62480  * Fork - LGPL
62481  * <script type="text/javascript">
62482  */
62483  /**
62484  * @class Roo.grid.AbstractGridView
62485  * @extends Roo.util.Observable
62486  * @abstract
62487  * Abstract base class for grid Views
62488  * @constructor
62489  */
62490 Roo.grid.AbstractGridView = function(){
62491         this.grid = null;
62492         
62493         this.events = {
62494             "beforerowremoved" : true,
62495             "beforerowsinserted" : true,
62496             "beforerefresh" : true,
62497             "rowremoved" : true,
62498             "rowsinserted" : true,
62499             "rowupdated" : true,
62500             "refresh" : true
62501         };
62502     Roo.grid.AbstractGridView.superclass.constructor.call(this);
62503 };
62504
62505 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
62506     rowClass : "x-grid-row",
62507     cellClass : "x-grid-cell",
62508     tdClass : "x-grid-td",
62509     hdClass : "x-grid-hd",
62510     splitClass : "x-grid-hd-split",
62511     
62512     init: function(grid){
62513         this.grid = grid;
62514                 var cid = this.grid.getGridEl().id;
62515         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
62516         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
62517         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
62518         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
62519         },
62520         
62521     getColumnRenderers : function(){
62522         var renderers = [];
62523         var cm = this.grid.colModel;
62524         var colCount = cm.getColumnCount();
62525         for(var i = 0; i < colCount; i++){
62526             renderers[i] = cm.getRenderer(i);
62527         }
62528         return renderers;
62529     },
62530     
62531     getColumnIds : function(){
62532         var ids = [];
62533         var cm = this.grid.colModel;
62534         var colCount = cm.getColumnCount();
62535         for(var i = 0; i < colCount; i++){
62536             ids[i] = cm.getColumnId(i);
62537         }
62538         return ids;
62539     },
62540     
62541     getDataIndexes : function(){
62542         if(!this.indexMap){
62543             this.indexMap = this.buildIndexMap();
62544         }
62545         return this.indexMap.colToData;
62546     },
62547     
62548     getColumnIndexByDataIndex : function(dataIndex){
62549         if(!this.indexMap){
62550             this.indexMap = this.buildIndexMap();
62551         }
62552         return this.indexMap.dataToCol[dataIndex];
62553     },
62554     
62555     /**
62556      * Set a css style for a column dynamically. 
62557      * @param {Number} colIndex The index of the column
62558      * @param {String} name The css property name
62559      * @param {String} value The css value
62560      */
62561     setCSSStyle : function(colIndex, name, value){
62562         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
62563         Roo.util.CSS.updateRule(selector, name, value);
62564     },
62565     
62566     generateRules : function(cm){
62567         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
62568         Roo.util.CSS.removeStyleSheet(rulesId);
62569         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
62570             var cid = cm.getColumnId(i);
62571             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
62572                          this.tdSelector, cid, " {\n}\n",
62573                          this.hdSelector, cid, " {\n}\n",
62574                          this.splitSelector, cid, " {\n}\n");
62575         }
62576         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
62577     }
62578 });/*
62579  * Based on:
62580  * Ext JS Library 1.1.1
62581  * Copyright(c) 2006-2007, Ext JS, LLC.
62582  *
62583  * Originally Released Under LGPL - original licence link has changed is not relivant.
62584  *
62585  * Fork - LGPL
62586  * <script type="text/javascript">
62587  */
62588
62589 // private
62590 // This is a support class used internally by the Grid components
62591 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
62592     this.grid = grid;
62593     this.view = grid.getView();
62594     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62595     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
62596     if(hd2){
62597         this.setHandleElId(Roo.id(hd));
62598         this.setOuterHandleElId(Roo.id(hd2));
62599     }
62600     this.scroll = false;
62601 };
62602 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
62603     maxDragWidth: 120,
62604     getDragData : function(e){
62605         var t = Roo.lib.Event.getTarget(e);
62606         var h = this.view.findHeaderCell(t);
62607         if(h){
62608             return {ddel: h.firstChild, header:h};
62609         }
62610         return false;
62611     },
62612
62613     onInitDrag : function(e){
62614         this.view.headersDisabled = true;
62615         var clone = this.dragData.ddel.cloneNode(true);
62616         clone.id = Roo.id();
62617         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
62618         this.proxy.update(clone);
62619         return true;
62620     },
62621
62622     afterValidDrop : function(){
62623         var v = this.view;
62624         setTimeout(function(){
62625             v.headersDisabled = false;
62626         }, 50);
62627     },
62628
62629     afterInvalidDrop : function(){
62630         var v = this.view;
62631         setTimeout(function(){
62632             v.headersDisabled = false;
62633         }, 50);
62634     }
62635 });
62636 /*
62637  * Based on:
62638  * Ext JS Library 1.1.1
62639  * Copyright(c) 2006-2007, Ext JS, LLC.
62640  *
62641  * Originally Released Under LGPL - original licence link has changed is not relivant.
62642  *
62643  * Fork - LGPL
62644  * <script type="text/javascript">
62645  */
62646 // private
62647 // This is a support class used internally by the Grid components
62648 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
62649     this.grid = grid;
62650     this.view = grid.getView();
62651     // split the proxies so they don't interfere with mouse events
62652     this.proxyTop = Roo.DomHelper.append(document.body, {
62653         cls:"col-move-top", html:"&#160;"
62654     }, true);
62655     this.proxyBottom = Roo.DomHelper.append(document.body, {
62656         cls:"col-move-bottom", html:"&#160;"
62657     }, true);
62658     this.proxyTop.hide = this.proxyBottom.hide = function(){
62659         this.setLeftTop(-100,-100);
62660         this.setStyle("visibility", "hidden");
62661     };
62662     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
62663     // temporarily disabled
62664     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
62665     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
62666 };
62667 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
62668     proxyOffsets : [-4, -9],
62669     fly: Roo.Element.fly,
62670
62671     getTargetFromEvent : function(e){
62672         var t = Roo.lib.Event.getTarget(e);
62673         var cindex = this.view.findCellIndex(t);
62674         if(cindex !== false){
62675             return this.view.getHeaderCell(cindex);
62676         }
62677         return null;
62678     },
62679
62680     nextVisible : function(h){
62681         var v = this.view, cm = this.grid.colModel;
62682         h = h.nextSibling;
62683         while(h){
62684             if(!cm.isHidden(v.getCellIndex(h))){
62685                 return h;
62686             }
62687             h = h.nextSibling;
62688         }
62689         return null;
62690     },
62691
62692     prevVisible : function(h){
62693         var v = this.view, cm = this.grid.colModel;
62694         h = h.prevSibling;
62695         while(h){
62696             if(!cm.isHidden(v.getCellIndex(h))){
62697                 return h;
62698             }
62699             h = h.prevSibling;
62700         }
62701         return null;
62702     },
62703
62704     positionIndicator : function(h, n, e){
62705         var x = Roo.lib.Event.getPageX(e);
62706         var r = Roo.lib.Dom.getRegion(n.firstChild);
62707         var px, pt, py = r.top + this.proxyOffsets[1];
62708         if((r.right - x) <= (r.right-r.left)/2){
62709             px = r.right+this.view.borderWidth;
62710             pt = "after";
62711         }else{
62712             px = r.left;
62713             pt = "before";
62714         }
62715         var oldIndex = this.view.getCellIndex(h);
62716         var newIndex = this.view.getCellIndex(n);
62717
62718         if(this.grid.colModel.isFixed(newIndex)){
62719             return false;
62720         }
62721
62722         var locked = this.grid.colModel.isLocked(newIndex);
62723
62724         if(pt == "after"){
62725             newIndex++;
62726         }
62727         if(oldIndex < newIndex){
62728             newIndex--;
62729         }
62730         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
62731             return false;
62732         }
62733         px +=  this.proxyOffsets[0];
62734         this.proxyTop.setLeftTop(px, py);
62735         this.proxyTop.show();
62736         if(!this.bottomOffset){
62737             this.bottomOffset = this.view.mainHd.getHeight();
62738         }
62739         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
62740         this.proxyBottom.show();
62741         return pt;
62742     },
62743
62744     onNodeEnter : function(n, dd, e, data){
62745         if(data.header != n){
62746             this.positionIndicator(data.header, n, e);
62747         }
62748     },
62749
62750     onNodeOver : function(n, dd, e, data){
62751         var result = false;
62752         if(data.header != n){
62753             result = this.positionIndicator(data.header, n, e);
62754         }
62755         if(!result){
62756             this.proxyTop.hide();
62757             this.proxyBottom.hide();
62758         }
62759         return result ? this.dropAllowed : this.dropNotAllowed;
62760     },
62761
62762     onNodeOut : function(n, dd, e, data){
62763         this.proxyTop.hide();
62764         this.proxyBottom.hide();
62765     },
62766
62767     onNodeDrop : function(n, dd, e, data){
62768         var h = data.header;
62769         if(h != n){
62770             var cm = this.grid.colModel;
62771             var x = Roo.lib.Event.getPageX(e);
62772             var r = Roo.lib.Dom.getRegion(n.firstChild);
62773             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
62774             var oldIndex = this.view.getCellIndex(h);
62775             var newIndex = this.view.getCellIndex(n);
62776             var locked = cm.isLocked(newIndex);
62777             if(pt == "after"){
62778                 newIndex++;
62779             }
62780             if(oldIndex < newIndex){
62781                 newIndex--;
62782             }
62783             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
62784                 return false;
62785             }
62786             cm.setLocked(oldIndex, locked, true);
62787             cm.moveColumn(oldIndex, newIndex);
62788             this.grid.fireEvent("columnmove", oldIndex, newIndex);
62789             return true;
62790         }
62791         return false;
62792     }
62793 });
62794 /*
62795  * Based on:
62796  * Ext JS Library 1.1.1
62797  * Copyright(c) 2006-2007, Ext JS, LLC.
62798  *
62799  * Originally Released Under LGPL - original licence link has changed is not relivant.
62800  *
62801  * Fork - LGPL
62802  * <script type="text/javascript">
62803  */
62804   
62805 /**
62806  * @class Roo.grid.GridView
62807  * @extends Roo.util.Observable
62808  *
62809  * @constructor
62810  * @param {Object} config
62811  */
62812 Roo.grid.GridView = function(config){
62813     Roo.grid.GridView.superclass.constructor.call(this);
62814     this.el = null;
62815
62816     Roo.apply(this, config);
62817 };
62818
62819 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
62820
62821     unselectable :  'unselectable="on"',
62822     unselectableCls :  'x-unselectable',
62823     
62824     
62825     rowClass : "x-grid-row",
62826
62827     cellClass : "x-grid-col",
62828
62829     tdClass : "x-grid-td",
62830
62831     hdClass : "x-grid-hd",
62832
62833     splitClass : "x-grid-split",
62834
62835     sortClasses : ["sort-asc", "sort-desc"],
62836
62837     enableMoveAnim : false,
62838
62839     hlColor: "C3DAF9",
62840
62841     dh : Roo.DomHelper,
62842
62843     fly : Roo.Element.fly,
62844
62845     css : Roo.util.CSS,
62846
62847     borderWidth: 1,
62848
62849     splitOffset: 3,
62850
62851     scrollIncrement : 22,
62852
62853     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
62854
62855     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
62856
62857     bind : function(ds, cm){
62858         if(this.ds){
62859             this.ds.un("load", this.onLoad, this);
62860             this.ds.un("datachanged", this.onDataChange, this);
62861             this.ds.un("add", this.onAdd, this);
62862             this.ds.un("remove", this.onRemove, this);
62863             this.ds.un("update", this.onUpdate, this);
62864             this.ds.un("clear", this.onClear, this);
62865         }
62866         if(ds){
62867             ds.on("load", this.onLoad, this);
62868             ds.on("datachanged", this.onDataChange, this);
62869             ds.on("add", this.onAdd, this);
62870             ds.on("remove", this.onRemove, this);
62871             ds.on("update", this.onUpdate, this);
62872             ds.on("clear", this.onClear, this);
62873         }
62874         this.ds = ds;
62875
62876         if(this.cm){
62877             this.cm.un("widthchange", this.onColWidthChange, this);
62878             this.cm.un("headerchange", this.onHeaderChange, this);
62879             this.cm.un("hiddenchange", this.onHiddenChange, this);
62880             this.cm.un("columnmoved", this.onColumnMove, this);
62881             this.cm.un("columnlockchange", this.onColumnLock, this);
62882         }
62883         if(cm){
62884             this.generateRules(cm);
62885             cm.on("widthchange", this.onColWidthChange, this);
62886             cm.on("headerchange", this.onHeaderChange, this);
62887             cm.on("hiddenchange", this.onHiddenChange, this);
62888             cm.on("columnmoved", this.onColumnMove, this);
62889             cm.on("columnlockchange", this.onColumnLock, this);
62890         }
62891         this.cm = cm;
62892     },
62893
62894     init: function(grid){
62895         Roo.grid.GridView.superclass.init.call(this, grid);
62896
62897         this.bind(grid.dataSource, grid.colModel);
62898
62899         grid.on("headerclick", this.handleHeaderClick, this);
62900
62901         if(grid.trackMouseOver){
62902             grid.on("mouseover", this.onRowOver, this);
62903             grid.on("mouseout", this.onRowOut, this);
62904         }
62905         grid.cancelTextSelection = function(){};
62906         this.gridId = grid.id;
62907
62908         var tpls = this.templates || {};
62909
62910         if(!tpls.master){
62911             tpls.master = new Roo.Template(
62912                '<div class="x-grid" hidefocus="true">',
62913                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
62914                   '<div class="x-grid-topbar"></div>',
62915                   '<div class="x-grid-scroller"><div></div></div>',
62916                   '<div class="x-grid-locked">',
62917                       '<div class="x-grid-header">{lockedHeader}</div>',
62918                       '<div class="x-grid-body">{lockedBody}</div>',
62919                   "</div>",
62920                   '<div class="x-grid-viewport">',
62921                       '<div class="x-grid-header">{header}</div>',
62922                       '<div class="x-grid-body">{body}</div>',
62923                   "</div>",
62924                   '<div class="x-grid-bottombar"></div>',
62925                  
62926                   '<div class="x-grid-resize-proxy">&#160;</div>',
62927                "</div>"
62928             );
62929             tpls.master.disableformats = true;
62930         }
62931
62932         if(!tpls.header){
62933             tpls.header = new Roo.Template(
62934                '<table border="0" cellspacing="0" cellpadding="0">',
62935                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
62936                "</table>{splits}"
62937             );
62938             tpls.header.disableformats = true;
62939         }
62940         tpls.header.compile();
62941
62942         if(!tpls.hcell){
62943             tpls.hcell = new Roo.Template(
62944                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
62945                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
62946                 "</div></td>"
62947              );
62948              tpls.hcell.disableFormats = true;
62949         }
62950         tpls.hcell.compile();
62951
62952         if(!tpls.hsplit){
62953             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
62954                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
62955             tpls.hsplit.disableFormats = true;
62956         }
62957         tpls.hsplit.compile();
62958
62959         if(!tpls.body){
62960             tpls.body = new Roo.Template(
62961                '<table border="0" cellspacing="0" cellpadding="0">',
62962                "<tbody>{rows}</tbody>",
62963                "</table>"
62964             );
62965             tpls.body.disableFormats = true;
62966         }
62967         tpls.body.compile();
62968
62969         if(!tpls.row){
62970             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
62971             tpls.row.disableFormats = true;
62972         }
62973         tpls.row.compile();
62974
62975         if(!tpls.cell){
62976             tpls.cell = new Roo.Template(
62977                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
62978                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
62979                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
62980                 "</td>"
62981             );
62982             tpls.cell.disableFormats = true;
62983         }
62984         tpls.cell.compile();
62985
62986         this.templates = tpls;
62987     },
62988
62989     // remap these for backwards compat
62990     onColWidthChange : function(){
62991         this.updateColumns.apply(this, arguments);
62992     },
62993     onHeaderChange : function(){
62994         this.updateHeaders.apply(this, arguments);
62995     }, 
62996     onHiddenChange : function(){
62997         this.handleHiddenChange.apply(this, arguments);
62998     },
62999     onColumnMove : function(){
63000         this.handleColumnMove.apply(this, arguments);
63001     },
63002     onColumnLock : function(){
63003         this.handleLockChange.apply(this, arguments);
63004     },
63005
63006     onDataChange : function(){
63007         this.refresh();
63008         this.updateHeaderSortState();
63009     },
63010
63011     onClear : function(){
63012         this.refresh();
63013     },
63014
63015     onUpdate : function(ds, record){
63016         this.refreshRow(record);
63017     },
63018
63019     refreshRow : function(record){
63020         var ds = this.ds, index;
63021         if(typeof record == 'number'){
63022             index = record;
63023             record = ds.getAt(index);
63024         }else{
63025             index = ds.indexOf(record);
63026         }
63027         this.insertRows(ds, index, index, true);
63028         this.onRemove(ds, record, index+1, true);
63029         this.syncRowHeights(index, index);
63030         this.layout();
63031         this.fireEvent("rowupdated", this, index, record);
63032     },
63033
63034     onAdd : function(ds, records, index){
63035         this.insertRows(ds, index, index + (records.length-1));
63036     },
63037
63038     onRemove : function(ds, record, index, isUpdate){
63039         if(isUpdate !== true){
63040             this.fireEvent("beforerowremoved", this, index, record);
63041         }
63042         var bt = this.getBodyTable(), lt = this.getLockedTable();
63043         if(bt.rows[index]){
63044             bt.firstChild.removeChild(bt.rows[index]);
63045         }
63046         if(lt.rows[index]){
63047             lt.firstChild.removeChild(lt.rows[index]);
63048         }
63049         if(isUpdate !== true){
63050             this.stripeRows(index);
63051             this.syncRowHeights(index, index);
63052             this.layout();
63053             this.fireEvent("rowremoved", this, index, record);
63054         }
63055     },
63056
63057     onLoad : function(){
63058         this.scrollToTop();
63059     },
63060
63061     /**
63062      * Scrolls the grid to the top
63063      */
63064     scrollToTop : function(){
63065         if(this.scroller){
63066             this.scroller.dom.scrollTop = 0;
63067             this.syncScroll();
63068         }
63069     },
63070
63071     /**
63072      * Gets a panel in the header of the grid that can be used for toolbars etc.
63073      * After modifying the contents of this panel a call to grid.autoSize() may be
63074      * required to register any changes in size.
63075      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
63076      * @return Roo.Element
63077      */
63078     getHeaderPanel : function(doShow){
63079         if(doShow){
63080             this.headerPanel.show();
63081         }
63082         return this.headerPanel;
63083     },
63084
63085     /**
63086      * Gets a panel in the footer of the grid that can be used for toolbars etc.
63087      * After modifying the contents of this panel a call to grid.autoSize() may be
63088      * required to register any changes in size.
63089      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
63090      * @return Roo.Element
63091      */
63092     getFooterPanel : function(doShow){
63093         if(doShow){
63094             this.footerPanel.show();
63095         }
63096         return this.footerPanel;
63097     },
63098
63099     initElements : function(){
63100         var E = Roo.Element;
63101         var el = this.grid.getGridEl().dom.firstChild;
63102         var cs = el.childNodes;
63103
63104         this.el = new E(el);
63105         
63106          this.focusEl = new E(el.firstChild);
63107         this.focusEl.swallowEvent("click", true);
63108         
63109         this.headerPanel = new E(cs[1]);
63110         this.headerPanel.enableDisplayMode("block");
63111
63112         this.scroller = new E(cs[2]);
63113         this.scrollSizer = new E(this.scroller.dom.firstChild);
63114
63115         this.lockedWrap = new E(cs[3]);
63116         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
63117         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
63118
63119         this.mainWrap = new E(cs[4]);
63120         this.mainHd = new E(this.mainWrap.dom.firstChild);
63121         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
63122
63123         this.footerPanel = new E(cs[5]);
63124         this.footerPanel.enableDisplayMode("block");
63125
63126         this.resizeProxy = new E(cs[6]);
63127
63128         this.headerSelector = String.format(
63129            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
63130            this.lockedHd.id, this.mainHd.id
63131         );
63132
63133         this.splitterSelector = String.format(
63134            '#{0} div.x-grid-split, #{1} div.x-grid-split',
63135            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
63136         );
63137     },
63138     idToCssName : function(s)
63139     {
63140         return s.replace(/[^a-z0-9]+/ig, '-');
63141     },
63142
63143     getHeaderCell : function(index){
63144         return Roo.DomQuery.select(this.headerSelector)[index];
63145     },
63146
63147     getHeaderCellMeasure : function(index){
63148         return this.getHeaderCell(index).firstChild;
63149     },
63150
63151     getHeaderCellText : function(index){
63152         return this.getHeaderCell(index).firstChild.firstChild;
63153     },
63154
63155     getLockedTable : function(){
63156         return this.lockedBody.dom.firstChild;
63157     },
63158
63159     getBodyTable : function(){
63160         return this.mainBody.dom.firstChild;
63161     },
63162
63163     getLockedRow : function(index){
63164         return this.getLockedTable().rows[index];
63165     },
63166
63167     getRow : function(index){
63168         return this.getBodyTable().rows[index];
63169     },
63170
63171     getRowComposite : function(index){
63172         if(!this.rowEl){
63173             this.rowEl = new Roo.CompositeElementLite();
63174         }
63175         var els = [], lrow, mrow;
63176         if(lrow = this.getLockedRow(index)){
63177             els.push(lrow);
63178         }
63179         if(mrow = this.getRow(index)){
63180             els.push(mrow);
63181         }
63182         this.rowEl.elements = els;
63183         return this.rowEl;
63184     },
63185     /**
63186      * Gets the 'td' of the cell
63187      * 
63188      * @param {Integer} rowIndex row to select
63189      * @param {Integer} colIndex column to select
63190      * 
63191      * @return {Object} 
63192      */
63193     getCell : function(rowIndex, colIndex){
63194         var locked = this.cm.getLockedCount();
63195         var source;
63196         if(colIndex < locked){
63197             source = this.lockedBody.dom.firstChild;
63198         }else{
63199             source = this.mainBody.dom.firstChild;
63200             colIndex -= locked;
63201         }
63202         return source.rows[rowIndex].childNodes[colIndex];
63203     },
63204
63205     getCellText : function(rowIndex, colIndex){
63206         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
63207     },
63208
63209     getCellBox : function(cell){
63210         var b = this.fly(cell).getBox();
63211         if(Roo.isOpera){ // opera fails to report the Y
63212             b.y = cell.offsetTop + this.mainBody.getY();
63213         }
63214         return b;
63215     },
63216
63217     getCellIndex : function(cell){
63218         var id = String(cell.className).match(this.cellRE);
63219         if(id){
63220             return parseInt(id[1], 10);
63221         }
63222         return 0;
63223     },
63224
63225     findHeaderIndex : function(n){
63226         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
63227         return r ? this.getCellIndex(r) : false;
63228     },
63229
63230     findHeaderCell : function(n){
63231         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
63232         return r ? r : false;
63233     },
63234
63235     findRowIndex : function(n){
63236         if(!n){
63237             return false;
63238         }
63239         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
63240         return r ? r.rowIndex : false;
63241     },
63242
63243     findCellIndex : function(node){
63244         var stop = this.el.dom;
63245         while(node && node != stop){
63246             if(this.findRE.test(node.className)){
63247                 return this.getCellIndex(node);
63248             }
63249             node = node.parentNode;
63250         }
63251         return false;
63252     },
63253
63254     getColumnId : function(index){
63255         return this.cm.getColumnId(index);
63256     },
63257
63258     getSplitters : function()
63259     {
63260         if(this.splitterSelector){
63261            return Roo.DomQuery.select(this.splitterSelector);
63262         }else{
63263             return null;
63264       }
63265     },
63266
63267     getSplitter : function(index){
63268         return this.getSplitters()[index];
63269     },
63270
63271     onRowOver : function(e, t){
63272         var row;
63273         if((row = this.findRowIndex(t)) !== false){
63274             this.getRowComposite(row).addClass("x-grid-row-over");
63275         }
63276     },
63277
63278     onRowOut : function(e, t){
63279         var row;
63280         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
63281             this.getRowComposite(row).removeClass("x-grid-row-over");
63282         }
63283     },
63284
63285     renderHeaders : function(){
63286         var cm = this.cm;
63287         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
63288         var cb = [], lb = [], sb = [], lsb = [], p = {};
63289         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63290             p.cellId = "x-grid-hd-0-" + i;
63291             p.splitId = "x-grid-csplit-0-" + i;
63292             p.id = cm.getColumnId(i);
63293             p.value = cm.getColumnHeader(i) || "";
63294             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
63295             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
63296             if(!cm.isLocked(i)){
63297                 cb[cb.length] = ct.apply(p);
63298                 sb[sb.length] = st.apply(p);
63299             }else{
63300                 lb[lb.length] = ct.apply(p);
63301                 lsb[lsb.length] = st.apply(p);
63302             }
63303         }
63304         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
63305                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
63306     },
63307
63308     updateHeaders : function(){
63309         var html = this.renderHeaders();
63310         this.lockedHd.update(html[0]);
63311         this.mainHd.update(html[1]);
63312     },
63313
63314     /**
63315      * Focuses the specified row.
63316      * @param {Number} row The row index
63317      */
63318     focusRow : function(row)
63319     {
63320         //Roo.log('GridView.focusRow');
63321         var x = this.scroller.dom.scrollLeft;
63322         this.focusCell(row, 0, false);
63323         this.scroller.dom.scrollLeft = x;
63324     },
63325
63326     /**
63327      * Focuses the specified cell.
63328      * @param {Number} row The row index
63329      * @param {Number} col The column index
63330      * @param {Boolean} hscroll false to disable horizontal scrolling
63331      */
63332     focusCell : function(row, col, hscroll)
63333     {
63334         //Roo.log('GridView.focusCell');
63335         var el = this.ensureVisible(row, col, hscroll);
63336         this.focusEl.alignTo(el, "tl-tl");
63337         if(Roo.isGecko){
63338             this.focusEl.focus();
63339         }else{
63340             this.focusEl.focus.defer(1, this.focusEl);
63341         }
63342     },
63343
63344     /**
63345      * Scrolls the specified cell into view
63346      * @param {Number} row The row index
63347      * @param {Number} col The column index
63348      * @param {Boolean} hscroll false to disable horizontal scrolling
63349      */
63350     ensureVisible : function(row, col, hscroll)
63351     {
63352         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
63353         //return null; //disable for testing.
63354         if(typeof row != "number"){
63355             row = row.rowIndex;
63356         }
63357         if(row < 0 && row >= this.ds.getCount()){
63358             return  null;
63359         }
63360         col = (col !== undefined ? col : 0);
63361         var cm = this.grid.colModel;
63362         while(cm.isHidden(col)){
63363             col++;
63364         }
63365
63366         var el = this.getCell(row, col);
63367         if(!el){
63368             return null;
63369         }
63370         var c = this.scroller.dom;
63371
63372         var ctop = parseInt(el.offsetTop, 10);
63373         var cleft = parseInt(el.offsetLeft, 10);
63374         var cbot = ctop + el.offsetHeight;
63375         var cright = cleft + el.offsetWidth;
63376         
63377         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
63378         var stop = parseInt(c.scrollTop, 10);
63379         var sleft = parseInt(c.scrollLeft, 10);
63380         var sbot = stop + ch;
63381         var sright = sleft + c.clientWidth;
63382         /*
63383         Roo.log('GridView.ensureVisible:' +
63384                 ' ctop:' + ctop +
63385                 ' c.clientHeight:' + c.clientHeight +
63386                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
63387                 ' stop:' + stop +
63388                 ' cbot:' + cbot +
63389                 ' sbot:' + sbot +
63390                 ' ch:' + ch  
63391                 );
63392         */
63393         if(ctop < stop){
63394             c.scrollTop = ctop;
63395             //Roo.log("set scrolltop to ctop DISABLE?");
63396         }else if(cbot > sbot){
63397             //Roo.log("set scrolltop to cbot-ch");
63398             c.scrollTop = cbot-ch;
63399         }
63400         
63401         if(hscroll !== false){
63402             if(cleft < sleft){
63403                 c.scrollLeft = cleft;
63404             }else if(cright > sright){
63405                 c.scrollLeft = cright-c.clientWidth;
63406             }
63407         }
63408          
63409         return el;
63410     },
63411
63412     updateColumns : function(){
63413         this.grid.stopEditing();
63414         var cm = this.grid.colModel, colIds = this.getColumnIds();
63415         //var totalWidth = cm.getTotalWidth();
63416         var pos = 0;
63417         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63418             //if(cm.isHidden(i)) continue;
63419             var w = cm.getColumnWidth(i);
63420             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63421             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
63422         }
63423         this.updateSplitters();
63424     },
63425
63426     generateRules : function(cm){
63427         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
63428         Roo.util.CSS.removeStyleSheet(rulesId);
63429         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63430             var cid = cm.getColumnId(i);
63431             var align = '';
63432             if(cm.config[i].align){
63433                 align = 'text-align:'+cm.config[i].align+';';
63434             }
63435             var hidden = '';
63436             if(cm.isHidden(i)){
63437                 hidden = 'display:none;';
63438             }
63439             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
63440             ruleBuf.push(
63441                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
63442                     this.hdSelector, cid, " {\n", align, width, "}\n",
63443                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
63444                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
63445         }
63446         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
63447     },
63448
63449     updateSplitters : function(){
63450         var cm = this.cm, s = this.getSplitters();
63451         if(s){ // splitters not created yet
63452             var pos = 0, locked = true;
63453             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
63454                 if(cm.isHidden(i)) {
63455                     continue;
63456                 }
63457                 var w = cm.getColumnWidth(i); // make sure it's a number
63458                 if(!cm.isLocked(i) && locked){
63459                     pos = 0;
63460                     locked = false;
63461                 }
63462                 pos += w;
63463                 s[i].style.left = (pos-this.splitOffset) + "px";
63464             }
63465         }
63466     },
63467
63468     handleHiddenChange : function(colModel, colIndex, hidden){
63469         if(hidden){
63470             this.hideColumn(colIndex);
63471         }else{
63472             this.unhideColumn(colIndex);
63473         }
63474     },
63475
63476     hideColumn : function(colIndex){
63477         var cid = this.getColumnId(colIndex);
63478         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
63479         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
63480         if(Roo.isSafari){
63481             this.updateHeaders();
63482         }
63483         this.updateSplitters();
63484         this.layout();
63485     },
63486
63487     unhideColumn : function(colIndex){
63488         var cid = this.getColumnId(colIndex);
63489         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
63490         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
63491
63492         if(Roo.isSafari){
63493             this.updateHeaders();
63494         }
63495         this.updateSplitters();
63496         this.layout();
63497     },
63498
63499     insertRows : function(dm, firstRow, lastRow, isUpdate){
63500         if(firstRow == 0 && lastRow == dm.getCount()-1){
63501             this.refresh();
63502         }else{
63503             if(!isUpdate){
63504                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
63505             }
63506             var s = this.getScrollState();
63507             var markup = this.renderRows(firstRow, lastRow);
63508             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
63509             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
63510             this.restoreScroll(s);
63511             if(!isUpdate){
63512                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
63513                 this.syncRowHeights(firstRow, lastRow);
63514                 this.stripeRows(firstRow);
63515                 this.layout();
63516             }
63517         }
63518     },
63519
63520     bufferRows : function(markup, target, index){
63521         var before = null, trows = target.rows, tbody = target.tBodies[0];
63522         if(index < trows.length){
63523             before = trows[index];
63524         }
63525         var b = document.createElement("div");
63526         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
63527         var rows = b.firstChild.rows;
63528         for(var i = 0, len = rows.length; i < len; i++){
63529             if(before){
63530                 tbody.insertBefore(rows[0], before);
63531             }else{
63532                 tbody.appendChild(rows[0]);
63533             }
63534         }
63535         b.innerHTML = "";
63536         b = null;
63537     },
63538
63539     deleteRows : function(dm, firstRow, lastRow){
63540         if(dm.getRowCount()<1){
63541             this.fireEvent("beforerefresh", this);
63542             this.mainBody.update("");
63543             this.lockedBody.update("");
63544             this.fireEvent("refresh", this);
63545         }else{
63546             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
63547             var bt = this.getBodyTable();
63548             var tbody = bt.firstChild;
63549             var rows = bt.rows;
63550             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
63551                 tbody.removeChild(rows[firstRow]);
63552             }
63553             this.stripeRows(firstRow);
63554             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
63555         }
63556     },
63557
63558     updateRows : function(dataSource, firstRow, lastRow){
63559         var s = this.getScrollState();
63560         this.refresh();
63561         this.restoreScroll(s);
63562     },
63563
63564     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
63565         if(!noRefresh){
63566            this.refresh();
63567         }
63568         this.updateHeaderSortState();
63569     },
63570
63571     getScrollState : function(){
63572         
63573         var sb = this.scroller.dom;
63574         return {left: sb.scrollLeft, top: sb.scrollTop};
63575     },
63576
63577     stripeRows : function(startRow){
63578         if(!this.grid.stripeRows || this.ds.getCount() < 1){
63579             return;
63580         }
63581         startRow = startRow || 0;
63582         var rows = this.getBodyTable().rows;
63583         var lrows = this.getLockedTable().rows;
63584         var cls = ' x-grid-row-alt ';
63585         for(var i = startRow, len = rows.length; i < len; i++){
63586             var row = rows[i], lrow = lrows[i];
63587             var isAlt = ((i+1) % 2 == 0);
63588             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
63589             if(isAlt == hasAlt){
63590                 continue;
63591             }
63592             if(isAlt){
63593                 row.className += " x-grid-row-alt";
63594             }else{
63595                 row.className = row.className.replace("x-grid-row-alt", "");
63596             }
63597             if(lrow){
63598                 lrow.className = row.className;
63599             }
63600         }
63601     },
63602
63603     restoreScroll : function(state){
63604         //Roo.log('GridView.restoreScroll');
63605         var sb = this.scroller.dom;
63606         sb.scrollLeft = state.left;
63607         sb.scrollTop = state.top;
63608         this.syncScroll();
63609     },
63610
63611     syncScroll : function(){
63612         //Roo.log('GridView.syncScroll');
63613         var sb = this.scroller.dom;
63614         var sh = this.mainHd.dom;
63615         var bs = this.mainBody.dom;
63616         var lv = this.lockedBody.dom;
63617         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
63618         lv.scrollTop = bs.scrollTop = sb.scrollTop;
63619     },
63620
63621     handleScroll : function(e){
63622         this.syncScroll();
63623         var sb = this.scroller.dom;
63624         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
63625         e.stopEvent();
63626     },
63627
63628     handleWheel : function(e){
63629         var d = e.getWheelDelta();
63630         this.scroller.dom.scrollTop -= d*22;
63631         // set this here to prevent jumpy scrolling on large tables
63632         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
63633         e.stopEvent();
63634     },
63635
63636     renderRows : function(startRow, endRow){
63637         // pull in all the crap needed to render rows
63638         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
63639         var colCount = cm.getColumnCount();
63640
63641         if(ds.getCount() < 1){
63642             return ["", ""];
63643         }
63644
63645         // build a map for all the columns
63646         var cs = [];
63647         for(var i = 0; i < colCount; i++){
63648             var name = cm.getDataIndex(i);
63649             cs[i] = {
63650                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
63651                 renderer : cm.getRenderer(i),
63652                 id : cm.getColumnId(i),
63653                 locked : cm.isLocked(i),
63654                 has_editor : cm.isCellEditable(i)
63655             };
63656         }
63657
63658         startRow = startRow || 0;
63659         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
63660
63661         // records to render
63662         var rs = ds.getRange(startRow, endRow);
63663
63664         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
63665     },
63666
63667     // As much as I hate to duplicate code, this was branched because FireFox really hates
63668     // [].join("") on strings. The performance difference was substantial enough to
63669     // branch this function
63670     doRender : Roo.isGecko ?
63671             function(cs, rs, ds, startRow, colCount, stripe){
63672                 var ts = this.templates, ct = ts.cell, rt = ts.row;
63673                 // buffers
63674                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63675                 
63676                 var hasListener = this.grid.hasListener('rowclass');
63677                 var rowcfg = {};
63678                 for(var j = 0, len = rs.length; j < len; j++){
63679                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
63680                     for(var i = 0; i < colCount; i++){
63681                         c = cs[i];
63682                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63683                         p.id = c.id;
63684                         p.css = p.attr = "";
63685                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63686                         if(p.value == undefined || p.value === "") {
63687                             p.value = "&#160;";
63688                         }
63689                         if(c.has_editor){
63690                             p.css += ' x-grid-editable-cell';
63691                         }
63692                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
63693                             p.css +=  ' x-grid-dirty-cell';
63694                         }
63695                         var markup = ct.apply(p);
63696                         if(!c.locked){
63697                             cb+= markup;
63698                         }else{
63699                             lcb+= markup;
63700                         }
63701                     }
63702                     var alt = [];
63703                     if(stripe && ((rowIndex+1) % 2 == 0)){
63704                         alt.push("x-grid-row-alt")
63705                     }
63706                     if(r.dirty){
63707                         alt.push(  " x-grid-dirty-row");
63708                     }
63709                     rp.cells = lcb;
63710                     if(this.getRowClass){
63711                         alt.push(this.getRowClass(r, rowIndex));
63712                     }
63713                     if (hasListener) {
63714                         rowcfg = {
63715                              
63716                             record: r,
63717                             rowIndex : rowIndex,
63718                             rowClass : ''
63719                         };
63720                         this.grid.fireEvent('rowclass', this, rowcfg);
63721                         alt.push(rowcfg.rowClass);
63722                     }
63723                     rp.alt = alt.join(" ");
63724                     lbuf+= rt.apply(rp);
63725                     rp.cells = cb;
63726                     buf+=  rt.apply(rp);
63727                 }
63728                 return [lbuf, buf];
63729             } :
63730             function(cs, rs, ds, startRow, colCount, stripe){
63731                 var ts = this.templates, ct = ts.cell, rt = ts.row;
63732                 // buffers
63733                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
63734                 var hasListener = this.grid.hasListener('rowclass');
63735  
63736                 var rowcfg = {};
63737                 for(var j = 0, len = rs.length; j < len; j++){
63738                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
63739                     for(var i = 0; i < colCount; i++){
63740                         c = cs[i];
63741                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
63742                         p.id = c.id;
63743                         p.css = p.attr = "";
63744                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
63745                         if(p.value == undefined || p.value === "") {
63746                             p.value = "&#160;";
63747                         }
63748                         //Roo.log(c);
63749                          if(c.has_editor){
63750                             p.css += ' x-grid-editable-cell';
63751                         }
63752                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
63753                             p.css += ' x-grid-dirty-cell' 
63754                         }
63755                         
63756                         var markup = ct.apply(p);
63757                         if(!c.locked){
63758                             cb[cb.length] = markup;
63759                         }else{
63760                             lcb[lcb.length] = markup;
63761                         }
63762                     }
63763                     var alt = [];
63764                     if(stripe && ((rowIndex+1) % 2 == 0)){
63765                         alt.push( "x-grid-row-alt");
63766                     }
63767                     if(r.dirty){
63768                         alt.push(" x-grid-dirty-row");
63769                     }
63770                     rp.cells = lcb;
63771                     if(this.getRowClass){
63772                         alt.push( this.getRowClass(r, rowIndex));
63773                     }
63774                     if (hasListener) {
63775                         rowcfg = {
63776                              
63777                             record: r,
63778                             rowIndex : rowIndex,
63779                             rowClass : ''
63780                         };
63781                         this.grid.fireEvent('rowclass', this, rowcfg);
63782                         alt.push(rowcfg.rowClass);
63783                     }
63784                     
63785                     rp.alt = alt.join(" ");
63786                     rp.cells = lcb.join("");
63787                     lbuf[lbuf.length] = rt.apply(rp);
63788                     rp.cells = cb.join("");
63789                     buf[buf.length] =  rt.apply(rp);
63790                 }
63791                 return [lbuf.join(""), buf.join("")];
63792             },
63793
63794     renderBody : function(){
63795         var markup = this.renderRows();
63796         var bt = this.templates.body;
63797         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
63798     },
63799
63800     /**
63801      * Refreshes the grid
63802      * @param {Boolean} headersToo
63803      */
63804     refresh : function(headersToo){
63805         this.fireEvent("beforerefresh", this);
63806         this.grid.stopEditing();
63807         var result = this.renderBody();
63808         this.lockedBody.update(result[0]);
63809         this.mainBody.update(result[1]);
63810         if(headersToo === true){
63811             this.updateHeaders();
63812             this.updateColumns();
63813             this.updateSplitters();
63814             this.updateHeaderSortState();
63815         }
63816         this.syncRowHeights();
63817         this.layout();
63818         this.fireEvent("refresh", this);
63819     },
63820
63821     handleColumnMove : function(cm, oldIndex, newIndex){
63822         this.indexMap = null;
63823         var s = this.getScrollState();
63824         this.refresh(true);
63825         this.restoreScroll(s);
63826         this.afterMove(newIndex);
63827     },
63828
63829     afterMove : function(colIndex){
63830         if(this.enableMoveAnim && Roo.enableFx){
63831             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
63832         }
63833         // if multisort - fix sortOrder, and reload..
63834         if (this.grid.dataSource.multiSort) {
63835             // the we can call sort again..
63836             var dm = this.grid.dataSource;
63837             var cm = this.grid.colModel;
63838             var so = [];
63839             for(var i = 0; i < cm.config.length; i++ ) {
63840                 
63841                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
63842                     continue; // dont' bother, it's not in sort list or being set.
63843                 }
63844                 
63845                 so.push(cm.config[i].dataIndex);
63846             };
63847             dm.sortOrder = so;
63848             dm.load(dm.lastOptions);
63849             
63850             
63851         }
63852         
63853     },
63854
63855     updateCell : function(dm, rowIndex, dataIndex){
63856         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
63857         if(typeof colIndex == "undefined"){ // not present in grid
63858             return;
63859         }
63860         var cm = this.grid.colModel;
63861         var cell = this.getCell(rowIndex, colIndex);
63862         var cellText = this.getCellText(rowIndex, colIndex);
63863
63864         var p = {
63865             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
63866             id : cm.getColumnId(colIndex),
63867             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
63868         };
63869         var renderer = cm.getRenderer(colIndex);
63870         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
63871         if(typeof val == "undefined" || val === "") {
63872             val = "&#160;";
63873         }
63874         cellText.innerHTML = val;
63875         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
63876         this.syncRowHeights(rowIndex, rowIndex);
63877     },
63878
63879     calcColumnWidth : function(colIndex, maxRowsToMeasure){
63880         var maxWidth = 0;
63881         if(this.grid.autoSizeHeaders){
63882             var h = this.getHeaderCellMeasure(colIndex);
63883             maxWidth = Math.max(maxWidth, h.scrollWidth);
63884         }
63885         var tb, index;
63886         if(this.cm.isLocked(colIndex)){
63887             tb = this.getLockedTable();
63888             index = colIndex;
63889         }else{
63890             tb = this.getBodyTable();
63891             index = colIndex - this.cm.getLockedCount();
63892         }
63893         if(tb && tb.rows){
63894             var rows = tb.rows;
63895             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
63896             for(var i = 0; i < stopIndex; i++){
63897                 var cell = rows[i].childNodes[index].firstChild;
63898                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
63899             }
63900         }
63901         return maxWidth + /*margin for error in IE*/ 5;
63902     },
63903     /**
63904      * Autofit a column to its content.
63905      * @param {Number} colIndex
63906      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
63907      */
63908      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
63909          if(this.cm.isHidden(colIndex)){
63910              return; // can't calc a hidden column
63911          }
63912         if(forceMinSize){
63913             var cid = this.cm.getColumnId(colIndex);
63914             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
63915            if(this.grid.autoSizeHeaders){
63916                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
63917            }
63918         }
63919         var newWidth = this.calcColumnWidth(colIndex);
63920         this.cm.setColumnWidth(colIndex,
63921             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
63922         if(!suppressEvent){
63923             this.grid.fireEvent("columnresize", colIndex, newWidth);
63924         }
63925     },
63926
63927     /**
63928      * Autofits all columns to their content and then expands to fit any extra space in the grid
63929      */
63930      autoSizeColumns : function(){
63931         var cm = this.grid.colModel;
63932         var colCount = cm.getColumnCount();
63933         for(var i = 0; i < colCount; i++){
63934             this.autoSizeColumn(i, true, true);
63935         }
63936         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
63937             this.fitColumns();
63938         }else{
63939             this.updateColumns();
63940             this.layout();
63941         }
63942     },
63943
63944     /**
63945      * Autofits all columns to the grid's width proportionate with their current size
63946      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
63947      */
63948     fitColumns : function(reserveScrollSpace){
63949         var cm = this.grid.colModel;
63950         var colCount = cm.getColumnCount();
63951         var cols = [];
63952         var width = 0;
63953         var i, w;
63954         for (i = 0; i < colCount; i++){
63955             if(!cm.isHidden(i) && !cm.isFixed(i)){
63956                 w = cm.getColumnWidth(i);
63957                 cols.push(i);
63958                 cols.push(w);
63959                 width += w;
63960             }
63961         }
63962         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
63963         if(reserveScrollSpace){
63964             avail -= 17;
63965         }
63966         var frac = (avail - cm.getTotalWidth())/width;
63967         while (cols.length){
63968             w = cols.pop();
63969             i = cols.pop();
63970             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
63971         }
63972         this.updateColumns();
63973         this.layout();
63974     },
63975
63976     onRowSelect : function(rowIndex){
63977         var row = this.getRowComposite(rowIndex);
63978         row.addClass("x-grid-row-selected");
63979     },
63980
63981     onRowDeselect : function(rowIndex){
63982         var row = this.getRowComposite(rowIndex);
63983         row.removeClass("x-grid-row-selected");
63984     },
63985
63986     onCellSelect : function(row, col){
63987         var cell = this.getCell(row, col);
63988         if(cell){
63989             Roo.fly(cell).addClass("x-grid-cell-selected");
63990         }
63991     },
63992
63993     onCellDeselect : function(row, col){
63994         var cell = this.getCell(row, col);
63995         if(cell){
63996             Roo.fly(cell).removeClass("x-grid-cell-selected");
63997         }
63998     },
63999
64000     updateHeaderSortState : function(){
64001         
64002         // sort state can be single { field: xxx, direction : yyy}
64003         // or   { xxx=>ASC , yyy : DESC ..... }
64004         
64005         var mstate = {};
64006         if (!this.ds.multiSort) { 
64007             var state = this.ds.getSortState();
64008             if(!state){
64009                 return;
64010             }
64011             mstate[state.field] = state.direction;
64012             // FIXME... - this is not used here.. but might be elsewhere..
64013             this.sortState = state;
64014             
64015         } else {
64016             mstate = this.ds.sortToggle;
64017         }
64018         //remove existing sort classes..
64019         
64020         var sc = this.sortClasses;
64021         var hds = this.el.select(this.headerSelector).removeClass(sc);
64022         
64023         for(var f in mstate) {
64024         
64025             var sortColumn = this.cm.findColumnIndex(f);
64026             
64027             if(sortColumn != -1){
64028                 var sortDir = mstate[f];        
64029                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
64030             }
64031         }
64032         
64033          
64034         
64035     },
64036
64037
64038     handleHeaderClick : function(g, index,e){
64039         
64040         Roo.log("header click");
64041         
64042         if (Roo.isTouch) {
64043             // touch events on header are handled by context
64044             this.handleHdCtx(g,index,e);
64045             return;
64046         }
64047         
64048         
64049         if(this.headersDisabled){
64050             return;
64051         }
64052         var dm = g.dataSource, cm = g.colModel;
64053         if(!cm.isSortable(index)){
64054             return;
64055         }
64056         g.stopEditing();
64057         
64058         if (dm.multiSort) {
64059             // update the sortOrder
64060             var so = [];
64061             for(var i = 0; i < cm.config.length; i++ ) {
64062                 
64063                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
64064                     continue; // dont' bother, it's not in sort list or being set.
64065                 }
64066                 
64067                 so.push(cm.config[i].dataIndex);
64068             };
64069             dm.sortOrder = so;
64070         }
64071         
64072         
64073         dm.sort(cm.getDataIndex(index));
64074     },
64075
64076
64077     destroy : function(){
64078         if(this.colMenu){
64079             this.colMenu.removeAll();
64080             Roo.menu.MenuMgr.unregister(this.colMenu);
64081             this.colMenu.getEl().remove();
64082             delete this.colMenu;
64083         }
64084         if(this.hmenu){
64085             this.hmenu.removeAll();
64086             Roo.menu.MenuMgr.unregister(this.hmenu);
64087             this.hmenu.getEl().remove();
64088             delete this.hmenu;
64089         }
64090         if(this.grid.enableColumnMove){
64091             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
64092             if(dds){
64093                 for(var dd in dds){
64094                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
64095                         var elid = dds[dd].dragElId;
64096                         dds[dd].unreg();
64097                         Roo.get(elid).remove();
64098                     } else if(dds[dd].config.isTarget){
64099                         dds[dd].proxyTop.remove();
64100                         dds[dd].proxyBottom.remove();
64101                         dds[dd].unreg();
64102                     }
64103                     if(Roo.dd.DDM.locationCache[dd]){
64104                         delete Roo.dd.DDM.locationCache[dd];
64105                     }
64106                 }
64107                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
64108             }
64109         }
64110         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
64111         this.bind(null, null);
64112         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
64113     },
64114
64115     handleLockChange : function(){
64116         this.refresh(true);
64117     },
64118
64119     onDenyColumnLock : function(){
64120
64121     },
64122
64123     onDenyColumnHide : function(){
64124
64125     },
64126
64127     handleHdMenuClick : function(item){
64128         var index = this.hdCtxIndex;
64129         var cm = this.cm, ds = this.ds;
64130         switch(item.id){
64131             case "asc":
64132                 ds.sort(cm.getDataIndex(index), "ASC");
64133                 break;
64134             case "desc":
64135                 ds.sort(cm.getDataIndex(index), "DESC");
64136                 break;
64137             case "lock":
64138                 var lc = cm.getLockedCount();
64139                 if(cm.getColumnCount(true) <= lc+1){
64140                     this.onDenyColumnLock();
64141                     return;
64142                 }
64143                 if(lc != index){
64144                     cm.setLocked(index, true, true);
64145                     cm.moveColumn(index, lc);
64146                     this.grid.fireEvent("columnmove", index, lc);
64147                 }else{
64148                     cm.setLocked(index, true);
64149                 }
64150             break;
64151             case "unlock":
64152                 var lc = cm.getLockedCount();
64153                 if((lc-1) != index){
64154                     cm.setLocked(index, false, true);
64155                     cm.moveColumn(index, lc-1);
64156                     this.grid.fireEvent("columnmove", index, lc-1);
64157                 }else{
64158                     cm.setLocked(index, false);
64159                 }
64160             break;
64161             case 'wider': // used to expand cols on touch..
64162             case 'narrow':
64163                 var cw = cm.getColumnWidth(index);
64164                 cw += (item.id == 'wider' ? 1 : -1) * 50;
64165                 cw = Math.max(0, cw);
64166                 cw = Math.min(cw,4000);
64167                 cm.setColumnWidth(index, cw);
64168                 break;
64169                 
64170             default:
64171                 index = cm.getIndexById(item.id.substr(4));
64172                 if(index != -1){
64173                     if(item.checked && cm.getColumnCount(true) <= 1){
64174                         this.onDenyColumnHide();
64175                         return false;
64176                     }
64177                     cm.setHidden(index, item.checked);
64178                 }
64179         }
64180         return true;
64181     },
64182
64183     beforeColMenuShow : function(){
64184         var cm = this.cm,  colCount = cm.getColumnCount();
64185         this.colMenu.removeAll();
64186         
64187         var items = [];
64188         for(var i = 0; i < colCount; i++){
64189             items.push({
64190                 id: "col-"+cm.getColumnId(i),
64191                 text: cm.getColumnHeader(i),
64192                 checked: !cm.isHidden(i),
64193                 hideOnClick:false
64194             });
64195         }
64196         
64197         if (this.grid.sortColMenu) {
64198             items.sort(function(a,b) {
64199                 if (a.text == b.text) {
64200                     return 0;
64201                 }
64202                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
64203             });
64204         }
64205         
64206         for(var i = 0; i < colCount; i++){
64207             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
64208         }
64209     },
64210
64211     handleHdCtx : function(g, index, e){
64212         e.stopEvent();
64213         var hd = this.getHeaderCell(index);
64214         this.hdCtxIndex = index;
64215         var ms = this.hmenu.items, cm = this.cm;
64216         ms.get("asc").setDisabled(!cm.isSortable(index));
64217         ms.get("desc").setDisabled(!cm.isSortable(index));
64218         if(this.grid.enableColLock !== false){
64219             ms.get("lock").setDisabled(cm.isLocked(index));
64220             ms.get("unlock").setDisabled(!cm.isLocked(index));
64221         }
64222         this.hmenu.show(hd, "tl-bl");
64223     },
64224
64225     handleHdOver : function(e){
64226         var hd = this.findHeaderCell(e.getTarget());
64227         if(hd && !this.headersDisabled){
64228             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
64229                this.fly(hd).addClass("x-grid-hd-over");
64230             }
64231         }
64232     },
64233
64234     handleHdOut : function(e){
64235         var hd = this.findHeaderCell(e.getTarget());
64236         if(hd){
64237             this.fly(hd).removeClass("x-grid-hd-over");
64238         }
64239     },
64240
64241     handleSplitDblClick : function(e, t){
64242         var i = this.getCellIndex(t);
64243         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
64244             this.autoSizeColumn(i, true);
64245             this.layout();
64246         }
64247     },
64248
64249     render : function(){
64250
64251         var cm = this.cm;
64252         var colCount = cm.getColumnCount();
64253
64254         if(this.grid.monitorWindowResize === true){
64255             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
64256         }
64257         var header = this.renderHeaders();
64258         var body = this.templates.body.apply({rows:""});
64259         var html = this.templates.master.apply({
64260             lockedBody: body,
64261             body: body,
64262             lockedHeader: header[0],
64263             header: header[1]
64264         });
64265
64266         //this.updateColumns();
64267
64268         this.grid.getGridEl().dom.innerHTML = html;
64269
64270         this.initElements();
64271         
64272         // a kludge to fix the random scolling effect in webkit
64273         this.el.on("scroll", function() {
64274             this.el.dom.scrollTop=0; // hopefully not recursive..
64275         },this);
64276
64277         this.scroller.on("scroll", this.handleScroll, this);
64278         this.lockedBody.on("mousewheel", this.handleWheel, this);
64279         this.mainBody.on("mousewheel", this.handleWheel, this);
64280
64281         this.mainHd.on("mouseover", this.handleHdOver, this);
64282         this.mainHd.on("mouseout", this.handleHdOut, this);
64283         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
64284                 {delegate: "."+this.splitClass});
64285
64286         this.lockedHd.on("mouseover", this.handleHdOver, this);
64287         this.lockedHd.on("mouseout", this.handleHdOut, this);
64288         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
64289                 {delegate: "."+this.splitClass});
64290
64291         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
64292             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64293         }
64294
64295         this.updateSplitters();
64296
64297         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
64298             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64299             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
64300         }
64301
64302         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
64303             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
64304             this.hmenu.add(
64305                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
64306                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
64307             );
64308             if(this.grid.enableColLock !== false){
64309                 this.hmenu.add('-',
64310                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
64311                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
64312                 );
64313             }
64314             if (Roo.isTouch) {
64315                  this.hmenu.add('-',
64316                     {id:"wider", text: this.columnsWiderText},
64317                     {id:"narrow", text: this.columnsNarrowText }
64318                 );
64319                 
64320                  
64321             }
64322             
64323             if(this.grid.enableColumnHide !== false){
64324
64325                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
64326                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
64327                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
64328
64329                 this.hmenu.add('-',
64330                     {id:"columns", text: this.columnsText, menu: this.colMenu}
64331                 );
64332             }
64333             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
64334
64335             this.grid.on("headercontextmenu", this.handleHdCtx, this);
64336         }
64337
64338         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
64339             this.dd = new Roo.grid.GridDragZone(this.grid, {
64340                 ddGroup : this.grid.ddGroup || 'GridDD'
64341             });
64342             
64343         }
64344
64345         /*
64346         for(var i = 0; i < colCount; i++){
64347             if(cm.isHidden(i)){
64348                 this.hideColumn(i);
64349             }
64350             if(cm.config[i].align){
64351                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
64352                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
64353             }
64354         }*/
64355         
64356         this.updateHeaderSortState();
64357
64358         this.beforeInitialResize();
64359         this.layout(true);
64360
64361         // two part rendering gives faster view to the user
64362         this.renderPhase2.defer(1, this);
64363     },
64364
64365     renderPhase2 : function(){
64366         // render the rows now
64367         this.refresh();
64368         if(this.grid.autoSizeColumns){
64369             this.autoSizeColumns();
64370         }
64371     },
64372
64373     beforeInitialResize : function(){
64374
64375     },
64376
64377     onColumnSplitterMoved : function(i, w){
64378         this.userResized = true;
64379         var cm = this.grid.colModel;
64380         cm.setColumnWidth(i, w, true);
64381         var cid = cm.getColumnId(i);
64382         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
64383         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
64384         this.updateSplitters();
64385         this.layout();
64386         this.grid.fireEvent("columnresize", i, w);
64387     },
64388
64389     syncRowHeights : function(startIndex, endIndex){
64390         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
64391             startIndex = startIndex || 0;
64392             var mrows = this.getBodyTable().rows;
64393             var lrows = this.getLockedTable().rows;
64394             var len = mrows.length-1;
64395             endIndex = Math.min(endIndex || len, len);
64396             for(var i = startIndex; i <= endIndex; i++){
64397                 var m = mrows[i], l = lrows[i];
64398                 var h = Math.max(m.offsetHeight, l.offsetHeight);
64399                 m.style.height = l.style.height = h + "px";
64400             }
64401         }
64402     },
64403
64404     layout : function(initialRender, is2ndPass)
64405     {
64406         var g = this.grid;
64407         var auto = g.autoHeight;
64408         var scrollOffset = 16;
64409         var c = g.getGridEl(), cm = this.cm,
64410                 expandCol = g.autoExpandColumn,
64411                 gv = this;
64412         //c.beginMeasure();
64413
64414         if(!c.dom.offsetWidth){ // display:none?
64415             if(initialRender){
64416                 this.lockedWrap.show();
64417                 this.mainWrap.show();
64418             }
64419             return;
64420         }
64421
64422         var hasLock = this.cm.isLocked(0);
64423
64424         var tbh = this.headerPanel.getHeight();
64425         var bbh = this.footerPanel.getHeight();
64426
64427         if(auto){
64428             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
64429             var newHeight = ch + c.getBorderWidth("tb");
64430             if(g.maxHeight){
64431                 newHeight = Math.min(g.maxHeight, newHeight);
64432             }
64433             c.setHeight(newHeight);
64434         }
64435
64436         if(g.autoWidth){
64437             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
64438         }
64439
64440         var s = this.scroller;
64441
64442         var csize = c.getSize(true);
64443
64444         this.el.setSize(csize.width, csize.height);
64445
64446         this.headerPanel.setWidth(csize.width);
64447         this.footerPanel.setWidth(csize.width);
64448
64449         var hdHeight = this.mainHd.getHeight();
64450         var vw = csize.width;
64451         var vh = csize.height - (tbh + bbh);
64452
64453         s.setSize(vw, vh);
64454
64455         var bt = this.getBodyTable();
64456         
64457         if(cm.getLockedCount() == cm.config.length){
64458             bt = this.getLockedTable();
64459         }
64460         
64461         var ltWidth = hasLock ?
64462                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
64463
64464         var scrollHeight = bt.offsetHeight;
64465         var scrollWidth = ltWidth + bt.offsetWidth;
64466         var vscroll = false, hscroll = false;
64467
64468         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
64469
64470         var lw = this.lockedWrap, mw = this.mainWrap;
64471         var lb = this.lockedBody, mb = this.mainBody;
64472
64473         setTimeout(function(){
64474             var t = s.dom.offsetTop;
64475             var w = s.dom.clientWidth,
64476                 h = s.dom.clientHeight;
64477
64478             lw.setTop(t);
64479             lw.setSize(ltWidth, h);
64480
64481             mw.setLeftTop(ltWidth, t);
64482             mw.setSize(w-ltWidth, h);
64483
64484             lb.setHeight(h-hdHeight);
64485             mb.setHeight(h-hdHeight);
64486
64487             if(is2ndPass !== true && !gv.userResized && expandCol){
64488                 // high speed resize without full column calculation
64489                 
64490                 var ci = cm.getIndexById(expandCol);
64491                 if (ci < 0) {
64492                     ci = cm.findColumnIndex(expandCol);
64493                 }
64494                 ci = Math.max(0, ci); // make sure it's got at least the first col.
64495                 var expandId = cm.getColumnId(ci);
64496                 var  tw = cm.getTotalWidth(false);
64497                 var currentWidth = cm.getColumnWidth(ci);
64498                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
64499                 if(currentWidth != cw){
64500                     cm.setColumnWidth(ci, cw, true);
64501                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64502                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
64503                     gv.updateSplitters();
64504                     gv.layout(false, true);
64505                 }
64506             }
64507
64508             if(initialRender){
64509                 lw.show();
64510                 mw.show();
64511             }
64512             //c.endMeasure();
64513         }, 10);
64514     },
64515
64516     onWindowResize : function(){
64517         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
64518             return;
64519         }
64520         this.layout();
64521     },
64522
64523     appendFooter : function(parentEl){
64524         return null;
64525     },
64526
64527     sortAscText : "Sort Ascending",
64528     sortDescText : "Sort Descending",
64529     lockText : "Lock Column",
64530     unlockText : "Unlock Column",
64531     columnsText : "Columns",
64532  
64533     columnsWiderText : "Wider",
64534     columnsNarrowText : "Thinner"
64535 });
64536
64537
64538 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
64539     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
64540     this.proxy.el.addClass('x-grid3-col-dd');
64541 };
64542
64543 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
64544     handleMouseDown : function(e){
64545
64546     },
64547
64548     callHandleMouseDown : function(e){
64549         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
64550     }
64551 });
64552 /*
64553  * Based on:
64554  * Ext JS Library 1.1.1
64555  * Copyright(c) 2006-2007, Ext JS, LLC.
64556  *
64557  * Originally Released Under LGPL - original licence link has changed is not relivant.
64558  *
64559  * Fork - LGPL
64560  * <script type="text/javascript">
64561  */
64562  /**
64563  * @extends Roo.dd.DDProxy
64564  * @class Roo.grid.SplitDragZone
64565  * Support for Column Header resizing
64566  * @constructor
64567  * @param {Object} config
64568  */
64569 // private
64570 // This is a support class used internally by the Grid components
64571 Roo.grid.SplitDragZone = function(grid, hd, hd2){
64572     this.grid = grid;
64573     this.view = grid.getView();
64574     this.proxy = this.view.resizeProxy;
64575     Roo.grid.SplitDragZone.superclass.constructor.call(
64576         this,
64577         hd, // ID
64578         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
64579         {  // CONFIG
64580             dragElId : Roo.id(this.proxy.dom),
64581             resizeFrame:false
64582         }
64583     );
64584     
64585     this.setHandleElId(Roo.id(hd));
64586     if (hd2 !== false) {
64587         this.setOuterHandleElId(Roo.id(hd2));
64588     }
64589     
64590     this.scroll = false;
64591 };
64592 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
64593     fly: Roo.Element.fly,
64594
64595     b4StartDrag : function(x, y){
64596         this.view.headersDisabled = true;
64597         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
64598                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
64599         );
64600         this.proxy.setHeight(h);
64601         
64602         // for old system colWidth really stored the actual width?
64603         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
64604         // which in reality did not work.. - it worked only for fixed sizes
64605         // for resizable we need to use actual sizes.
64606         var w = this.cm.getColumnWidth(this.cellIndex);
64607         if (!this.view.mainWrap) {
64608             // bootstrap.
64609             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
64610         }
64611         
64612         
64613         
64614         // this was w-this.grid.minColumnWidth;
64615         // doesnt really make sense? - w = thie curren width or the rendered one?
64616         var minw = Math.max(w-this.grid.minColumnWidth, 0);
64617         this.resetConstraints();
64618         this.setXConstraint(minw, 1000);
64619         this.setYConstraint(0, 0);
64620         this.minX = x - minw;
64621         this.maxX = x + 1000;
64622         this.startPos = x;
64623         if (!this.view.mainWrap) { // this is Bootstrap code..
64624             this.getDragEl().style.display='block';
64625         }
64626         
64627         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
64628     },
64629
64630
64631     handleMouseDown : function(e){
64632         ev = Roo.EventObject.setEvent(e);
64633         var t = this.fly(ev.getTarget());
64634         if(t.hasClass("x-grid-split")){
64635             this.cellIndex = this.view.getCellIndex(t.dom);
64636             this.split = t.dom;
64637             this.cm = this.grid.colModel;
64638             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
64639                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
64640             }
64641         }
64642     },
64643
64644     endDrag : function(e){
64645         this.view.headersDisabled = false;
64646         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
64647         var diff = endX - this.startPos;
64648         // 
64649         var w = this.cm.getColumnWidth(this.cellIndex);
64650         if (!this.view.mainWrap) {
64651             w = 0;
64652         }
64653         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
64654     },
64655
64656     autoOffset : function(){
64657         this.setDelta(0,0);
64658     }
64659 });/*
64660  * Based on:
64661  * Ext JS Library 1.1.1
64662  * Copyright(c) 2006-2007, Ext JS, LLC.
64663  *
64664  * Originally Released Under LGPL - original licence link has changed is not relivant.
64665  *
64666  * Fork - LGPL
64667  * <script type="text/javascript">
64668  */
64669  
64670 // private
64671 // This is a support class used internally by the Grid components
64672 Roo.grid.GridDragZone = function(grid, config){
64673     this.view = grid.getView();
64674     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
64675     if(this.view.lockedBody){
64676         this.setHandleElId(Roo.id(this.view.mainBody.dom));
64677         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
64678     }
64679     this.scroll = false;
64680     this.grid = grid;
64681     this.ddel = document.createElement('div');
64682     this.ddel.className = 'x-grid-dd-wrap';
64683 };
64684
64685 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
64686     ddGroup : "GridDD",
64687
64688     getDragData : function(e){
64689         var t = Roo.lib.Event.getTarget(e);
64690         var rowIndex = this.view.findRowIndex(t);
64691         var sm = this.grid.selModel;
64692             
64693         //Roo.log(rowIndex);
64694         
64695         if (sm.getSelectedCell) {
64696             // cell selection..
64697             if (!sm.getSelectedCell()) {
64698                 return false;
64699             }
64700             if (rowIndex != sm.getSelectedCell()[0]) {
64701                 return false;
64702             }
64703         
64704         }
64705         if (sm.getSelections && sm.getSelections().length < 1) {
64706             return false;
64707         }
64708         
64709         
64710         // before it used to all dragging of unseleted... - now we dont do that.
64711         if(rowIndex !== false){
64712             
64713             // if editorgrid.. 
64714             
64715             
64716             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
64717                
64718             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
64719               //  
64720             //}
64721             if (e.hasModifier()){
64722                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
64723             }
64724             
64725             Roo.log("getDragData");
64726             
64727             return {
64728                 grid: this.grid,
64729                 ddel: this.ddel,
64730                 rowIndex: rowIndex,
64731                 selections: sm.getSelections ? sm.getSelections() : (
64732                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
64733             };
64734         }
64735         return false;
64736     },
64737     
64738     
64739     onInitDrag : function(e){
64740         var data = this.dragData;
64741         this.ddel.innerHTML = this.grid.getDragDropText();
64742         this.proxy.update(this.ddel);
64743         // fire start drag?
64744     },
64745
64746     afterRepair : function(){
64747         this.dragging = false;
64748     },
64749
64750     getRepairXY : function(e, data){
64751         return false;
64752     },
64753
64754     onEndDrag : function(data, e){
64755         // fire end drag?
64756     },
64757
64758     onValidDrop : function(dd, e, id){
64759         // fire drag drop?
64760         this.hideProxy();
64761     },
64762
64763     beforeInvalidDrop : function(e, id){
64764
64765     }
64766 });/*
64767  * Based on:
64768  * Ext JS Library 1.1.1
64769  * Copyright(c) 2006-2007, Ext JS, LLC.
64770  *
64771  * Originally Released Under LGPL - original licence link has changed is not relivant.
64772  *
64773  * Fork - LGPL
64774  * <script type="text/javascript">
64775  */
64776  
64777
64778 /**
64779  * @class Roo.grid.ColumnModel
64780  * @extends Roo.util.Observable
64781  * This is the default implementation of a ColumnModel used by the Grid. It defines
64782  * the columns in the grid.
64783  * <br>Usage:<br>
64784  <pre><code>
64785  var colModel = new Roo.grid.ColumnModel([
64786         {header: "Ticker", width: 60, sortable: true, locked: true},
64787         {header: "Company Name", width: 150, sortable: true},
64788         {header: "Market Cap.", width: 100, sortable: true},
64789         {header: "$ Sales", width: 100, sortable: true, renderer: money},
64790         {header: "Employees", width: 100, sortable: true, resizable: false}
64791  ]);
64792  </code></pre>
64793  * <p>
64794  
64795  * The config options listed for this class are options which may appear in each
64796  * individual column definition.
64797  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
64798  * @constructor
64799  * @param {Object} config An Array of column config objects. See this class's
64800  * config objects for details.
64801 */
64802 Roo.grid.ColumnModel = function(config){
64803         /**
64804      * The config passed into the constructor
64805      */
64806     this.config = []; //config;
64807     this.lookup = {};
64808
64809     // if no id, create one
64810     // if the column does not have a dataIndex mapping,
64811     // map it to the order it is in the config
64812     for(var i = 0, len = config.length; i < len; i++){
64813         this.addColumn(config[i]);
64814         
64815     }
64816
64817     /**
64818      * The width of columns which have no width specified (defaults to 100)
64819      * @type Number
64820      */
64821     this.defaultWidth = 100;
64822
64823     /**
64824      * Default sortable of columns which have no sortable specified (defaults to false)
64825      * @type Boolean
64826      */
64827     this.defaultSortable = false;
64828
64829     this.addEvents({
64830         /**
64831              * @event widthchange
64832              * Fires when the width of a column changes.
64833              * @param {ColumnModel} this
64834              * @param {Number} columnIndex The column index
64835              * @param {Number} newWidth The new width
64836              */
64837             "widthchange": true,
64838         /**
64839              * @event headerchange
64840              * Fires when the text of a header changes.
64841              * @param {ColumnModel} this
64842              * @param {Number} columnIndex The column index
64843              * @param {Number} newText The new header text
64844              */
64845             "headerchange": true,
64846         /**
64847              * @event hiddenchange
64848              * Fires when a column is hidden or "unhidden".
64849              * @param {ColumnModel} this
64850              * @param {Number} columnIndex The column index
64851              * @param {Boolean} hidden true if hidden, false otherwise
64852              */
64853             "hiddenchange": true,
64854             /**
64855          * @event columnmoved
64856          * Fires when a column is moved.
64857          * @param {ColumnModel} this
64858          * @param {Number} oldIndex
64859          * @param {Number} newIndex
64860          */
64861         "columnmoved" : true,
64862         /**
64863          * @event columlockchange
64864          * Fires when a column's locked state is changed
64865          * @param {ColumnModel} this
64866          * @param {Number} colIndex
64867          * @param {Boolean} locked true if locked
64868          */
64869         "columnlockchange" : true
64870     });
64871     Roo.grid.ColumnModel.superclass.constructor.call(this);
64872 };
64873 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
64874     /**
64875      * @cfg {String} header [required] The header text to display in the Grid view.
64876      */
64877         /**
64878      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
64879      */
64880         /**
64881      * @cfg {String} smHeader Header at Bootsrap Small width
64882      */
64883         /**
64884      * @cfg {String} mdHeader Header at Bootsrap Medium width
64885      */
64886         /**
64887      * @cfg {String} lgHeader Header at Bootsrap Large width
64888      */
64889         /**
64890      * @cfg {String} xlHeader Header at Bootsrap extra Large width
64891      */
64892     /**
64893      * @cfg {String} dataIndex  The name of the field in the grid's {@link Roo.data.Store}'s
64894      * {@link Roo.data.Record} definition from which to draw the column's value. If not
64895      * specified, the column's index is used as an index into the Record's data Array.
64896      */
64897     /**
64898      * @cfg {Number} width  The initial width in pixels of the column. Using this
64899      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
64900      */
64901     /**
64902      * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
64903      * Defaults to the value of the {@link #defaultSortable} property.
64904      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
64905      */
64906     /**
64907      * @cfg {Boolean} locked  True to lock the column in place while scrolling the Grid.  Defaults to false.
64908      */
64909     /**
64910      * @cfg {Boolean} fixed  True if the column width cannot be changed.  Defaults to false.
64911      */
64912     /**
64913      * @cfg {Boolean} resizable  False to disable column resizing. Defaults to true.
64914      */
64915     /**
64916      * @cfg {Boolean} hidden  True to hide the column. Defaults to false.
64917      */
64918     /**
64919      * @cfg {Function} renderer A function used to generate HTML markup for a cell
64920      * given the cell's data value. See {@link #setRenderer}. If not specified, the
64921      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
64922      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
64923      */
64924        /**
64925      * @cfg {Roo.grid.GridEditor} editor  For grid editors - returns the grid editor 
64926      */
64927     /**
64928      * @cfg {String} align (left|right) Set the CSS text-align property of the column.  Defaults to undefined (left).
64929      */
64930     /**
64931      * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined (middle)
64932      */
64933     /**
64934      * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
64935      */
64936     /**
64937      * @cfg {String} tooltip mouse over tooltip text
64938      */
64939     /**
64940      * @cfg {Number} xs  can be '0' for hidden at this size (number less than 12)
64941      */
64942     /**
64943      * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
64944      */
64945     /**
64946      * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
64947      */
64948     /**
64949      * @cfg {Number} lg   can be '0' for hidden at this size (number less than 12)
64950      */
64951         /**
64952      * @cfg {Number} xl   can be '0' for hidden at this size (number less than 12)
64953      */
64954     /**
64955      * Returns the id of the column at the specified index.
64956      * @param {Number} index The column index
64957      * @return {String} the id
64958      */
64959     getColumnId : function(index){
64960         return this.config[index].id;
64961     },
64962
64963     /**
64964      * Returns the column for a specified id.
64965      * @param {String} id The column id
64966      * @return {Object} the column
64967      */
64968     getColumnById : function(id){
64969         return this.lookup[id];
64970     },
64971
64972     
64973     /**
64974      * Returns the column Object for a specified dataIndex.
64975      * @param {String} dataIndex The column dataIndex
64976      * @return {Object|Boolean} the column or false if not found
64977      */
64978     getColumnByDataIndex: function(dataIndex){
64979         var index = this.findColumnIndex(dataIndex);
64980         return index > -1 ? this.config[index] : false;
64981     },
64982     
64983     /**
64984      * Returns the index for a specified column id.
64985      * @param {String} id The column id
64986      * @return {Number} the index, or -1 if not found
64987      */
64988     getIndexById : function(id){
64989         for(var i = 0, len = this.config.length; i < len; i++){
64990             if(this.config[i].id == id){
64991                 return i;
64992             }
64993         }
64994         return -1;
64995     },
64996     
64997     /**
64998      * Returns the index for a specified column dataIndex.
64999      * @param {String} dataIndex The column dataIndex
65000      * @return {Number} the index, or -1 if not found
65001      */
65002     
65003     findColumnIndex : function(dataIndex){
65004         for(var i = 0, len = this.config.length; i < len; i++){
65005             if(this.config[i].dataIndex == dataIndex){
65006                 return i;
65007             }
65008         }
65009         return -1;
65010     },
65011     
65012     
65013     moveColumn : function(oldIndex, newIndex){
65014         var c = this.config[oldIndex];
65015         this.config.splice(oldIndex, 1);
65016         this.config.splice(newIndex, 0, c);
65017         this.dataMap = null;
65018         this.fireEvent("columnmoved", this, oldIndex, newIndex);
65019     },
65020
65021     isLocked : function(colIndex){
65022         return this.config[colIndex].locked === true;
65023     },
65024
65025     setLocked : function(colIndex, value, suppressEvent){
65026         if(this.isLocked(colIndex) == value){
65027             return;
65028         }
65029         this.config[colIndex].locked = value;
65030         if(!suppressEvent){
65031             this.fireEvent("columnlockchange", this, colIndex, value);
65032         }
65033     },
65034
65035     getTotalLockedWidth : function(){
65036         var totalWidth = 0;
65037         for(var i = 0; i < this.config.length; i++){
65038             if(this.isLocked(i) && !this.isHidden(i)){
65039                 this.totalWidth += this.getColumnWidth(i);
65040             }
65041         }
65042         return totalWidth;
65043     },
65044
65045     getLockedCount : function(){
65046         for(var i = 0, len = this.config.length; i < len; i++){
65047             if(!this.isLocked(i)){
65048                 return i;
65049             }
65050         }
65051         
65052         return this.config.length;
65053     },
65054
65055     /**
65056      * Returns the number of columns.
65057      * @return {Number}
65058      */
65059     getColumnCount : function(visibleOnly){
65060         if(visibleOnly === true){
65061             var c = 0;
65062             for(var i = 0, len = this.config.length; i < len; i++){
65063                 if(!this.isHidden(i)){
65064                     c++;
65065                 }
65066             }
65067             return c;
65068         }
65069         return this.config.length;
65070     },
65071
65072     /**
65073      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
65074      * @param {Function} fn
65075      * @param {Object} scope (optional)
65076      * @return {Array} result
65077      */
65078     getColumnsBy : function(fn, scope){
65079         var r = [];
65080         for(var i = 0, len = this.config.length; i < len; i++){
65081             var c = this.config[i];
65082             if(fn.call(scope||this, c, i) === true){
65083                 r[r.length] = c;
65084             }
65085         }
65086         return r;
65087     },
65088
65089     /**
65090      * Returns true if the specified column is sortable.
65091      * @param {Number} col The column index
65092      * @return {Boolean}
65093      */
65094     isSortable : function(col){
65095         if(typeof this.config[col].sortable == "undefined"){
65096             return this.defaultSortable;
65097         }
65098         return this.config[col].sortable;
65099     },
65100
65101     /**
65102      * Returns the rendering (formatting) function defined for the column.
65103      * @param {Number} col The column index.
65104      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
65105      */
65106     getRenderer : function(col){
65107         if(!this.config[col].renderer){
65108             return Roo.grid.ColumnModel.defaultRenderer;
65109         }
65110         return this.config[col].renderer;
65111     },
65112
65113     /**
65114      * Sets the rendering (formatting) function for a column.
65115      * @param {Number} col The column index
65116      * @param {Function} fn The function to use to process the cell's raw data
65117      * to return HTML markup for the grid view. The render function is called with
65118      * the following parameters:<ul>
65119      * <li>Data value.</li>
65120      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
65121      * <li>css A CSS style string to apply to the table cell.</li>
65122      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
65123      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
65124      * <li>Row index</li>
65125      * <li>Column index</li>
65126      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
65127      */
65128     setRenderer : function(col, fn){
65129         this.config[col].renderer = fn;
65130     },
65131
65132     /**
65133      * Returns the width for the specified column.
65134      * @param {Number} col The column index
65135      * @param (optional) {String} gridSize bootstrap width size.
65136      * @return {Number}
65137      */
65138     getColumnWidth : function(col, gridSize)
65139         {
65140                 var cfg = this.config[col];
65141                 
65142                 if (typeof(gridSize) == 'undefined') {
65143                         return cfg.width * 1 || this.defaultWidth;
65144                 }
65145                 if (gridSize === false) { // if we set it..
65146                         return cfg.width || false;
65147                 }
65148                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
65149                 
65150                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
65151                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
65152                                 continue;
65153                         }
65154                         return cfg[ sizes[i] ];
65155                 }
65156                 return 1;
65157                 
65158     },
65159
65160     /**
65161      * Sets the width for a column.
65162      * @param {Number} col The column index
65163      * @param {Number} width The new width
65164      */
65165     setColumnWidth : function(col, width, suppressEvent){
65166         this.config[col].width = width;
65167         this.totalWidth = null;
65168         if(!suppressEvent){
65169              this.fireEvent("widthchange", this, col, width);
65170         }
65171     },
65172
65173     /**
65174      * Returns the total width of all columns.
65175      * @param {Boolean} includeHidden True to include hidden column widths
65176      * @return {Number}
65177      */
65178     getTotalWidth : function(includeHidden){
65179         if(!this.totalWidth){
65180             this.totalWidth = 0;
65181             for(var i = 0, len = this.config.length; i < len; i++){
65182                 if(includeHidden || !this.isHidden(i)){
65183                     this.totalWidth += this.getColumnWidth(i);
65184                 }
65185             }
65186         }
65187         return this.totalWidth;
65188     },
65189
65190     /**
65191      * Returns the header for the specified column.
65192      * @param {Number} col The column index
65193      * @return {String}
65194      */
65195     getColumnHeader : function(col){
65196         return this.config[col].header;
65197     },
65198
65199     /**
65200      * Sets the header for a column.
65201      * @param {Number} col The column index
65202      * @param {String} header The new header
65203      */
65204     setColumnHeader : function(col, header){
65205         this.config[col].header = header;
65206         this.fireEvent("headerchange", this, col, header);
65207     },
65208
65209     /**
65210      * Returns the tooltip for the specified column.
65211      * @param {Number} col The column index
65212      * @return {String}
65213      */
65214     getColumnTooltip : function(col){
65215             return this.config[col].tooltip;
65216     },
65217     /**
65218      * Sets the tooltip for a column.
65219      * @param {Number} col The column index
65220      * @param {String} tooltip The new tooltip
65221      */
65222     setColumnTooltip : function(col, tooltip){
65223             this.config[col].tooltip = tooltip;
65224     },
65225
65226     /**
65227      * Returns the dataIndex for the specified column.
65228      * @param {Number} col The column index
65229      * @return {Number}
65230      */
65231     getDataIndex : function(col){
65232         return this.config[col].dataIndex;
65233     },
65234
65235     /**
65236      * Sets the dataIndex for a column.
65237      * @param {Number} col The column index
65238      * @param {Number} dataIndex The new dataIndex
65239      */
65240     setDataIndex : function(col, dataIndex){
65241         this.config[col].dataIndex = dataIndex;
65242     },
65243
65244     
65245     
65246     /**
65247      * Returns true if the cell is editable.
65248      * @param {Number} colIndex The column index
65249      * @param {Number} rowIndex The row index - this is nto actually used..?
65250      * @return {Boolean}
65251      */
65252     isCellEditable : function(colIndex, rowIndex){
65253         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
65254     },
65255
65256     /**
65257      * Returns the editor defined for the cell/column.
65258      * return false or null to disable editing.
65259      * @param {Number} colIndex The column index
65260      * @param {Number} rowIndex The row index
65261      * @return {Object}
65262      */
65263     getCellEditor : function(colIndex, rowIndex){
65264         return this.config[colIndex].editor;
65265     },
65266
65267     /**
65268      * Sets if a column is editable.
65269      * @param {Number} col The column index
65270      * @param {Boolean} editable True if the column is editable
65271      */
65272     setEditable : function(col, editable){
65273         this.config[col].editable = editable;
65274     },
65275
65276
65277     /**
65278      * Returns true if the column is hidden.
65279      * @param {Number} colIndex The column index
65280      * @return {Boolean}
65281      */
65282     isHidden : function(colIndex){
65283         return this.config[colIndex].hidden;
65284     },
65285
65286
65287     /**
65288      * Returns true if the column width cannot be changed
65289      */
65290     isFixed : function(colIndex){
65291         return this.config[colIndex].fixed;
65292     },
65293
65294     /**
65295      * Returns true if the column can be resized
65296      * @return {Boolean}
65297      */
65298     isResizable : function(colIndex){
65299         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
65300     },
65301     /**
65302      * Sets if a column is hidden.
65303      * @param {Number} colIndex The column index
65304      * @param {Boolean} hidden True if the column is hidden
65305      */
65306     setHidden : function(colIndex, hidden){
65307         this.config[colIndex].hidden = hidden;
65308         this.totalWidth = null;
65309         this.fireEvent("hiddenchange", this, colIndex, hidden);
65310     },
65311
65312     /**
65313      * Sets the editor for a column.
65314      * @param {Number} col The column index
65315      * @param {Object} editor The editor object
65316      */
65317     setEditor : function(col, editor){
65318         this.config[col].editor = editor;
65319     },
65320     /**
65321      * Add a column (experimental...) - defaults to adding to the end..
65322      * @param {Object} config 
65323     */
65324     addColumn : function(c)
65325     {
65326     
65327         var i = this.config.length;
65328         this.config[i] = c;
65329         
65330         if(typeof c.dataIndex == "undefined"){
65331             c.dataIndex = i;
65332         }
65333         if(typeof c.renderer == "string"){
65334             c.renderer = Roo.util.Format[c.renderer];
65335         }
65336         if(typeof c.id == "undefined"){
65337             c.id = Roo.id();
65338         }
65339         if(c.editor && c.editor.xtype){
65340             c.editor  = Roo.factory(c.editor, Roo.grid);
65341         }
65342         if(c.editor && c.editor.isFormField){
65343             c.editor = new Roo.grid.GridEditor(c.editor);
65344         }
65345         this.lookup[c.id] = c;
65346     }
65347     
65348 });
65349
65350 Roo.grid.ColumnModel.defaultRenderer = function(value)
65351 {
65352     if(typeof value == "object") {
65353         return value;
65354     }
65355         if(typeof value == "string" && value.length < 1){
65356             return "&#160;";
65357         }
65358     
65359         return String.format("{0}", value);
65360 };
65361
65362 // Alias for backwards compatibility
65363 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
65364 /*
65365  * Based on:
65366  * Ext JS Library 1.1.1
65367  * Copyright(c) 2006-2007, Ext JS, LLC.
65368  *
65369  * Originally Released Under LGPL - original licence link has changed is not relivant.
65370  *
65371  * Fork - LGPL
65372  * <script type="text/javascript">
65373  */
65374
65375 /**
65376  * @class Roo.grid.AbstractSelectionModel
65377  * @extends Roo.util.Observable
65378  * @abstract
65379  * Abstract base class for grid SelectionModels.  It provides the interface that should be
65380  * implemented by descendant classes.  This class should not be directly instantiated.
65381  * @constructor
65382  */
65383 Roo.grid.AbstractSelectionModel = function(){
65384     this.locked = false;
65385     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
65386 };
65387
65388 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
65389     /** @ignore Called by the grid automatically. Do not call directly. */
65390     init : function(grid){
65391         this.grid = grid;
65392         this.initEvents();
65393     },
65394
65395     /**
65396      * Locks the selections.
65397      */
65398     lock : function(){
65399         this.locked = true;
65400     },
65401
65402     /**
65403      * Unlocks the selections.
65404      */
65405     unlock : function(){
65406         this.locked = false;
65407     },
65408
65409     /**
65410      * Returns true if the selections are locked.
65411      * @return {Boolean}
65412      */
65413     isLocked : function(){
65414         return this.locked;
65415     }
65416 });/*
65417  * Based on:
65418  * Ext JS Library 1.1.1
65419  * Copyright(c) 2006-2007, Ext JS, LLC.
65420  *
65421  * Originally Released Under LGPL - original licence link has changed is not relivant.
65422  *
65423  * Fork - LGPL
65424  * <script type="text/javascript">
65425  */
65426 /**
65427  * @extends Roo.grid.AbstractSelectionModel
65428  * @class Roo.grid.RowSelectionModel
65429  * The default SelectionModel used by {@link Roo.grid.Grid}.
65430  * It supports multiple selections and keyboard selection/navigation. 
65431  * @constructor
65432  * @param {Object} config
65433  */
65434 Roo.grid.RowSelectionModel = function(config){
65435     Roo.apply(this, config);
65436     this.selections = new Roo.util.MixedCollection(false, function(o){
65437         return o.id;
65438     });
65439
65440     this.last = false;
65441     this.lastActive = false;
65442
65443     this.addEvents({
65444         /**
65445         * @event selectionchange
65446         * Fires when the selection changes
65447         * @param {SelectionModel} this
65448         */
65449        "selectionchange" : true,
65450        /**
65451         * @event afterselectionchange
65452         * Fires after the selection changes (eg. by key press or clicking)
65453         * @param {SelectionModel} this
65454         */
65455        "afterselectionchange" : true,
65456        /**
65457         * @event beforerowselect
65458         * Fires when a row is selected being selected, return false to cancel.
65459         * @param {SelectionModel} this
65460         * @param {Number} rowIndex The selected index
65461         * @param {Boolean} keepExisting False if other selections will be cleared
65462         */
65463        "beforerowselect" : true,
65464        /**
65465         * @event rowselect
65466         * Fires when a row is selected.
65467         * @param {SelectionModel} this
65468         * @param {Number} rowIndex The selected index
65469         * @param {Roo.data.Record} r The record
65470         */
65471        "rowselect" : true,
65472        /**
65473         * @event rowdeselect
65474         * Fires when a row is deselected.
65475         * @param {SelectionModel} this
65476         * @param {Number} rowIndex The selected index
65477         */
65478         "rowdeselect" : true
65479     });
65480     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
65481     this.locked = false;
65482 };
65483
65484 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
65485     /**
65486      * @cfg {Boolean} singleSelect
65487      * True to allow selection of only one row at a time (defaults to false)
65488      */
65489     singleSelect : false,
65490
65491     // private
65492     initEvents : function(){
65493
65494         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
65495             this.grid.on("mousedown", this.handleMouseDown, this);
65496         }else{ // allow click to work like normal
65497             this.grid.on("rowclick", this.handleDragableRowClick, this);
65498         }
65499         // bootstrap does not have a view..
65500         var view = this.grid.view ? this.grid.view : this.grid;
65501         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
65502             "up" : function(e){
65503                 if(!e.shiftKey){
65504                     this.selectPrevious(e.shiftKey);
65505                 }else if(this.last !== false && this.lastActive !== false){
65506                     var last = this.last;
65507                     this.selectRange(this.last,  this.lastActive-1);
65508                     view.focusRow(this.lastActive);
65509                     if(last !== false){
65510                         this.last = last;
65511                     }
65512                 }else{
65513                     this.selectFirstRow();
65514                 }
65515                 this.fireEvent("afterselectionchange", this);
65516             },
65517             "down" : function(e){
65518                 if(!e.shiftKey){
65519                     this.selectNext(e.shiftKey);
65520                 }else if(this.last !== false && this.lastActive !== false){
65521                     var last = this.last;
65522                     this.selectRange(this.last,  this.lastActive+1);
65523                     view.focusRow(this.lastActive);
65524                     if(last !== false){
65525                         this.last = last;
65526                     }
65527                 }else{
65528                     this.selectFirstRow();
65529                 }
65530                 this.fireEvent("afterselectionchange", this);
65531             },
65532             scope: this
65533         });
65534
65535          
65536         view.on("refresh", this.onRefresh, this);
65537         view.on("rowupdated", this.onRowUpdated, this);
65538         view.on("rowremoved", this.onRemove, this);
65539     },
65540
65541     // private
65542     onRefresh : function(){
65543         var ds = this.grid.ds, i, v = this.grid.view;
65544         var s = this.selections;
65545         s.each(function(r){
65546             if((i = ds.indexOfId(r.id)) != -1){
65547                 v.onRowSelect(i);
65548                 s.add(ds.getAt(i)); // updating the selection relate data
65549             }else{
65550                 s.remove(r);
65551             }
65552         });
65553     },
65554
65555     // private
65556     onRemove : function(v, index, r){
65557         this.selections.remove(r);
65558     },
65559
65560     // private
65561     onRowUpdated : function(v, index, r){
65562         if(this.isSelected(r)){
65563             v.onRowSelect(index);
65564         }
65565     },
65566
65567     /**
65568      * Select records.
65569      * @param {Array} records The records to select
65570      * @param {Boolean} keepExisting (optional) True to keep existing selections
65571      */
65572     selectRecords : function(records, keepExisting){
65573         if(!keepExisting){
65574             this.clearSelections();
65575         }
65576         var ds = this.grid.ds;
65577         for(var i = 0, len = records.length; i < len; i++){
65578             this.selectRow(ds.indexOf(records[i]), true);
65579         }
65580     },
65581
65582     /**
65583      * Gets the number of selected rows.
65584      * @return {Number}
65585      */
65586     getCount : function(){
65587         return this.selections.length;
65588     },
65589
65590     /**
65591      * Selects the first row in the grid.
65592      */
65593     selectFirstRow : function(){
65594         this.selectRow(0);
65595     },
65596
65597     /**
65598      * Select the last row.
65599      * @param {Boolean} keepExisting (optional) True to keep existing selections
65600      */
65601     selectLastRow : function(keepExisting){
65602         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
65603     },
65604
65605     /**
65606      * Selects the row immediately following the last selected row.
65607      * @param {Boolean} keepExisting (optional) True to keep existing selections
65608      */
65609     selectNext : function(keepExisting){
65610         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
65611             this.selectRow(this.last+1, keepExisting);
65612             var view = this.grid.view ? this.grid.view : this.grid;
65613             view.focusRow(this.last);
65614         }
65615     },
65616
65617     /**
65618      * Selects the row that precedes the last selected row.
65619      * @param {Boolean} keepExisting (optional) True to keep existing selections
65620      */
65621     selectPrevious : function(keepExisting){
65622         if(this.last){
65623             this.selectRow(this.last-1, keepExisting);
65624             var view = this.grid.view ? this.grid.view : this.grid;
65625             view.focusRow(this.last);
65626         }
65627     },
65628
65629     /**
65630      * Returns the selected records
65631      * @return {Array} Array of selected records
65632      */
65633     getSelections : function(){
65634         return [].concat(this.selections.items);
65635     },
65636
65637     /**
65638      * Returns the first selected record.
65639      * @return {Record}
65640      */
65641     getSelected : function(){
65642         return this.selections.itemAt(0);
65643     },
65644
65645
65646     /**
65647      * Clears all selections.
65648      */
65649     clearSelections : function(fast){
65650         if(this.locked) {
65651             return;
65652         }
65653         if(fast !== true){
65654             var ds = this.grid.ds;
65655             var s = this.selections;
65656             s.each(function(r){
65657                 this.deselectRow(ds.indexOfId(r.id));
65658             }, this);
65659             s.clear();
65660         }else{
65661             this.selections.clear();
65662         }
65663         this.last = false;
65664     },
65665
65666
65667     /**
65668      * Selects all rows.
65669      */
65670     selectAll : function(){
65671         if(this.locked) {
65672             return;
65673         }
65674         this.selections.clear();
65675         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
65676             this.selectRow(i, true);
65677         }
65678     },
65679
65680     /**
65681      * Returns True if there is a selection.
65682      * @return {Boolean}
65683      */
65684     hasSelection : function(){
65685         return this.selections.length > 0;
65686     },
65687
65688     /**
65689      * Returns True if the specified row is selected.
65690      * @param {Number/Record} record The record or index of the record to check
65691      * @return {Boolean}
65692      */
65693     isSelected : function(index){
65694         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
65695         return (r && this.selections.key(r.id) ? true : false);
65696     },
65697
65698     /**
65699      * Returns True if the specified record id is selected.
65700      * @param {String} id The id of record to check
65701      * @return {Boolean}
65702      */
65703     isIdSelected : function(id){
65704         return (this.selections.key(id) ? true : false);
65705     },
65706
65707     // private
65708     handleMouseDown : function(e, t)
65709     {
65710         var view = this.grid.view ? this.grid.view : this.grid;
65711         var rowIndex;
65712         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
65713             return;
65714         };
65715         if(e.shiftKey && this.last !== false){
65716             var last = this.last;
65717             this.selectRange(last, rowIndex, e.ctrlKey);
65718             this.last = last; // reset the last
65719             view.focusRow(rowIndex);
65720         }else{
65721             var isSelected = this.isSelected(rowIndex);
65722             if(e.button !== 0 && isSelected){
65723                 view.focusRow(rowIndex);
65724             }else if(e.ctrlKey && isSelected){
65725                 this.deselectRow(rowIndex);
65726             }else if(!isSelected){
65727                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
65728                 view.focusRow(rowIndex);
65729             }
65730         }
65731         this.fireEvent("afterselectionchange", this);
65732     },
65733     // private
65734     handleDragableRowClick :  function(grid, rowIndex, e) 
65735     {
65736         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
65737             this.selectRow(rowIndex, false);
65738             var view = this.grid.view ? this.grid.view : this.grid;
65739             view.focusRow(rowIndex);
65740              this.fireEvent("afterselectionchange", this);
65741         }
65742     },
65743     
65744     /**
65745      * Selects multiple rows.
65746      * @param {Array} rows Array of the indexes of the row to select
65747      * @param {Boolean} keepExisting (optional) True to keep existing selections
65748      */
65749     selectRows : function(rows, keepExisting){
65750         if(!keepExisting){
65751             this.clearSelections();
65752         }
65753         for(var i = 0, len = rows.length; i < len; i++){
65754             this.selectRow(rows[i], true);
65755         }
65756     },
65757
65758     /**
65759      * Selects a range of rows. All rows in between startRow and endRow are also selected.
65760      * @param {Number} startRow The index of the first row in the range
65761      * @param {Number} endRow The index of the last row in the range
65762      * @param {Boolean} keepExisting (optional) True to retain existing selections
65763      */
65764     selectRange : function(startRow, endRow, keepExisting){
65765         if(this.locked) {
65766             return;
65767         }
65768         if(!keepExisting){
65769             this.clearSelections();
65770         }
65771         if(startRow <= endRow){
65772             for(var i = startRow; i <= endRow; i++){
65773                 this.selectRow(i, true);
65774             }
65775         }else{
65776             for(var i = startRow; i >= endRow; i--){
65777                 this.selectRow(i, true);
65778             }
65779         }
65780     },
65781
65782     /**
65783      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
65784      * @param {Number} startRow The index of the first row in the range
65785      * @param {Number} endRow The index of the last row in the range
65786      */
65787     deselectRange : function(startRow, endRow, preventViewNotify){
65788         if(this.locked) {
65789             return;
65790         }
65791         for(var i = startRow; i <= endRow; i++){
65792             this.deselectRow(i, preventViewNotify);
65793         }
65794     },
65795
65796     /**
65797      * Selects a row.
65798      * @param {Number} row The index of the row to select
65799      * @param {Boolean} keepExisting (optional) True to keep existing selections
65800      */
65801     selectRow : function(index, keepExisting, preventViewNotify){
65802         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
65803             return;
65804         }
65805         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
65806             if(!keepExisting || this.singleSelect){
65807                 this.clearSelections();
65808             }
65809             var r = this.grid.ds.getAt(index);
65810             this.selections.add(r);
65811             this.last = this.lastActive = index;
65812             if(!preventViewNotify){
65813                 var view = this.grid.view ? this.grid.view : this.grid;
65814                 view.onRowSelect(index);
65815             }
65816             this.fireEvent("rowselect", this, index, r);
65817             this.fireEvent("selectionchange", this);
65818         }
65819     },
65820
65821     /**
65822      * Deselects a row.
65823      * @param {Number} row The index of the row to deselect
65824      */
65825     deselectRow : function(index, preventViewNotify){
65826         if(this.locked) {
65827             return;
65828         }
65829         if(this.last == index){
65830             this.last = false;
65831         }
65832         if(this.lastActive == index){
65833             this.lastActive = false;
65834         }
65835         var r = this.grid.ds.getAt(index);
65836         this.selections.remove(r);
65837         if(!preventViewNotify){
65838             var view = this.grid.view ? this.grid.view : this.grid;
65839             view.onRowDeselect(index);
65840         }
65841         this.fireEvent("rowdeselect", this, index);
65842         this.fireEvent("selectionchange", this);
65843     },
65844
65845     // private
65846     restoreLast : function(){
65847         if(this._last){
65848             this.last = this._last;
65849         }
65850     },
65851
65852     // private
65853     acceptsNav : function(row, col, cm){
65854         return !cm.isHidden(col) && cm.isCellEditable(col, row);
65855     },
65856
65857     // private
65858     onEditorKey : function(field, e){
65859         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
65860         if(k == e.TAB){
65861             e.stopEvent();
65862             ed.completeEdit();
65863             if(e.shiftKey){
65864                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
65865             }else{
65866                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
65867             }
65868         }else if(k == e.ENTER && !e.ctrlKey){
65869             e.stopEvent();
65870             ed.completeEdit();
65871             if(e.shiftKey){
65872                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
65873             }else{
65874                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
65875             }
65876         }else if(k == e.ESC){
65877             ed.cancelEdit();
65878         }
65879         if(newCell){
65880             g.startEditing(newCell[0], newCell[1]);
65881         }
65882     }
65883 });/*
65884  * Based on:
65885  * Ext JS Library 1.1.1
65886  * Copyright(c) 2006-2007, Ext JS, LLC.
65887  *
65888  * Originally Released Under LGPL - original licence link has changed is not relivant.
65889  *
65890  * Fork - LGPL
65891  * <script type="text/javascript">
65892  */
65893 /**
65894  * @class Roo.grid.CellSelectionModel
65895  * @extends Roo.grid.AbstractSelectionModel
65896  * This class provides the basic implementation for cell selection in a grid.
65897  * @constructor
65898  * @param {Object} config The object containing the configuration of this model.
65899  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
65900  */
65901 Roo.grid.CellSelectionModel = function(config){
65902     Roo.apply(this, config);
65903
65904     this.selection = null;
65905
65906     this.addEvents({
65907         /**
65908              * @event beforerowselect
65909              * Fires before a cell is selected.
65910              * @param {SelectionModel} this
65911              * @param {Number} rowIndex The selected row index
65912              * @param {Number} colIndex The selected cell index
65913              */
65914             "beforecellselect" : true,
65915         /**
65916              * @event cellselect
65917              * Fires when a cell is selected.
65918              * @param {SelectionModel} this
65919              * @param {Number} rowIndex The selected row index
65920              * @param {Number} colIndex The selected cell index
65921              */
65922             "cellselect" : true,
65923         /**
65924              * @event selectionchange
65925              * Fires when the active selection changes.
65926              * @param {SelectionModel} this
65927              * @param {Object} selection null for no selection or an object (o) with two properties
65928                 <ul>
65929                 <li>o.record: the record object for the row the selection is in</li>
65930                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
65931                 </ul>
65932              */
65933             "selectionchange" : true,
65934         /**
65935              * @event tabend
65936              * Fires when the tab (or enter) was pressed on the last editable cell
65937              * You can use this to trigger add new row.
65938              * @param {SelectionModel} this
65939              */
65940             "tabend" : true,
65941          /**
65942              * @event beforeeditnext
65943              * Fires before the next editable sell is made active
65944              * You can use this to skip to another cell or fire the tabend
65945              *    if you set cell to false
65946              * @param {Object} eventdata object : { cell : [ row, col ] } 
65947              */
65948             "beforeeditnext" : true
65949     });
65950     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
65951 };
65952
65953 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
65954     
65955     enter_is_tab: false,
65956
65957     /** @ignore */
65958     initEvents : function(){
65959         this.grid.on("mousedown", this.handleMouseDown, this);
65960         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
65961         var view = this.grid.view;
65962         view.on("refresh", this.onViewChange, this);
65963         view.on("rowupdated", this.onRowUpdated, this);
65964         view.on("beforerowremoved", this.clearSelections, this);
65965         view.on("beforerowsinserted", this.clearSelections, this);
65966         if(this.grid.isEditor){
65967             this.grid.on("beforeedit", this.beforeEdit,  this);
65968         }
65969     },
65970
65971         //private
65972     beforeEdit : function(e){
65973         this.select(e.row, e.column, false, true, e.record);
65974     },
65975
65976         //private
65977     onRowUpdated : function(v, index, r){
65978         if(this.selection && this.selection.record == r){
65979             v.onCellSelect(index, this.selection.cell[1]);
65980         }
65981     },
65982
65983         //private
65984     onViewChange : function(){
65985         this.clearSelections(true);
65986     },
65987
65988         /**
65989          * Returns the currently selected cell,.
65990          * @return {Array} The selected cell (row, column) or null if none selected.
65991          */
65992     getSelectedCell : function(){
65993         return this.selection ? this.selection.cell : null;
65994     },
65995
65996     /**
65997      * Clears all selections.
65998      * @param {Boolean} true to prevent the gridview from being notified about the change.
65999      */
66000     clearSelections : function(preventNotify){
66001         var s = this.selection;
66002         if(s){
66003             if(preventNotify !== true){
66004                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
66005             }
66006             this.selection = null;
66007             this.fireEvent("selectionchange", this, null);
66008         }
66009     },
66010
66011     /**
66012      * Returns true if there is a selection.
66013      * @return {Boolean}
66014      */
66015     hasSelection : function(){
66016         return this.selection ? true : false;
66017     },
66018
66019     /** @ignore */
66020     handleMouseDown : function(e, t){
66021         var v = this.grid.getView();
66022         if(this.isLocked()){
66023             return;
66024         };
66025         var row = v.findRowIndex(t);
66026         var cell = v.findCellIndex(t);
66027         if(row !== false && cell !== false){
66028             this.select(row, cell);
66029         }
66030     },
66031
66032     /**
66033      * Selects a cell.
66034      * @param {Number} rowIndex
66035      * @param {Number} collIndex
66036      */
66037     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
66038         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
66039             this.clearSelections();
66040             r = r || this.grid.dataSource.getAt(rowIndex);
66041             this.selection = {
66042                 record : r,
66043                 cell : [rowIndex, colIndex]
66044             };
66045             if(!preventViewNotify){
66046                 var v = this.grid.getView();
66047                 v.onCellSelect(rowIndex, colIndex);
66048                 if(preventFocus !== true){
66049                     v.focusCell(rowIndex, colIndex);
66050                 }
66051             }
66052             this.fireEvent("cellselect", this, rowIndex, colIndex);
66053             this.fireEvent("selectionchange", this, this.selection);
66054         }
66055     },
66056
66057         //private
66058     isSelectable : function(rowIndex, colIndex, cm){
66059         return !cm.isHidden(colIndex);
66060     },
66061
66062     /** @ignore */
66063     handleKeyDown : function(e){
66064         //Roo.log('Cell Sel Model handleKeyDown');
66065         if(!e.isNavKeyPress()){
66066             return;
66067         }
66068         var g = this.grid, s = this.selection;
66069         if(!s){
66070             e.stopEvent();
66071             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
66072             if(cell){
66073                 this.select(cell[0], cell[1]);
66074             }
66075             return;
66076         }
66077         var sm = this;
66078         var walk = function(row, col, step){
66079             return g.walkCells(row, col, step, sm.isSelectable,  sm);
66080         };
66081         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
66082         var newCell;
66083
66084       
66085
66086         switch(k){
66087             case e.TAB:
66088                 // handled by onEditorKey
66089                 if (g.isEditor && g.editing) {
66090                     return;
66091                 }
66092                 if(e.shiftKey) {
66093                     newCell = walk(r, c-1, -1);
66094                 } else {
66095                     newCell = walk(r, c+1, 1);
66096                 }
66097                 break;
66098             
66099             case e.DOWN:
66100                newCell = walk(r+1, c, 1);
66101                 break;
66102             
66103             case e.UP:
66104                 newCell = walk(r-1, c, -1);
66105                 break;
66106             
66107             case e.RIGHT:
66108                 newCell = walk(r, c+1, 1);
66109                 break;
66110             
66111             case e.LEFT:
66112                 newCell = walk(r, c-1, -1);
66113                 break;
66114             
66115             case e.ENTER:
66116                 
66117                 if(g.isEditor && !g.editing){
66118                    g.startEditing(r, c);
66119                    e.stopEvent();
66120                    return;
66121                 }
66122                 
66123                 
66124              break;
66125         };
66126         if(newCell){
66127             this.select(newCell[0], newCell[1]);
66128             e.stopEvent();
66129             
66130         }
66131     },
66132
66133     acceptsNav : function(row, col, cm){
66134         return !cm.isHidden(col) && cm.isCellEditable(col, row);
66135     },
66136     /**
66137      * Selects a cell.
66138      * @param {Number} field (not used) - as it's normally used as a listener
66139      * @param {Number} e - event - fake it by using
66140      *
66141      * var e = Roo.EventObjectImpl.prototype;
66142      * e.keyCode = e.TAB
66143      *
66144      * 
66145      */
66146     onEditorKey : function(field, e){
66147         
66148         var k = e.getKey(),
66149             newCell,
66150             g = this.grid,
66151             ed = g.activeEditor,
66152             forward = false;
66153         ///Roo.log('onEditorKey' + k);
66154         
66155         
66156         if (this.enter_is_tab && k == e.ENTER) {
66157             k = e.TAB;
66158         }
66159         
66160         if(k == e.TAB){
66161             if(e.shiftKey){
66162                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
66163             }else{
66164                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
66165                 forward = true;
66166             }
66167             
66168             e.stopEvent();
66169             
66170         } else if(k == e.ENTER &&  !e.ctrlKey){
66171             ed.completeEdit();
66172             e.stopEvent();
66173             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
66174         
66175                 } else if(k == e.ESC){
66176             ed.cancelEdit();
66177         }
66178                 
66179         if (newCell) {
66180             var ecall = { cell : newCell, forward : forward };
66181             this.fireEvent('beforeeditnext', ecall );
66182             newCell = ecall.cell;
66183                         forward = ecall.forward;
66184         }
66185                 
66186         if(newCell){
66187             //Roo.log('next cell after edit');
66188             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
66189         } else if (forward) {
66190             // tabbed past last
66191             this.fireEvent.defer(100, this, ['tabend',this]);
66192         }
66193     }
66194 });/*
66195  * Based on:
66196  * Ext JS Library 1.1.1
66197  * Copyright(c) 2006-2007, Ext JS, LLC.
66198  *
66199  * Originally Released Under LGPL - original licence link has changed is not relivant.
66200  *
66201  * Fork - LGPL
66202  * <script type="text/javascript">
66203  */
66204  
66205 /**
66206  * @class Roo.grid.EditorGrid
66207  * @extends Roo.grid.Grid
66208  * Class for creating and editable grid.
66209  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
66210  * The container MUST have some type of size defined for the grid to fill. The container will be 
66211  * automatically set to position relative if it isn't already.
66212  * @param {Object} dataSource The data model to bind to
66213  * @param {Object} colModel The column model with info about this grid's columns
66214  */
66215 Roo.grid.EditorGrid = function(container, config){
66216     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
66217     this.getGridEl().addClass("xedit-grid");
66218
66219     if(!this.selModel){
66220         this.selModel = new Roo.grid.CellSelectionModel();
66221     }
66222
66223     this.activeEditor = null;
66224
66225         this.addEvents({
66226             /**
66227              * @event beforeedit
66228              * Fires before cell editing is triggered. The edit event object has the following properties <br />
66229              * <ul style="padding:5px;padding-left:16px;">
66230              * <li>grid - This grid</li>
66231              * <li>record - The record being edited</li>
66232              * <li>field - The field name being edited</li>
66233              * <li>value - The value for the field being edited.</li>
66234              * <li>row - The grid row index</li>
66235              * <li>column - The grid column index</li>
66236              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
66237              * </ul>
66238              * @param {Object} e An edit event (see above for description)
66239              */
66240             "beforeedit" : true,
66241             /**
66242              * @event afteredit
66243              * Fires after a cell is edited. <br />
66244              * <ul style="padding:5px;padding-left:16px;">
66245              * <li>grid - This grid</li>
66246              * <li>record - The record being edited</li>
66247              * <li>field - The field name being edited</li>
66248              * <li>value - The value being set</li>
66249              * <li>originalValue - The original value for the field, before the edit.</li>
66250              * <li>row - The grid row index</li>
66251              * <li>column - The grid column index</li>
66252              * </ul>
66253              * @param {Object} e An edit event (see above for description)
66254              */
66255             "afteredit" : true,
66256             /**
66257              * @event validateedit
66258              * Fires after a cell is edited, but before the value is set in the record. 
66259          * You can use this to modify the value being set in the field, Return false
66260              * to cancel the change. The edit event object has the following properties <br />
66261              * <ul style="padding:5px;padding-left:16px;">
66262          * <li>editor - This editor</li>
66263              * <li>grid - This grid</li>
66264              * <li>record - The record being edited</li>
66265              * <li>field - The field name being edited</li>
66266              * <li>value - The value being set</li>
66267              * <li>originalValue - The original value for the field, before the edit.</li>
66268              * <li>row - The grid row index</li>
66269              * <li>column - The grid column index</li>
66270              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
66271              * </ul>
66272              * @param {Object} e An edit event (see above for description)
66273              */
66274             "validateedit" : true
66275         });
66276     this.on("bodyscroll", this.stopEditing,  this);
66277     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
66278 };
66279
66280 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
66281     /**
66282      * @cfg {Number} clicksToEdit
66283      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
66284      */
66285     clicksToEdit: 2,
66286
66287     // private
66288     isEditor : true,
66289     // private
66290     trackMouseOver: false, // causes very odd FF errors
66291
66292     onCellDblClick : function(g, row, col){
66293         this.startEditing(row, col);
66294     },
66295
66296     onEditComplete : function(ed, value, startValue){
66297         this.editing = false;
66298         this.activeEditor = null;
66299         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
66300         var r = ed.record;
66301         var field = this.colModel.getDataIndex(ed.col);
66302         var e = {
66303             grid: this,
66304             record: r,
66305             field: field,
66306             originalValue: startValue,
66307             value: value,
66308             row: ed.row,
66309             column: ed.col,
66310             cancel:false,
66311             editor: ed
66312         };
66313         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
66314         cell.show();
66315           
66316         if(String(value) !== String(startValue)){
66317             
66318             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
66319                 r.set(field, e.value);
66320                 // if we are dealing with a combo box..
66321                 // then we also set the 'name' colum to be the displayField
66322                 if (ed.field.displayField && ed.field.name) {
66323                     r.set(ed.field.name, ed.field.el.dom.value);
66324                 }
66325                 
66326                 delete e.cancel; //?? why!!!
66327                 this.fireEvent("afteredit", e);
66328             }
66329         } else {
66330             this.fireEvent("afteredit", e); // always fire it!
66331         }
66332         this.view.focusCell(ed.row, ed.col);
66333     },
66334
66335     /**
66336      * Starts editing the specified for the specified row/column
66337      * @param {Number} rowIndex
66338      * @param {Number} colIndex
66339      */
66340     startEditing : function(row, col){
66341         this.stopEditing();
66342         if(this.colModel.isCellEditable(col, row)){
66343             this.view.ensureVisible(row, col, true);
66344           
66345             var r = this.dataSource.getAt(row);
66346             var field = this.colModel.getDataIndex(col);
66347             var cell = Roo.get(this.view.getCell(row,col));
66348             var e = {
66349                 grid: this,
66350                 record: r,
66351                 field: field,
66352                 value: r.data[field],
66353                 row: row,
66354                 column: col,
66355                 cancel:false 
66356             };
66357             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
66358                 this.editing = true;
66359                 var ed = this.colModel.getCellEditor(col, row);
66360                 
66361                 if (!ed) {
66362                     return;
66363                 }
66364                 if(!ed.rendered){
66365                     ed.render(ed.parentEl || document.body);
66366                 }
66367                 ed.field.reset();
66368                
66369                 cell.hide();
66370                 
66371                 (function(){ // complex but required for focus issues in safari, ie and opera
66372                     ed.row = row;
66373                     ed.col = col;
66374                     ed.record = r;
66375                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
66376                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
66377                     this.activeEditor = ed;
66378                     var v = r.data[field];
66379                     ed.startEdit(this.view.getCell(row, col), v);
66380                     // combo's with 'displayField and name set
66381                     if (ed.field.displayField && ed.field.name) {
66382                         ed.field.el.dom.value = r.data[ed.field.name];
66383                     }
66384                     
66385                     
66386                 }).defer(50, this);
66387             }
66388         }
66389     },
66390         
66391     /**
66392      * Stops any active editing
66393      */
66394     stopEditing : function(){
66395         if(this.activeEditor){
66396             this.activeEditor.completeEdit();
66397         }
66398         this.activeEditor = null;
66399     },
66400         
66401          /**
66402      * Called to get grid's drag proxy text, by default returns this.ddText.
66403      * @return {String}
66404      */
66405     getDragDropText : function(){
66406         var count = this.selModel.getSelectedCell() ? 1 : 0;
66407         return String.format(this.ddText, count, count == 1 ? '' : 's');
66408     }
66409         
66410 });/*
66411  * Based on:
66412  * Ext JS Library 1.1.1
66413  * Copyright(c) 2006-2007, Ext JS, LLC.
66414  *
66415  * Originally Released Under LGPL - original licence link has changed is not relivant.
66416  *
66417  * Fork - LGPL
66418  * <script type="text/javascript">
66419  */
66420
66421 // private - not really -- you end up using it !
66422 // This is a support class used internally by the Grid components
66423
66424 /**
66425  * @class Roo.grid.GridEditor
66426  * @extends Roo.Editor
66427  * Class for creating and editable grid elements.
66428  * @param {Object} config any settings (must include field)
66429  */
66430 Roo.grid.GridEditor = function(field, config){
66431     if (!config && field.field) {
66432         config = field;
66433         field = Roo.factory(config.field, Roo.form);
66434     }
66435     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
66436     field.monitorTab = false;
66437 };
66438
66439 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
66440     
66441     /**
66442      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
66443      */
66444     
66445     alignment: "tl-tl",
66446     autoSize: "width",
66447     hideEl : false,
66448     cls: "x-small-editor x-grid-editor",
66449     shim:false,
66450     shadow:"frame"
66451 });/*
66452  * Based on:
66453  * Ext JS Library 1.1.1
66454  * Copyright(c) 2006-2007, Ext JS, LLC.
66455  *
66456  * Originally Released Under LGPL - original licence link has changed is not relivant.
66457  *
66458  * Fork - LGPL
66459  * <script type="text/javascript">
66460  */
66461   
66462
66463   
66464 Roo.grid.PropertyRecord = Roo.data.Record.create([
66465     {name:'name',type:'string'},  'value'
66466 ]);
66467
66468
66469 Roo.grid.PropertyStore = function(grid, source){
66470     this.grid = grid;
66471     this.store = new Roo.data.Store({
66472         recordType : Roo.grid.PropertyRecord
66473     });
66474     this.store.on('update', this.onUpdate,  this);
66475     if(source){
66476         this.setSource(source);
66477     }
66478     Roo.grid.PropertyStore.superclass.constructor.call(this);
66479 };
66480
66481
66482
66483 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
66484     setSource : function(o){
66485         this.source = o;
66486         this.store.removeAll();
66487         var data = [];
66488         for(var k in o){
66489             if(this.isEditableValue(o[k])){
66490                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
66491             }
66492         }
66493         this.store.loadRecords({records: data}, {}, true);
66494     },
66495
66496     onUpdate : function(ds, record, type){
66497         if(type == Roo.data.Record.EDIT){
66498             var v = record.data['value'];
66499             var oldValue = record.modified['value'];
66500             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
66501                 this.source[record.id] = v;
66502                 record.commit();
66503                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
66504             }else{
66505                 record.reject();
66506             }
66507         }
66508     },
66509
66510     getProperty : function(row){
66511        return this.store.getAt(row);
66512     },
66513
66514     isEditableValue: function(val){
66515         if(val && val instanceof Date){
66516             return true;
66517         }else if(typeof val == 'object' || typeof val == 'function'){
66518             return false;
66519         }
66520         return true;
66521     },
66522
66523     setValue : function(prop, value){
66524         this.source[prop] = value;
66525         this.store.getById(prop).set('value', value);
66526     },
66527
66528     getSource : function(){
66529         return this.source;
66530     }
66531 });
66532
66533 Roo.grid.PropertyColumnModel = function(grid, store){
66534     this.grid = grid;
66535     var g = Roo.grid;
66536     g.PropertyColumnModel.superclass.constructor.call(this, [
66537         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
66538         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
66539     ]);
66540     this.store = store;
66541     this.bselect = Roo.DomHelper.append(document.body, {
66542         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
66543             {tag: 'option', value: 'true', html: 'true'},
66544             {tag: 'option', value: 'false', html: 'false'}
66545         ]
66546     });
66547     Roo.id(this.bselect);
66548     var f = Roo.form;
66549     this.editors = {
66550         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
66551         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
66552         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
66553         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
66554         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
66555     };
66556     this.renderCellDelegate = this.renderCell.createDelegate(this);
66557     this.renderPropDelegate = this.renderProp.createDelegate(this);
66558 };
66559
66560 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
66561     
66562     
66563     nameText : 'Name',
66564     valueText : 'Value',
66565     
66566     dateFormat : 'm/j/Y',
66567     
66568     
66569     renderDate : function(dateVal){
66570         return dateVal.dateFormat(this.dateFormat);
66571     },
66572
66573     renderBool : function(bVal){
66574         return bVal ? 'true' : 'false';
66575     },
66576
66577     isCellEditable : function(colIndex, rowIndex){
66578         return colIndex == 1;
66579     },
66580
66581     getRenderer : function(col){
66582         return col == 1 ?
66583             this.renderCellDelegate : this.renderPropDelegate;
66584     },
66585
66586     renderProp : function(v){
66587         return this.getPropertyName(v);
66588     },
66589
66590     renderCell : function(val){
66591         var rv = val;
66592         if(val instanceof Date){
66593             rv = this.renderDate(val);
66594         }else if(typeof val == 'boolean'){
66595             rv = this.renderBool(val);
66596         }
66597         return Roo.util.Format.htmlEncode(rv);
66598     },
66599
66600     getPropertyName : function(name){
66601         var pn = this.grid.propertyNames;
66602         return pn && pn[name] ? pn[name] : name;
66603     },
66604
66605     getCellEditor : function(colIndex, rowIndex){
66606         var p = this.store.getProperty(rowIndex);
66607         var n = p.data['name'], val = p.data['value'];
66608         
66609         if(typeof(this.grid.customEditors[n]) == 'string'){
66610             return this.editors[this.grid.customEditors[n]];
66611         }
66612         if(typeof(this.grid.customEditors[n]) != 'undefined'){
66613             return this.grid.customEditors[n];
66614         }
66615         if(val instanceof Date){
66616             return this.editors['date'];
66617         }else if(typeof val == 'number'){
66618             return this.editors['number'];
66619         }else if(typeof val == 'boolean'){
66620             return this.editors['boolean'];
66621         }else{
66622             return this.editors['string'];
66623         }
66624     }
66625 });
66626
66627 /**
66628  * @class Roo.grid.PropertyGrid
66629  * @extends Roo.grid.EditorGrid
66630  * This class represents the  interface of a component based property grid control.
66631  * <br><br>Usage:<pre><code>
66632  var grid = new Roo.grid.PropertyGrid("my-container-id", {
66633       
66634  });
66635  // set any options
66636  grid.render();
66637  * </code></pre>
66638   
66639  * @constructor
66640  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66641  * The container MUST have some type of size defined for the grid to fill. The container will be
66642  * automatically set to position relative if it isn't already.
66643  * @param {Object} config A config object that sets properties on this grid.
66644  */
66645 Roo.grid.PropertyGrid = function(container, config){
66646     config = config || {};
66647     var store = new Roo.grid.PropertyStore(this);
66648     this.store = store;
66649     var cm = new Roo.grid.PropertyColumnModel(this, store);
66650     store.store.sort('name', 'ASC');
66651     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
66652         ds: store.store,
66653         cm: cm,
66654         enableColLock:false,
66655         enableColumnMove:false,
66656         stripeRows:false,
66657         trackMouseOver: false,
66658         clicksToEdit:1
66659     }, config));
66660     this.getGridEl().addClass('x-props-grid');
66661     this.lastEditRow = null;
66662     this.on('columnresize', this.onColumnResize, this);
66663     this.addEvents({
66664          /**
66665              * @event beforepropertychange
66666              * Fires before a property changes (return false to stop?)
66667              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66668              * @param {String} id Record Id
66669              * @param {String} newval New Value
66670          * @param {String} oldval Old Value
66671              */
66672         "beforepropertychange": true,
66673         /**
66674              * @event propertychange
66675              * Fires after a property changes
66676              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
66677              * @param {String} id Record Id
66678              * @param {String} newval New Value
66679          * @param {String} oldval Old Value
66680              */
66681         "propertychange": true
66682     });
66683     this.customEditors = this.customEditors || {};
66684 };
66685 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
66686     
66687      /**
66688      * @cfg {Object} customEditors map of colnames=> custom editors.
66689      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
66690      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
66691      * false disables editing of the field.
66692          */
66693     
66694       /**
66695      * @cfg {Object} propertyNames map of property Names to their displayed value
66696          */
66697     
66698     render : function(){
66699         Roo.grid.PropertyGrid.superclass.render.call(this);
66700         this.autoSize.defer(100, this);
66701     },
66702
66703     autoSize : function(){
66704         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
66705         if(this.view){
66706             this.view.fitColumns();
66707         }
66708     },
66709
66710     onColumnResize : function(){
66711         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
66712         this.autoSize();
66713     },
66714     /**
66715      * Sets the data for the Grid
66716      * accepts a Key => Value object of all the elements avaiable.
66717      * @param {Object} data  to appear in grid.
66718      */
66719     setSource : function(source){
66720         this.store.setSource(source);
66721         //this.autoSize();
66722     },
66723     /**
66724      * Gets all the data from the grid.
66725      * @return {Object} data  data stored in grid
66726      */
66727     getSource : function(){
66728         return this.store.getSource();
66729     }
66730 });/*
66731   
66732  * Licence LGPL
66733  
66734  */
66735  
66736 /**
66737  * @class Roo.grid.Calendar
66738  * @extends Roo.grid.Grid
66739  * This class extends the Grid to provide a calendar widget
66740  * <br><br>Usage:<pre><code>
66741  var grid = new Roo.grid.Calendar("my-container-id", {
66742      ds: myDataStore,
66743      cm: myColModel,
66744      selModel: mySelectionModel,
66745      autoSizeColumns: true,
66746      monitorWindowResize: false,
66747      trackMouseOver: true
66748      eventstore : real data store..
66749  });
66750  // set any options
66751  grid.render();
66752   
66753   * @constructor
66754  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
66755  * The container MUST have some type of size defined for the grid to fill. The container will be
66756  * automatically set to position relative if it isn't already.
66757  * @param {Object} config A config object that sets properties on this grid.
66758  */
66759 Roo.grid.Calendar = function(container, config){
66760         // initialize the container
66761         this.container = Roo.get(container);
66762         this.container.update("");
66763         this.container.setStyle("overflow", "hidden");
66764     this.container.addClass('x-grid-container');
66765
66766     this.id = this.container.id;
66767
66768     Roo.apply(this, config);
66769     // check and correct shorthanded configs
66770     
66771     var rows = [];
66772     var d =1;
66773     for (var r = 0;r < 6;r++) {
66774         
66775         rows[r]=[];
66776         for (var c =0;c < 7;c++) {
66777             rows[r][c]= '';
66778         }
66779     }
66780     if (this.eventStore) {
66781         this.eventStore= Roo.factory(this.eventStore, Roo.data);
66782         this.eventStore.on('load',this.onLoad, this);
66783         this.eventStore.on('beforeload',this.clearEvents, this);
66784          
66785     }
66786     
66787     this.dataSource = new Roo.data.Store({
66788             proxy: new Roo.data.MemoryProxy(rows),
66789             reader: new Roo.data.ArrayReader({}, [
66790                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
66791     });
66792
66793     this.dataSource.load();
66794     this.ds = this.dataSource;
66795     this.ds.xmodule = this.xmodule || false;
66796     
66797     
66798     var cellRender = function(v,x,r)
66799     {
66800         return String.format(
66801             '<div class="fc-day  fc-widget-content"><div>' +
66802                 '<div class="fc-event-container"></div>' +
66803                 '<div class="fc-day-number">{0}</div>'+
66804                 
66805                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
66806             '</div></div>', v);
66807     
66808     }
66809     
66810     
66811     this.colModel = new Roo.grid.ColumnModel( [
66812         {
66813             xtype: 'ColumnModel',
66814             xns: Roo.grid,
66815             dataIndex : 'weekday0',
66816             header : 'Sunday',
66817             renderer : cellRender
66818         },
66819         {
66820             xtype: 'ColumnModel',
66821             xns: Roo.grid,
66822             dataIndex : 'weekday1',
66823             header : 'Monday',
66824             renderer : cellRender
66825         },
66826         {
66827             xtype: 'ColumnModel',
66828             xns: Roo.grid,
66829             dataIndex : 'weekday2',
66830             header : 'Tuesday',
66831             renderer : cellRender
66832         },
66833         {
66834             xtype: 'ColumnModel',
66835             xns: Roo.grid,
66836             dataIndex : 'weekday3',
66837             header : 'Wednesday',
66838             renderer : cellRender
66839         },
66840         {
66841             xtype: 'ColumnModel',
66842             xns: Roo.grid,
66843             dataIndex : 'weekday4',
66844             header : 'Thursday',
66845             renderer : cellRender
66846         },
66847         {
66848             xtype: 'ColumnModel',
66849             xns: Roo.grid,
66850             dataIndex : 'weekday5',
66851             header : 'Friday',
66852             renderer : cellRender
66853         },
66854         {
66855             xtype: 'ColumnModel',
66856             xns: Roo.grid,
66857             dataIndex : 'weekday6',
66858             header : 'Saturday',
66859             renderer : cellRender
66860         }
66861     ]);
66862     this.cm = this.colModel;
66863     this.cm.xmodule = this.xmodule || false;
66864  
66865         
66866           
66867     //this.selModel = new Roo.grid.CellSelectionModel();
66868     //this.sm = this.selModel;
66869     //this.selModel.init(this);
66870     
66871     
66872     if(this.width){
66873         this.container.setWidth(this.width);
66874     }
66875
66876     if(this.height){
66877         this.container.setHeight(this.height);
66878     }
66879     /** @private */
66880         this.addEvents({
66881         // raw events
66882         /**
66883          * @event click
66884          * The raw click event for the entire grid.
66885          * @param {Roo.EventObject} e
66886          */
66887         "click" : true,
66888         /**
66889          * @event dblclick
66890          * The raw dblclick event for the entire grid.
66891          * @param {Roo.EventObject} e
66892          */
66893         "dblclick" : true,
66894         /**
66895          * @event contextmenu
66896          * The raw contextmenu event for the entire grid.
66897          * @param {Roo.EventObject} e
66898          */
66899         "contextmenu" : true,
66900         /**
66901          * @event mousedown
66902          * The raw mousedown event for the entire grid.
66903          * @param {Roo.EventObject} e
66904          */
66905         "mousedown" : true,
66906         /**
66907          * @event mouseup
66908          * The raw mouseup event for the entire grid.
66909          * @param {Roo.EventObject} e
66910          */
66911         "mouseup" : true,
66912         /**
66913          * @event mouseover
66914          * The raw mouseover event for the entire grid.
66915          * @param {Roo.EventObject} e
66916          */
66917         "mouseover" : true,
66918         /**
66919          * @event mouseout
66920          * The raw mouseout event for the entire grid.
66921          * @param {Roo.EventObject} e
66922          */
66923         "mouseout" : true,
66924         /**
66925          * @event keypress
66926          * The raw keypress event for the entire grid.
66927          * @param {Roo.EventObject} e
66928          */
66929         "keypress" : true,
66930         /**
66931          * @event keydown
66932          * The raw keydown event for the entire grid.
66933          * @param {Roo.EventObject} e
66934          */
66935         "keydown" : true,
66936
66937         // custom events
66938
66939         /**
66940          * @event cellclick
66941          * Fires when a cell is clicked
66942          * @param {Grid} this
66943          * @param {Number} rowIndex
66944          * @param {Number} columnIndex
66945          * @param {Roo.EventObject} e
66946          */
66947         "cellclick" : true,
66948         /**
66949          * @event celldblclick
66950          * Fires when a cell is double clicked
66951          * @param {Grid} this
66952          * @param {Number} rowIndex
66953          * @param {Number} columnIndex
66954          * @param {Roo.EventObject} e
66955          */
66956         "celldblclick" : true,
66957         /**
66958          * @event rowclick
66959          * Fires when a row is clicked
66960          * @param {Grid} this
66961          * @param {Number} rowIndex
66962          * @param {Roo.EventObject} e
66963          */
66964         "rowclick" : true,
66965         /**
66966          * @event rowdblclick
66967          * Fires when a row is double clicked
66968          * @param {Grid} this
66969          * @param {Number} rowIndex
66970          * @param {Roo.EventObject} e
66971          */
66972         "rowdblclick" : true,
66973         /**
66974          * @event headerclick
66975          * Fires when a header is clicked
66976          * @param {Grid} this
66977          * @param {Number} columnIndex
66978          * @param {Roo.EventObject} e
66979          */
66980         "headerclick" : true,
66981         /**
66982          * @event headerdblclick
66983          * Fires when a header cell is double clicked
66984          * @param {Grid} this
66985          * @param {Number} columnIndex
66986          * @param {Roo.EventObject} e
66987          */
66988         "headerdblclick" : true,
66989         /**
66990          * @event rowcontextmenu
66991          * Fires when a row is right clicked
66992          * @param {Grid} this
66993          * @param {Number} rowIndex
66994          * @param {Roo.EventObject} e
66995          */
66996         "rowcontextmenu" : true,
66997         /**
66998          * @event cellcontextmenu
66999          * Fires when a cell is right clicked
67000          * @param {Grid} this
67001          * @param {Number} rowIndex
67002          * @param {Number} cellIndex
67003          * @param {Roo.EventObject} e
67004          */
67005          "cellcontextmenu" : true,
67006         /**
67007          * @event headercontextmenu
67008          * Fires when a header is right clicked
67009          * @param {Grid} this
67010          * @param {Number} columnIndex
67011          * @param {Roo.EventObject} e
67012          */
67013         "headercontextmenu" : true,
67014         /**
67015          * @event bodyscroll
67016          * Fires when the body element is scrolled
67017          * @param {Number} scrollLeft
67018          * @param {Number} scrollTop
67019          */
67020         "bodyscroll" : true,
67021         /**
67022          * @event columnresize
67023          * Fires when the user resizes a column
67024          * @param {Number} columnIndex
67025          * @param {Number} newSize
67026          */
67027         "columnresize" : true,
67028         /**
67029          * @event columnmove
67030          * Fires when the user moves a column
67031          * @param {Number} oldIndex
67032          * @param {Number} newIndex
67033          */
67034         "columnmove" : true,
67035         /**
67036          * @event startdrag
67037          * Fires when row(s) start being dragged
67038          * @param {Grid} this
67039          * @param {Roo.GridDD} dd The drag drop object
67040          * @param {event} e The raw browser event
67041          */
67042         "startdrag" : true,
67043         /**
67044          * @event enddrag
67045          * Fires when a drag operation is complete
67046          * @param {Grid} this
67047          * @param {Roo.GridDD} dd The drag drop object
67048          * @param {event} e The raw browser event
67049          */
67050         "enddrag" : true,
67051         /**
67052          * @event dragdrop
67053          * Fires when dragged row(s) are dropped on a valid DD target
67054          * @param {Grid} this
67055          * @param {Roo.GridDD} dd The drag drop object
67056          * @param {String} targetId The target drag drop object
67057          * @param {event} e The raw browser event
67058          */
67059         "dragdrop" : true,
67060         /**
67061          * @event dragover
67062          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
67063          * @param {Grid} this
67064          * @param {Roo.GridDD} dd The drag drop object
67065          * @param {String} targetId The target drag drop object
67066          * @param {event} e The raw browser event
67067          */
67068         "dragover" : true,
67069         /**
67070          * @event dragenter
67071          *  Fires when the dragged row(s) first cross another DD target while being dragged
67072          * @param {Grid} this
67073          * @param {Roo.GridDD} dd The drag drop object
67074          * @param {String} targetId The target drag drop object
67075          * @param {event} e The raw browser event
67076          */
67077         "dragenter" : true,
67078         /**
67079          * @event dragout
67080          * Fires when the dragged row(s) leave another DD target while being dragged
67081          * @param {Grid} this
67082          * @param {Roo.GridDD} dd The drag drop object
67083          * @param {String} targetId The target drag drop object
67084          * @param {event} e The raw browser event
67085          */
67086         "dragout" : true,
67087         /**
67088          * @event rowclass
67089          * Fires when a row is rendered, so you can change add a style to it.
67090          * @param {GridView} gridview   The grid view
67091          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
67092          */
67093         'rowclass' : true,
67094
67095         /**
67096          * @event render
67097          * Fires when the grid is rendered
67098          * @param {Grid} grid
67099          */
67100         'render' : true,
67101             /**
67102              * @event select
67103              * Fires when a date is selected
67104              * @param {DatePicker} this
67105              * @param {Date} date The selected date
67106              */
67107         'select': true,
67108         /**
67109              * @event monthchange
67110              * Fires when the displayed month changes 
67111              * @param {DatePicker} this
67112              * @param {Date} date The selected month
67113              */
67114         'monthchange': true,
67115         /**
67116              * @event evententer
67117              * Fires when mouse over an event
67118              * @param {Calendar} this
67119              * @param {event} Event
67120              */
67121         'evententer': true,
67122         /**
67123              * @event eventleave
67124              * Fires when the mouse leaves an
67125              * @param {Calendar} this
67126              * @param {event}
67127              */
67128         'eventleave': true,
67129         /**
67130              * @event eventclick
67131              * Fires when the mouse click an
67132              * @param {Calendar} this
67133              * @param {event}
67134              */
67135         'eventclick': true,
67136         /**
67137              * @event eventrender
67138              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
67139              * @param {Calendar} this
67140              * @param {data} data to be modified
67141              */
67142         'eventrender': true
67143         
67144     });
67145
67146     Roo.grid.Grid.superclass.constructor.call(this);
67147     this.on('render', function() {
67148         this.view.el.addClass('x-grid-cal'); 
67149         
67150         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
67151
67152     },this);
67153     
67154     if (!Roo.grid.Calendar.style) {
67155         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
67156             
67157             
67158             '.x-grid-cal .x-grid-col' :  {
67159                 height: 'auto !important',
67160                 'vertical-align': 'top'
67161             },
67162             '.x-grid-cal  .fc-event-hori' : {
67163                 height: '14px'
67164             }
67165              
67166             
67167         }, Roo.id());
67168     }
67169
67170     
67171     
67172 };
67173 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
67174     /**
67175      * @cfg {Store} eventStore The store that loads events.
67176      */
67177     eventStore : 25,
67178
67179      
67180     activeDate : false,
67181     startDay : 0,
67182     autoWidth : true,
67183     monitorWindowResize : false,
67184
67185     
67186     resizeColumns : function() {
67187         var col = (this.view.el.getWidth() / 7) - 3;
67188         // loop through cols, and setWidth
67189         for(var i =0 ; i < 7 ; i++){
67190             this.cm.setColumnWidth(i, col);
67191         }
67192     },
67193      setDate :function(date) {
67194         
67195         Roo.log('setDate?');
67196         
67197         this.resizeColumns();
67198         var vd = this.activeDate;
67199         this.activeDate = date;
67200 //        if(vd && this.el){
67201 //            var t = date.getTime();
67202 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
67203 //                Roo.log('using add remove');
67204 //                
67205 //                this.fireEvent('monthchange', this, date);
67206 //                
67207 //                this.cells.removeClass("fc-state-highlight");
67208 //                this.cells.each(function(c){
67209 //                   if(c.dateValue == t){
67210 //                       c.addClass("fc-state-highlight");
67211 //                       setTimeout(function(){
67212 //                            try{c.dom.firstChild.focus();}catch(e){}
67213 //                       }, 50);
67214 //                       return false;
67215 //                   }
67216 //                   return true;
67217 //                });
67218 //                return;
67219 //            }
67220 //        }
67221         
67222         var days = date.getDaysInMonth();
67223         
67224         var firstOfMonth = date.getFirstDateOfMonth();
67225         var startingPos = firstOfMonth.getDay()-this.startDay;
67226         
67227         if(startingPos < this.startDay){
67228             startingPos += 7;
67229         }
67230         
67231         var pm = date.add(Date.MONTH, -1);
67232         var prevStart = pm.getDaysInMonth()-startingPos;
67233 //        
67234         
67235         
67236         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
67237         
67238         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
67239         //this.cells.addClassOnOver('fc-state-hover');
67240         
67241         var cells = this.cells.elements;
67242         var textEls = this.textNodes;
67243         
67244         //Roo.each(cells, function(cell){
67245         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
67246         //});
67247         
67248         days += startingPos;
67249
67250         // convert everything to numbers so it's fast
67251         var day = 86400000;
67252         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
67253         //Roo.log(d);
67254         //Roo.log(pm);
67255         //Roo.log(prevStart);
67256         
67257         var today = new Date().clearTime().getTime();
67258         var sel = date.clearTime().getTime();
67259         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
67260         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
67261         var ddMatch = this.disabledDatesRE;
67262         var ddText = this.disabledDatesText;
67263         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
67264         var ddaysText = this.disabledDaysText;
67265         var format = this.format;
67266         
67267         var setCellClass = function(cal, cell){
67268             
67269             //Roo.log('set Cell Class');
67270             cell.title = "";
67271             var t = d.getTime();
67272             
67273             //Roo.log(d);
67274             
67275             
67276             cell.dateValue = t;
67277             if(t == today){
67278                 cell.className += " fc-today";
67279                 cell.className += " fc-state-highlight";
67280                 cell.title = cal.todayText;
67281             }
67282             if(t == sel){
67283                 // disable highlight in other month..
67284                 cell.className += " fc-state-highlight";
67285                 
67286             }
67287             // disabling
67288             if(t < min) {
67289                 //cell.className = " fc-state-disabled";
67290                 cell.title = cal.minText;
67291                 return;
67292             }
67293             if(t > max) {
67294                 //cell.className = " fc-state-disabled";
67295                 cell.title = cal.maxText;
67296                 return;
67297             }
67298             if(ddays){
67299                 if(ddays.indexOf(d.getDay()) != -1){
67300                     // cell.title = ddaysText;
67301                    // cell.className = " fc-state-disabled";
67302                 }
67303             }
67304             if(ddMatch && format){
67305                 var fvalue = d.dateFormat(format);
67306                 if(ddMatch.test(fvalue)){
67307                     cell.title = ddText.replace("%0", fvalue);
67308                    cell.className = " fc-state-disabled";
67309                 }
67310             }
67311             
67312             if (!cell.initialClassName) {
67313                 cell.initialClassName = cell.dom.className;
67314             }
67315             
67316             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
67317         };
67318
67319         var i = 0;
67320         
67321         for(; i < startingPos; i++) {
67322             cells[i].dayName =  (++prevStart);
67323             Roo.log(textEls[i]);
67324             d.setDate(d.getDate()+1);
67325             
67326             //cells[i].className = "fc-past fc-other-month";
67327             setCellClass(this, cells[i]);
67328         }
67329         
67330         var intDay = 0;
67331         
67332         for(; i < days; i++){
67333             intDay = i - startingPos + 1;
67334             cells[i].dayName =  (intDay);
67335             d.setDate(d.getDate()+1);
67336             
67337             cells[i].className = ''; // "x-date-active";
67338             setCellClass(this, cells[i]);
67339         }
67340         var extraDays = 0;
67341         
67342         for(; i < 42; i++) {
67343             //textEls[i].innerHTML = (++extraDays);
67344             
67345             d.setDate(d.getDate()+1);
67346             cells[i].dayName = (++extraDays);
67347             cells[i].className = "fc-future fc-other-month";
67348             setCellClass(this, cells[i]);
67349         }
67350         
67351         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
67352         
67353         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
67354         
67355         // this will cause all the cells to mis
67356         var rows= [];
67357         var i =0;
67358         for (var r = 0;r < 6;r++) {
67359             for (var c =0;c < 7;c++) {
67360                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
67361             }    
67362         }
67363         
67364         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
67365         for(i=0;i<cells.length;i++) {
67366             
67367             this.cells.elements[i].dayName = cells[i].dayName ;
67368             this.cells.elements[i].className = cells[i].className;
67369             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
67370             this.cells.elements[i].title = cells[i].title ;
67371             this.cells.elements[i].dateValue = cells[i].dateValue ;
67372         }
67373         
67374         
67375         
67376         
67377         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
67378         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
67379         
67380         ////if(totalRows != 6){
67381             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
67382            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
67383        // }
67384         
67385         this.fireEvent('monthchange', this, date);
67386         
67387         
67388     },
67389  /**
67390      * Returns the grid's SelectionModel.
67391      * @return {SelectionModel}
67392      */
67393     getSelectionModel : function(){
67394         if(!this.selModel){
67395             this.selModel = new Roo.grid.CellSelectionModel();
67396         }
67397         return this.selModel;
67398     },
67399
67400     load: function() {
67401         this.eventStore.load()
67402         
67403         
67404         
67405     },
67406     
67407     findCell : function(dt) {
67408         dt = dt.clearTime().getTime();
67409         var ret = false;
67410         this.cells.each(function(c){
67411             //Roo.log("check " +c.dateValue + '?=' + dt);
67412             if(c.dateValue == dt){
67413                 ret = c;
67414                 return false;
67415             }
67416             return true;
67417         });
67418         
67419         return ret;
67420     },
67421     
67422     findCells : function(rec) {
67423         var s = rec.data.start_dt.clone().clearTime().getTime();
67424        // Roo.log(s);
67425         var e= rec.data.end_dt.clone().clearTime().getTime();
67426        // Roo.log(e);
67427         var ret = [];
67428         this.cells.each(function(c){
67429              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
67430             
67431             if(c.dateValue > e){
67432                 return ;
67433             }
67434             if(c.dateValue < s){
67435                 return ;
67436             }
67437             ret.push(c);
67438         });
67439         
67440         return ret;    
67441     },
67442     
67443     findBestRow: function(cells)
67444     {
67445         var ret = 0;
67446         
67447         for (var i =0 ; i < cells.length;i++) {
67448             ret  = Math.max(cells[i].rows || 0,ret);
67449         }
67450         return ret;
67451         
67452     },
67453     
67454     
67455     addItem : function(rec)
67456     {
67457         // look for vertical location slot in
67458         var cells = this.findCells(rec);
67459         
67460         rec.row = this.findBestRow(cells);
67461         
67462         // work out the location.
67463         
67464         var crow = false;
67465         var rows = [];
67466         for(var i =0; i < cells.length; i++) {
67467             if (!crow) {
67468                 crow = {
67469                     start : cells[i],
67470                     end :  cells[i]
67471                 };
67472                 continue;
67473             }
67474             if (crow.start.getY() == cells[i].getY()) {
67475                 // on same row.
67476                 crow.end = cells[i];
67477                 continue;
67478             }
67479             // different row.
67480             rows.push(crow);
67481             crow = {
67482                 start: cells[i],
67483                 end : cells[i]
67484             };
67485             
67486         }
67487         
67488         rows.push(crow);
67489         rec.els = [];
67490         rec.rows = rows;
67491         rec.cells = cells;
67492         for (var i = 0; i < cells.length;i++) {
67493             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
67494             
67495         }
67496         
67497         
67498     },
67499     
67500     clearEvents: function() {
67501         
67502         if (!this.eventStore.getCount()) {
67503             return;
67504         }
67505         // reset number of rows in cells.
67506         Roo.each(this.cells.elements, function(c){
67507             c.rows = 0;
67508         });
67509         
67510         this.eventStore.each(function(e) {
67511             this.clearEvent(e);
67512         },this);
67513         
67514     },
67515     
67516     clearEvent : function(ev)
67517     {
67518         if (ev.els) {
67519             Roo.each(ev.els, function(el) {
67520                 el.un('mouseenter' ,this.onEventEnter, this);
67521                 el.un('mouseleave' ,this.onEventLeave, this);
67522                 el.remove();
67523             },this);
67524             ev.els = [];
67525         }
67526     },
67527     
67528     
67529     renderEvent : function(ev,ctr) {
67530         if (!ctr) {
67531              ctr = this.view.el.select('.fc-event-container',true).first();
67532         }
67533         
67534          
67535         this.clearEvent(ev);
67536             //code
67537        
67538         
67539         
67540         ev.els = [];
67541         var cells = ev.cells;
67542         var rows = ev.rows;
67543         this.fireEvent('eventrender', this, ev);
67544         
67545         for(var i =0; i < rows.length; i++) {
67546             
67547             cls = '';
67548             if (i == 0) {
67549                 cls += ' fc-event-start';
67550             }
67551             if ((i+1) == rows.length) {
67552                 cls += ' fc-event-end';
67553             }
67554             
67555             //Roo.log(ev.data);
67556             // how many rows should it span..
67557             var cg = this.eventTmpl.append(ctr,Roo.apply({
67558                 fccls : cls
67559                 
67560             }, ev.data) , true);
67561             
67562             
67563             cg.on('mouseenter' ,this.onEventEnter, this, ev);
67564             cg.on('mouseleave' ,this.onEventLeave, this, ev);
67565             cg.on('click', this.onEventClick, this, ev);
67566             
67567             ev.els.push(cg);
67568             
67569             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
67570             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
67571             //Roo.log(cg);
67572              
67573             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
67574             cg.setWidth(ebox.right - sbox.x -2);
67575         }
67576     },
67577     
67578     renderEvents: function()
67579     {   
67580         // first make sure there is enough space..
67581         
67582         if (!this.eventTmpl) {
67583             this.eventTmpl = new Roo.Template(
67584                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
67585                     '<div class="fc-event-inner">' +
67586                         '<span class="fc-event-time">{time}</span>' +
67587                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
67588                     '</div>' +
67589                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
67590                 '</div>'
67591             );
67592                 
67593         }
67594                
67595         
67596         
67597         this.cells.each(function(c) {
67598             //Roo.log(c.select('.fc-day-content div',true).first());
67599             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
67600         });
67601         
67602         var ctr = this.view.el.select('.fc-event-container',true).first();
67603         
67604         var cls;
67605         this.eventStore.each(function(ev){
67606             
67607             this.renderEvent(ev);
67608              
67609              
67610         }, this);
67611         this.view.layout();
67612         
67613     },
67614     
67615     onEventEnter: function (e, el,event,d) {
67616         this.fireEvent('evententer', this, el, event);
67617     },
67618     
67619     onEventLeave: function (e, el,event,d) {
67620         this.fireEvent('eventleave', this, el, event);
67621     },
67622     
67623     onEventClick: function (e, el,event,d) {
67624         this.fireEvent('eventclick', this, el, event);
67625     },
67626     
67627     onMonthChange: function () {
67628         this.store.load();
67629     },
67630     
67631     onLoad: function () {
67632         
67633         //Roo.log('calendar onload');
67634 //         
67635         if(this.eventStore.getCount() > 0){
67636             
67637            
67638             
67639             this.eventStore.each(function(d){
67640                 
67641                 
67642                 // FIXME..
67643                 var add =   d.data;
67644                 if (typeof(add.end_dt) == 'undefined')  {
67645                     Roo.log("Missing End time in calendar data: ");
67646                     Roo.log(d);
67647                     return;
67648                 }
67649                 if (typeof(add.start_dt) == 'undefined')  {
67650                     Roo.log("Missing Start time in calendar data: ");
67651                     Roo.log(d);
67652                     return;
67653                 }
67654                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
67655                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
67656                 add.id = add.id || d.id;
67657                 add.title = add.title || '??';
67658                 
67659                 this.addItem(d);
67660                 
67661              
67662             },this);
67663         }
67664         
67665         this.renderEvents();
67666     }
67667     
67668
67669 });
67670 /*
67671  grid : {
67672                 xtype: 'Grid',
67673                 xns: Roo.grid,
67674                 listeners : {
67675                     render : function ()
67676                     {
67677                         _this.grid = this;
67678                         
67679                         if (!this.view.el.hasClass('course-timesheet')) {
67680                             this.view.el.addClass('course-timesheet');
67681                         }
67682                         if (this.tsStyle) {
67683                             this.ds.load({});
67684                             return; 
67685                         }
67686                         Roo.log('width');
67687                         Roo.log(_this.grid.view.el.getWidth());
67688                         
67689                         
67690                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
67691                             '.course-timesheet .x-grid-row' : {
67692                                 height: '80px'
67693                             },
67694                             '.x-grid-row td' : {
67695                                 'vertical-align' : 0
67696                             },
67697                             '.course-edit-link' : {
67698                                 'color' : 'blue',
67699                                 'text-overflow' : 'ellipsis',
67700                                 'overflow' : 'hidden',
67701                                 'white-space' : 'nowrap',
67702                                 'cursor' : 'pointer'
67703                             },
67704                             '.sub-link' : {
67705                                 'color' : 'green'
67706                             },
67707                             '.de-act-sup-link' : {
67708                                 'color' : 'purple',
67709                                 'text-decoration' : 'line-through'
67710                             },
67711                             '.de-act-link' : {
67712                                 'color' : 'red',
67713                                 'text-decoration' : 'line-through'
67714                             },
67715                             '.course-timesheet .course-highlight' : {
67716                                 'border-top-style': 'dashed !important',
67717                                 'border-bottom-bottom': 'dashed !important'
67718                             },
67719                             '.course-timesheet .course-item' : {
67720                                 'font-family'   : 'tahoma, arial, helvetica',
67721                                 'font-size'     : '11px',
67722                                 'overflow'      : 'hidden',
67723                                 'padding-left'  : '10px',
67724                                 'padding-right' : '10px',
67725                                 'padding-top' : '10px' 
67726                             }
67727                             
67728                         }, Roo.id());
67729                                 this.ds.load({});
67730                     }
67731                 },
67732                 autoWidth : true,
67733                 monitorWindowResize : false,
67734                 cellrenderer : function(v,x,r)
67735                 {
67736                     return v;
67737                 },
67738                 sm : {
67739                     xtype: 'CellSelectionModel',
67740                     xns: Roo.grid
67741                 },
67742                 dataSource : {
67743                     xtype: 'Store',
67744                     xns: Roo.data,
67745                     listeners : {
67746                         beforeload : function (_self, options)
67747                         {
67748                             options.params = options.params || {};
67749                             options.params._month = _this.monthField.getValue();
67750                             options.params.limit = 9999;
67751                             options.params['sort'] = 'when_dt';    
67752                             options.params['dir'] = 'ASC';    
67753                             this.proxy.loadResponse = this.loadResponse;
67754                             Roo.log("load?");
67755                             //this.addColumns();
67756                         },
67757                         load : function (_self, records, options)
67758                         {
67759                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
67760                                 // if you click on the translation.. you can edit it...
67761                                 var el = Roo.get(this);
67762                                 var id = el.dom.getAttribute('data-id');
67763                                 var d = el.dom.getAttribute('data-date');
67764                                 var t = el.dom.getAttribute('data-time');
67765                                 //var id = this.child('span').dom.textContent;
67766                                 
67767                                 //Roo.log(this);
67768                                 Pman.Dialog.CourseCalendar.show({
67769                                     id : id,
67770                                     when_d : d,
67771                                     when_t : t,
67772                                     productitem_active : id ? 1 : 0
67773                                 }, function() {
67774                                     _this.grid.ds.load({});
67775                                 });
67776                            
67777                            });
67778                            
67779                            _this.panel.fireEvent('resize', [ '', '' ]);
67780                         }
67781                     },
67782                     loadResponse : function(o, success, response){
67783                             // this is overridden on before load..
67784                             
67785                             Roo.log("our code?");       
67786                             //Roo.log(success);
67787                             //Roo.log(response)
67788                             delete this.activeRequest;
67789                             if(!success){
67790                                 this.fireEvent("loadexception", this, o, response);
67791                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67792                                 return;
67793                             }
67794                             var result;
67795                             try {
67796                                 result = o.reader.read(response);
67797                             }catch(e){
67798                                 Roo.log("load exception?");
67799                                 this.fireEvent("loadexception", this, o, response, e);
67800                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
67801                                 return;
67802                             }
67803                             Roo.log("ready...");        
67804                             // loop through result.records;
67805                             // and set this.tdate[date] = [] << array of records..
67806                             _this.tdata  = {};
67807                             Roo.each(result.records, function(r){
67808                                 //Roo.log(r.data);
67809                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
67810                                     _this.tdata[r.data.when_dt.format('j')] = [];
67811                                 }
67812                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
67813                             });
67814                             
67815                             //Roo.log(_this.tdata);
67816                             
67817                             result.records = [];
67818                             result.totalRecords = 6;
67819                     
67820                             // let's generate some duumy records for the rows.
67821                             //var st = _this.dateField.getValue();
67822                             
67823                             // work out monday..
67824                             //st = st.add(Date.DAY, -1 * st.format('w'));
67825                             
67826                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67827                             
67828                             var firstOfMonth = date.getFirstDayOfMonth();
67829                             var days = date.getDaysInMonth();
67830                             var d = 1;
67831                             var firstAdded = false;
67832                             for (var i = 0; i < result.totalRecords ; i++) {
67833                                 //var d= st.add(Date.DAY, i);
67834                                 var row = {};
67835                                 var added = 0;
67836                                 for(var w = 0 ; w < 7 ; w++){
67837                                     if(!firstAdded && firstOfMonth != w){
67838                                         continue;
67839                                     }
67840                                     if(d > days){
67841                                         continue;
67842                                     }
67843                                     firstAdded = true;
67844                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
67845                                     row['weekday'+w] = String.format(
67846                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
67847                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
67848                                                     d,
67849                                                     date.format('Y-m-')+dd
67850                                                 );
67851                                     added++;
67852                                     if(typeof(_this.tdata[d]) != 'undefined'){
67853                                         Roo.each(_this.tdata[d], function(r){
67854                                             var is_sub = '';
67855                                             var deactive = '';
67856                                             var id = r.id;
67857                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
67858                                             if(r.parent_id*1>0){
67859                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
67860                                                 id = r.parent_id;
67861                                             }
67862                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
67863                                                 deactive = 'de-act-link';
67864                                             }
67865                                             
67866                                             row['weekday'+w] += String.format(
67867                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
67868                                                     id, //0
67869                                                     r.product_id_name, //1
67870                                                     r.when_dt.format('h:ia'), //2
67871                                                     is_sub, //3
67872                                                     deactive, //4
67873                                                     desc // 5
67874                                             );
67875                                         });
67876                                     }
67877                                     d++;
67878                                 }
67879                                 
67880                                 // only do this if something added..
67881                                 if(added > 0){ 
67882                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
67883                                 }
67884                                 
67885                                 
67886                                 // push it twice. (second one with an hour..
67887                                 
67888                             }
67889                             //Roo.log(result);
67890                             this.fireEvent("load", this, o, o.request.arg);
67891                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
67892                         },
67893                     sortInfo : {field: 'when_dt', direction : 'ASC' },
67894                     proxy : {
67895                         xtype: 'HttpProxy',
67896                         xns: Roo.data,
67897                         method : 'GET',
67898                         url : baseURL + '/Roo/Shop_course.php'
67899                     },
67900                     reader : {
67901                         xtype: 'JsonReader',
67902                         xns: Roo.data,
67903                         id : 'id',
67904                         fields : [
67905                             {
67906                                 'name': 'id',
67907                                 'type': 'int'
67908                             },
67909                             {
67910                                 'name': 'when_dt',
67911                                 'type': 'string'
67912                             },
67913                             {
67914                                 'name': 'end_dt',
67915                                 'type': 'string'
67916                             },
67917                             {
67918                                 'name': 'parent_id',
67919                                 'type': 'int'
67920                             },
67921                             {
67922                                 'name': 'product_id',
67923                                 'type': 'int'
67924                             },
67925                             {
67926                                 'name': 'productitem_id',
67927                                 'type': 'int'
67928                             },
67929                             {
67930                                 'name': 'guid',
67931                                 'type': 'int'
67932                             }
67933                         ]
67934                     }
67935                 },
67936                 toolbar : {
67937                     xtype: 'Toolbar',
67938                     xns: Roo,
67939                     items : [
67940                         {
67941                             xtype: 'Button',
67942                             xns: Roo.Toolbar,
67943                             listeners : {
67944                                 click : function (_self, e)
67945                                 {
67946                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67947                                     sd.setMonth(sd.getMonth()-1);
67948                                     _this.monthField.setValue(sd.format('Y-m-d'));
67949                                     _this.grid.ds.load({});
67950                                 }
67951                             },
67952                             text : "Back"
67953                         },
67954                         {
67955                             xtype: 'Separator',
67956                             xns: Roo.Toolbar
67957                         },
67958                         {
67959                             xtype: 'MonthField',
67960                             xns: Roo.form,
67961                             listeners : {
67962                                 render : function (_self)
67963                                 {
67964                                     _this.monthField = _self;
67965                                    // _this.monthField.set  today
67966                                 },
67967                                 select : function (combo, date)
67968                                 {
67969                                     _this.grid.ds.load({});
67970                                 }
67971                             },
67972                             value : (function() { return new Date(); })()
67973                         },
67974                         {
67975                             xtype: 'Separator',
67976                             xns: Roo.Toolbar
67977                         },
67978                         {
67979                             xtype: 'TextItem',
67980                             xns: Roo.Toolbar,
67981                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
67982                         },
67983                         {
67984                             xtype: 'Fill',
67985                             xns: Roo.Toolbar
67986                         },
67987                         {
67988                             xtype: 'Button',
67989                             xns: Roo.Toolbar,
67990                             listeners : {
67991                                 click : function (_self, e)
67992                                 {
67993                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
67994                                     sd.setMonth(sd.getMonth()+1);
67995                                     _this.monthField.setValue(sd.format('Y-m-d'));
67996                                     _this.grid.ds.load({});
67997                                 }
67998                             },
67999                             text : "Next"
68000                         }
68001                     ]
68002                 },
68003                  
68004             }
68005         };
68006         
68007         *//*
68008  * Based on:
68009  * Ext JS Library 1.1.1
68010  * Copyright(c) 2006-2007, Ext JS, LLC.
68011  *
68012  * Originally Released Under LGPL - original licence link has changed is not relivant.
68013  *
68014  * Fork - LGPL
68015  * <script type="text/javascript">
68016  */
68017  
68018 /**
68019  * @class Roo.LoadMask
68020  * A simple utility class for generically masking elements while loading data.  If the element being masked has
68021  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
68022  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
68023  * element's UpdateManager load indicator and will be destroyed after the initial load.
68024  * @constructor
68025  * Create a new LoadMask
68026  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
68027  * @param {Object} config The config object
68028  */
68029 Roo.LoadMask = function(el, config){
68030     this.el = Roo.get(el);
68031     Roo.apply(this, config);
68032     if(this.store){
68033         this.store.on('beforeload', this.onBeforeLoad, this);
68034         this.store.on('load', this.onLoad, this);
68035         this.store.on('loadexception', this.onLoadException, this);
68036         this.removeMask = false;
68037     }else{
68038         var um = this.el.getUpdateManager();
68039         um.showLoadIndicator = false; // disable the default indicator
68040         um.on('beforeupdate', this.onBeforeLoad, this);
68041         um.on('update', this.onLoad, this);
68042         um.on('failure', this.onLoad, this);
68043         this.removeMask = true;
68044     }
68045 };
68046
68047 Roo.LoadMask.prototype = {
68048     /**
68049      * @cfg {Boolean} removeMask
68050      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
68051      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
68052      */
68053     removeMask : false,
68054     /**
68055      * @cfg {String} msg
68056      * The text to display in a centered loading message box (defaults to 'Loading...')
68057      */
68058     msg : 'Loading...',
68059     /**
68060      * @cfg {String} msgCls
68061      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
68062      */
68063     msgCls : 'x-mask-loading',
68064
68065     /**
68066      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
68067      * @type Boolean
68068      */
68069     disabled: false,
68070
68071     /**
68072      * Disables the mask to prevent it from being displayed
68073      */
68074     disable : function(){
68075        this.disabled = true;
68076     },
68077
68078     /**
68079      * Enables the mask so that it can be displayed
68080      */
68081     enable : function(){
68082         this.disabled = false;
68083     },
68084     
68085     onLoadException : function()
68086     {
68087         Roo.log(arguments);
68088         
68089         if (typeof(arguments[3]) != 'undefined') {
68090             Roo.MessageBox.alert("Error loading",arguments[3]);
68091         } 
68092         /*
68093         try {
68094             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
68095                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
68096             }   
68097         } catch(e) {
68098             
68099         }
68100         */
68101     
68102         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
68103     },
68104     // private
68105     onLoad : function()
68106     {
68107         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
68108     },
68109
68110     // private
68111     onBeforeLoad : function(){
68112         if(!this.disabled){
68113             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
68114         }
68115     },
68116
68117     // private
68118     destroy : function(){
68119         if(this.store){
68120             this.store.un('beforeload', this.onBeforeLoad, this);
68121             this.store.un('load', this.onLoad, this);
68122             this.store.un('loadexception', this.onLoadException, this);
68123         }else{
68124             var um = this.el.getUpdateManager();
68125             um.un('beforeupdate', this.onBeforeLoad, this);
68126             um.un('update', this.onLoad, this);
68127             um.un('failure', this.onLoad, this);
68128         }
68129     }
68130 };/*
68131  * Based on:
68132  * Ext JS Library 1.1.1
68133  * Copyright(c) 2006-2007, Ext JS, LLC.
68134  *
68135  * Originally Released Under LGPL - original licence link has changed is not relivant.
68136  *
68137  * Fork - LGPL
68138  * <script type="text/javascript">
68139  */
68140
68141
68142 /**
68143  * @class Roo.XTemplate
68144  * @extends Roo.Template
68145  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
68146 <pre><code>
68147 var t = new Roo.XTemplate(
68148         '&lt;select name="{name}"&gt;',
68149                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
68150         '&lt;/select&gt;'
68151 );
68152  
68153 // then append, applying the master template values
68154  </code></pre>
68155  *
68156  * Supported features:
68157  *
68158  *  Tags:
68159
68160 <pre><code>
68161       {a_variable} - output encoded.
68162       {a_variable.format:("Y-m-d")} - call a method on the variable
68163       {a_variable:raw} - unencoded output
68164       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
68165       {a_variable:this.method_on_template(...)} - call a method on the template object.
68166  
68167 </code></pre>
68168  *  The tpl tag:
68169 <pre><code>
68170         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
68171         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
68172         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
68173         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
68174   
68175         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
68176         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
68177 </code></pre>
68178  *      
68179  */
68180 Roo.XTemplate = function()
68181 {
68182     Roo.XTemplate.superclass.constructor.apply(this, arguments);
68183     if (this.html) {
68184         this.compile();
68185     }
68186 };
68187
68188
68189 Roo.extend(Roo.XTemplate, Roo.Template, {
68190
68191     /**
68192      * The various sub templates
68193      */
68194     tpls : false,
68195     /**
68196      *
68197      * basic tag replacing syntax
68198      * WORD:WORD()
68199      *
68200      * // you can fake an object call by doing this
68201      *  x.t:(test,tesT) 
68202      * 
68203      */
68204     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
68205
68206     /**
68207      * compile the template
68208      *
68209      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
68210      *
68211      */
68212     compile: function()
68213     {
68214         var s = this.html;
68215      
68216         s = ['<tpl>', s, '</tpl>'].join('');
68217     
68218         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
68219             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
68220             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
68221             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
68222             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
68223             m,
68224             id     = 0,
68225             tpls   = [];
68226     
68227         while(true == !!(m = s.match(re))){
68228             var forMatch   = m[0].match(nameRe),
68229                 ifMatch   = m[0].match(ifRe),
68230                 execMatch   = m[0].match(execRe),
68231                 namedMatch   = m[0].match(namedRe),
68232                 
68233                 exp  = null, 
68234                 fn   = null,
68235                 exec = null,
68236                 name = forMatch && forMatch[1] ? forMatch[1] : '';
68237                 
68238             if (ifMatch) {
68239                 // if - puts fn into test..
68240                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
68241                 if(exp){
68242                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
68243                 }
68244             }
68245             
68246             if (execMatch) {
68247                 // exec - calls a function... returns empty if true is  returned.
68248                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
68249                 if(exp){
68250                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
68251                 }
68252             }
68253             
68254             
68255             if (name) {
68256                 // for = 
68257                 switch(name){
68258                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
68259                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
68260                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
68261                 }
68262             }
68263             var uid = namedMatch ? namedMatch[1] : id;
68264             
68265             
68266             tpls.push({
68267                 id:     namedMatch ? namedMatch[1] : id,
68268                 target: name,
68269                 exec:   exec,
68270                 test:   fn,
68271                 body:   m[1] || ''
68272             });
68273             if (namedMatch) {
68274                 s = s.replace(m[0], '');
68275             } else { 
68276                 s = s.replace(m[0], '{xtpl'+ id + '}');
68277             }
68278             ++id;
68279         }
68280         this.tpls = [];
68281         for(var i = tpls.length-1; i >= 0; --i){
68282             this.compileTpl(tpls[i]);
68283             this.tpls[tpls[i].id] = tpls[i];
68284         }
68285         this.master = tpls[tpls.length-1];
68286         return this;
68287     },
68288     /**
68289      * same as applyTemplate, except it's done to one of the subTemplates
68290      * when using named templates, you can do:
68291      *
68292      * var str = pl.applySubTemplate('your-name', values);
68293      *
68294      * 
68295      * @param {Number} id of the template
68296      * @param {Object} values to apply to template
68297      * @param {Object} parent (normaly the instance of this object)
68298      */
68299     applySubTemplate : function(id, values, parent)
68300     {
68301         
68302         
68303         var t = this.tpls[id];
68304         
68305         
68306         try { 
68307             if(t.test && !t.test.call(this, values, parent)){
68308                 return '';
68309             }
68310         } catch(e) {
68311             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
68312             Roo.log(e.toString());
68313             Roo.log(t.test);
68314             return ''
68315         }
68316         try { 
68317             
68318             if(t.exec && t.exec.call(this, values, parent)){
68319                 return '';
68320             }
68321         } catch(e) {
68322             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
68323             Roo.log(e.toString());
68324             Roo.log(t.exec);
68325             return ''
68326         }
68327         try {
68328             var vs = t.target ? t.target.call(this, values, parent) : values;
68329             parent = t.target ? values : parent;
68330             if(t.target && vs instanceof Array){
68331                 var buf = [];
68332                 for(var i = 0, len = vs.length; i < len; i++){
68333                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
68334                 }
68335                 return buf.join('');
68336             }
68337             return t.compiled.call(this, vs, parent);
68338         } catch (e) {
68339             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
68340             Roo.log(e.toString());
68341             Roo.log(t.compiled);
68342             return '';
68343         }
68344     },
68345
68346     compileTpl : function(tpl)
68347     {
68348         var fm = Roo.util.Format;
68349         var useF = this.disableFormats !== true;
68350         var sep = Roo.isGecko ? "+" : ",";
68351         var undef = function(str) {
68352             Roo.log("Property not found :"  + str);
68353             return '';
68354         };
68355         
68356         var fn = function(m, name, format, args)
68357         {
68358             //Roo.log(arguments);
68359             args = args ? args.replace(/\\'/g,"'") : args;
68360             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
68361             if (typeof(format) == 'undefined') {
68362                 format= 'htmlEncode';
68363             }
68364             if (format == 'raw' ) {
68365                 format = false;
68366             }
68367             
68368             if(name.substr(0, 4) == 'xtpl'){
68369                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
68370             }
68371             
68372             // build an array of options to determine if value is undefined..
68373             
68374             // basically get 'xxxx.yyyy' then do
68375             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
68376             //    (function () { Roo.log("Property not found"); return ''; })() :
68377             //    ......
68378             
68379             var udef_ar = [];
68380             var lookfor = '';
68381             Roo.each(name.split('.'), function(st) {
68382                 lookfor += (lookfor.length ? '.': '') + st;
68383                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
68384             });
68385             
68386             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
68387             
68388             
68389             if(format && useF){
68390                 
68391                 args = args ? ',' + args : "";
68392                  
68393                 if(format.substr(0, 5) != "this."){
68394                     format = "fm." + format + '(';
68395                 }else{
68396                     format = 'this.call("'+ format.substr(5) + '", ';
68397                     args = ", values";
68398                 }
68399                 
68400                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
68401             }
68402              
68403             if (args.length) {
68404                 // called with xxyx.yuu:(test,test)
68405                 // change to ()
68406                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
68407             }
68408             // raw.. - :raw modifier..
68409             return "'"+ sep + udef_st  + name + ")"+sep+"'";
68410             
68411         };
68412         var body;
68413         // branched to use + in gecko and [].join() in others
68414         if(Roo.isGecko){
68415             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
68416                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
68417                     "';};};";
68418         }else{
68419             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
68420             body.push(tpl.body.replace(/(\r\n|\n)/g,
68421                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
68422             body.push("'].join('');};};");
68423             body = body.join('');
68424         }
68425         
68426         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
68427        
68428         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
68429         eval(body);
68430         
68431         return this;
68432     },
68433
68434     applyTemplate : function(values){
68435         return this.master.compiled.call(this, values, {});
68436         //var s = this.subs;
68437     },
68438
68439     apply : function(){
68440         return this.applyTemplate.apply(this, arguments);
68441     }
68442
68443  });
68444
68445 Roo.XTemplate.from = function(el){
68446     el = Roo.getDom(el);
68447     return new Roo.XTemplate(el.value || el.innerHTML);
68448 };Roo.dialog = {};
68449 /*
68450 * Licence: LGPL
68451 */
68452
68453 /**
68454  * @class Roo.dialog.UploadCropbox
68455  * @extends Roo.BoxComponent
68456  * Dialog UploadCropbox class
68457  * @cfg {String} emptyText show when image has been loaded
68458  * @cfg {String} rotateNotify show when image too small to rotate
68459  * @cfg {Number} errorTimeout default 3000
68460  * @cfg {Number} minWidth default 300
68461  * @cfg {Number} minHeight default 300
68462  * @cfg {Number} outputMaxWidth default 1200
68463  * @cfg {Number} windowSize default 300
68464  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
68465  * @cfg {Boolean} isDocument (true|false) default false
68466  * @cfg {String} url action url
68467  * @cfg {String} paramName default 'imageUpload'
68468  * @cfg {String} method default POST
68469  * @cfg {Boolean} loadMask (true|false) default true
68470  * @cfg {Boolean} loadingText default 'Loading...'
68471  * 
68472  * @constructor
68473  * Create a new UploadCropbox
68474  * @param {Object} config The config object
68475  */
68476
68477  Roo.dialog.UploadCropbox = function(config){
68478     Roo.dialog.UploadCropbox.superclass.constructor.call(this, config);
68479     
68480     this.addEvents({
68481         /**
68482          * @event beforeselectfile
68483          * Fire before select file
68484          * @param {Roo.dialog.UploadCropbox} this
68485          */
68486         "beforeselectfile" : true,
68487         /**
68488          * @event initial
68489          * Fire after initEvent
68490          * @param {Roo.dialog.UploadCropbox} this
68491          */
68492         "initial" : true,
68493         /**
68494          * @event crop
68495          * Fire after initEvent
68496          * @param {Roo.dialog.UploadCropbox} this
68497          * @param {String} data
68498          */
68499         "crop" : true,
68500         /**
68501          * @event prepare
68502          * Fire when preparing the file data
68503          * @param {Roo.dialog.UploadCropbox} this
68504          * @param {Object} file
68505          */
68506         "prepare" : true,
68507         /**
68508          * @event exception
68509          * Fire when get exception
68510          * @param {Roo.dialog.UploadCropbox} this
68511          * @param {XMLHttpRequest} xhr
68512          */
68513         "exception" : true,
68514         /**
68515          * @event beforeloadcanvas
68516          * Fire before load the canvas
68517          * @param {Roo.dialog.UploadCropbox} this
68518          * @param {String} src
68519          */
68520         "beforeloadcanvas" : true,
68521         /**
68522          * @event trash
68523          * Fire when trash image
68524          * @param {Roo.dialog.UploadCropbox} this
68525          */
68526         "trash" : true,
68527         /**
68528          * @event download
68529          * Fire when download the image
68530          * @param {Roo.dialog.UploadCropbox} this
68531          */
68532         "download" : true,
68533         /**
68534          * @event footerbuttonclick
68535          * Fire when footerbuttonclick
68536          * @param {Roo.dialog.UploadCropbox} this
68537          * @param {String} type
68538          */
68539         "footerbuttonclick" : true,
68540         /**
68541          * @event resize
68542          * Fire when resize
68543          * @param {Roo.dialog.UploadCropbox} this
68544          */
68545         "resize" : true,
68546         /**
68547          * @event rotate
68548          * Fire when rotate the image
68549          * @param {Roo.dialog.UploadCropbox} this
68550          * @param {String} pos
68551          */
68552         "rotate" : true,
68553         /**
68554          * @event inspect
68555          * Fire when inspect the file
68556          * @param {Roo.dialog.UploadCropbox} this
68557          * @param {Object} file
68558          */
68559         "inspect" : true,
68560         /**
68561          * @event upload
68562          * Fire when xhr upload the file
68563          * @param {Roo.dialog.UploadCropbox} this
68564          * @param {Object} data
68565          */
68566         "upload" : true,
68567         /**
68568          * @event arrange
68569          * Fire when arrange the file data
68570          * @param {Roo.dialog.UploadCropbox} this
68571          * @param {Object} formData
68572          */
68573         "arrange" : true,
68574         /**
68575          * @event loadcanvas
68576          * Fire after load the canvas
68577          * @param {Roo.dialog.UploadCropbox}
68578          * @param {Object} imgEl
68579          */
68580         "loadcanvas" : true
68581     });
68582     
68583     this.buttons = this.buttons || Roo.dialog.UploadCropbox.footer.STANDARD;
68584 };
68585
68586 Roo.extend(Roo.dialog.UploadCropbox, Roo.Component,  {
68587     
68588     emptyText : 'Click to upload image',
68589     rotateNotify : 'Image is too small to rotate',
68590     errorTimeout : 3000,
68591     scale : 0,
68592     baseScale : 1,
68593     rotate : 0,
68594     dragable : false,
68595     pinching : false,
68596     mouseX : 0,
68597     mouseY : 0,
68598     cropData : false,
68599     minWidth : 300,
68600     minHeight : 300,
68601     outputMaxWidth : 1200,
68602     windowSize : 300,
68603     file : false,
68604     exif : {},
68605     baseRotate : 1,
68606     cropType : 'image/jpeg',
68607     buttons : false,
68608     canvasLoaded : false,
68609     isDocument : false,
68610     method : 'POST',
68611     paramName : 'imageUpload',
68612     loadMask : true,
68613     loadingText : 'Loading...',
68614     maskEl : false,
68615     
68616     getAutoCreate : function()
68617     {
68618         var cfg = {
68619             tag : 'div',
68620             cls : 'roo-upload-cropbox',
68621             cn : [
68622                 {
68623                     tag : 'input',
68624                     cls : 'roo-upload-cropbox-selector',
68625                     type : 'file'
68626                 },
68627                 {
68628                     tag : 'div',
68629                     cls : 'roo-upload-cropbox-body',
68630                     style : 'cursor:pointer',
68631                     cn : [
68632                         {
68633                             tag : 'div',
68634                             cls : 'roo-upload-cropbox-preview'
68635                         },
68636                         {
68637                             tag : 'div',
68638                             cls : 'roo-upload-cropbox-thumb'
68639                         },
68640                         {
68641                             tag : 'div',
68642                             cls : 'roo-upload-cropbox-empty-notify',
68643                             html : this.emptyText
68644                         },
68645                         {
68646                             tag : 'div',
68647                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
68648                             html : this.rotateNotify
68649                         }
68650                     ]
68651                 },
68652                 {
68653                     tag : 'div',
68654                     cls : 'roo-upload-cropbox-footer',
68655                     cn : {
68656                         tag : 'div',
68657                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
68658                         cn : []
68659                     }
68660                 }
68661             ]
68662         };
68663         
68664         return cfg;
68665     },
68666     
68667     onRender : function(ct, position)
68668     {
68669         Roo.dialog.UploadCropbox.superclass.onRender.call(this, ct, position);
68670
68671         if(this.el){
68672             if (this.el.attr('xtype')) {
68673                 this.el.attr('xtypex', this.el.attr('xtype'));
68674                 this.el.dom.removeAttribute('xtype');
68675                 
68676                 this.initEvents();
68677             }
68678         }
68679         else {
68680             var cfg = Roo.apply({},  this.getAutoCreate());
68681         
68682             cfg.id = this.id || Roo.id();
68683             
68684             if (this.cls) {
68685                 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
68686             }
68687             
68688             if (this.style) { // fixme needs to support more complex style data.
68689                 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
68690             }
68691             
68692             this.el = ct.createChild(cfg, position);
68693             
68694             this.initEvents();
68695         }
68696         
68697         if (this.buttons.length) {
68698             
68699             Roo.each(this.buttons, function(bb) {
68700                 
68701                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
68702                 
68703                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
68704                 
68705             }, this);
68706         }
68707         
68708         if(this.loadMask){
68709             this.maskEl = this.el;
68710         }
68711     },
68712     
68713     initEvents : function()
68714     {
68715         this.urlAPI = (window.createObjectURL && window) || 
68716                                 (window.URL && URL.revokeObjectURL && URL) || 
68717                                 (window.webkitURL && webkitURL);
68718                         
68719         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
68720         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68721         
68722         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
68723         this.selectorEl.hide();
68724         
68725         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
68726         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68727         
68728         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
68729         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68730         this.thumbEl.hide();
68731         
68732         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
68733         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68734         
68735         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
68736         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68737         this.errorEl.hide();
68738         
68739         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
68740         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
68741         this.footerEl.hide();
68742         
68743         this.setThumbBoxSize();
68744         
68745         this.bind();
68746         
68747         this.resize();
68748         
68749         this.fireEvent('initial', this);
68750     },
68751
68752     bind : function()
68753     {
68754         var _this = this;
68755         
68756         window.addEventListener("resize", function() { _this.resize(); } );
68757         
68758         this.bodyEl.on('click', this.beforeSelectFile, this);
68759         
68760         if(Roo.isTouch){
68761             this.bodyEl.on('touchstart', this.onTouchStart, this);
68762             this.bodyEl.on('touchmove', this.onTouchMove, this);
68763             this.bodyEl.on('touchend', this.onTouchEnd, this);
68764         }
68765         
68766         if(!Roo.isTouch){
68767             this.bodyEl.on('mousedown', this.onMouseDown, this);
68768             this.bodyEl.on('mousemove', this.onMouseMove, this);
68769             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
68770             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
68771             Roo.get(document).on('mouseup', this.onMouseUp, this);
68772         }
68773         
68774         this.selectorEl.on('change', this.onFileSelected, this);
68775     },
68776     
68777     reset : function()
68778     {    
68779         this.scale = 0;
68780         this.baseScale = 1;
68781         this.rotate = 0;
68782         this.baseRotate = 1;
68783         this.dragable = false;
68784         this.pinching = false;
68785         this.mouseX = 0;
68786         this.mouseY = 0;
68787         this.cropData = false;
68788         this.notifyEl.dom.innerHTML = this.emptyText;
68789         
68790         // this.selectorEl.dom.value = '';
68791         
68792     },
68793     
68794     resize : function()
68795     {
68796         if(this.fireEvent('resize', this) != false){
68797             this.setThumbBoxPosition();
68798             this.setCanvasPosition();
68799         }
68800     },
68801     
68802     onFooterButtonClick : function(e, el, o, type)
68803     {
68804         switch (type) {
68805             case 'rotate-left' :
68806                 this.onRotateLeft(e);
68807                 break;
68808             case 'rotate-right' :
68809                 this.onRotateRight(e);
68810                 break;
68811             case 'picture' :
68812                 this.beforeSelectFile(e);
68813                 break;
68814             case 'trash' :
68815                 this.trash(e);
68816                 break;
68817             case 'crop' :
68818                 this.crop(e);
68819                 break;
68820             case 'download' :
68821                 this.download(e);
68822                 break;
68823             case 'center' :
68824                 this.center(e);
68825                 break;
68826             default :
68827                 break;
68828         }
68829         
68830         this.fireEvent('footerbuttonclick', this, type);
68831     },
68832     
68833     beforeSelectFile : function(e)
68834     {
68835         e.preventDefault();
68836         
68837         if(this.fireEvent('beforeselectfile', this) != false){
68838             this.selectorEl.dom.click();
68839         }
68840     },
68841     
68842     onFileSelected : function(e)
68843     {
68844         e.preventDefault();
68845         
68846         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
68847             return;
68848         }
68849         
68850         var file = this.selectorEl.dom.files[0];
68851         
68852         if(this.fireEvent('inspect', this, file) != false){
68853             this.prepare(file);
68854         }
68855         
68856     },
68857     
68858     trash : function(e)
68859     {
68860         this.fireEvent('trash', this);
68861     },
68862     
68863     download : function(e)
68864     {
68865         this.fireEvent('download', this);
68866     },
68867
68868     center : function(e)
68869     {
68870         this.setCanvasPosition();
68871     },
68872     
68873     loadCanvas : function(src)
68874     {   
68875         if(this.fireEvent('beforeloadcanvas', this, src) != false){
68876             
68877             this.reset();
68878             
68879             this.imageEl = document.createElement('img');
68880             
68881             var _this = this;
68882             
68883             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
68884             
68885             this.imageEl.src = src;
68886         }
68887     },
68888     
68889     onLoadCanvas : function()
68890     {   
68891         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
68892         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
68893
68894         if(this.fireEvent('loadcanvas', this, this.imageEl) != false){
68895         
68896             this.bodyEl.un('click', this.beforeSelectFile, this);
68897             
68898             this.notifyEl.hide();
68899             this.thumbEl.show();
68900             this.footerEl.show();
68901             
68902             this.baseRotateLevel();
68903             
68904             if(this.isDocument){
68905                 this.setThumbBoxSize();
68906             }
68907             
68908             this.setThumbBoxPosition();
68909             
68910             this.baseScaleLevel();
68911             
68912             this.draw();
68913             
68914             this.resize();
68915             
68916             this.canvasLoaded = true;
68917         
68918         }
68919         
68920         if(this.loadMask){
68921             this.maskEl.unmask();
68922         }
68923         
68924     },
68925     
68926     setCanvasPosition : function(center = true)
68927     {   
68928         if(!this.canvasEl){
68929             return;
68930         }
68931
68932         var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
68933         var newCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
68934
68935         if(center) {
68936             this.previewEl.setLeft(newCenterLeft);
68937             this.previewEl.setTop(newCenterTop);
68938
68939             return;
68940         }
68941         
68942         var oldScaleLevel = this.baseScale * Math.pow(1.02, this.startScale);
68943         var oldCanvasWidth = Math.floor(this.imageEl.OriginWidth * oldScaleLevel);
68944         var oldCanvasHeight = Math.floor(this.imageEl.OriginHeight * oldScaleLevel);
68945
68946         var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - oldCanvasWidth) / 2);
68947         var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - oldCanvasHeight) / 2);
68948
68949         var leftDiff = newCenterLeft - oldCenterLeft;
68950         var topDiff = newCenterTop - oldCenterTop;
68951
68952         var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
68953         var newPreviewTop = this.previewEl.getTop(true) + topDiff;
68954
68955         this.previewEl.setLeft(newPreviewLeft);
68956         this.previewEl.setTop(newPreviewTop);
68957         
68958     },
68959     
68960     onMouseDown : function(e)
68961     {   
68962         e.stopEvent();
68963         
68964         this.dragable = true;
68965         this.pinching = false;
68966         
68967         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
68968             this.dragable = false;
68969             return;
68970         }
68971         
68972         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
68973         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
68974         
68975     },
68976     
68977     onMouseMove : function(e)
68978     {   
68979         e.stopEvent();
68980         
68981         if(!this.canvasLoaded){
68982             return;
68983         }
68984         
68985         if (!this.dragable){
68986             return;
68987         }
68988
68989         var maxPaddingLeft = this.canvasEl.width / 0.9 * 0.05;
68990         var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
68991
68992         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
68993             maxPaddingLeft = (this.canvasEl.height * this.minWidth / this.minHeight - this.canvasEl.width) / 2 + maxPaddingLeft;
68994         }
68995
68996         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
68997             maxPaddingTop = (this.canvasEl.width * this.minHeight / this.minWidth - this.canvasEl.height) / 2 + maxPaddingTop;
68998         }
68999         
69000         var minX = Math.ceil(this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - this.canvasEl.width - maxPaddingLeft);
69001         var minY = Math.ceil(this.thumbEl.getTop(true) + this.thumbEl.getHeight() - this.canvasEl.height - maxPaddingTop);
69002         
69003         var maxX = Math.ceil(this.thumbEl.getLeft(true) + maxPaddingLeft);
69004         var maxY = Math.ceil(this.thumbEl.getTop(true) +  maxPaddingTop);
69005
69006         if(minX > maxX) {
69007             var tempX = minX;
69008             minX = maxX;
69009             maxX = tempX;
69010         }
69011
69012         if(minY > maxY) {
69013             var tempY = minY;
69014             minY = maxY;
69015             maxY = tempY;
69016         }
69017
69018         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
69019         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
69020         
69021         x = x - this.mouseX;
69022         y = y - this.mouseY;
69023
69024         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
69025         var bgY = Math.ceil(y + this.previewEl.getTop(true));
69026         
69027         bgX = (bgX < minX) ? minX : ((bgX > maxX) ? maxX : bgX);
69028         bgY = (bgY < minY) ? minY : ((bgY > maxY) ? maxY : bgY);
69029         
69030         this.previewEl.setLeft(bgX);
69031         this.previewEl.setTop(bgY);
69032         
69033         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
69034         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
69035     },
69036     
69037     onMouseUp : function(e)
69038     {   
69039         e.stopEvent();
69040         
69041         this.dragable = false;
69042     },
69043     
69044     onMouseWheel : function(e)
69045     {   
69046         e.stopEvent();
69047         
69048         this.startScale = this.scale;
69049         this.scale = (e.getWheelDelta() > 0) ? (this.scale + 1) : (this.scale - 1);
69050         
69051         if(!this.zoomable()){
69052             this.scale = this.startScale;
69053             return;
69054         }
69055
69056         
69057         this.draw();
69058         
69059         return;
69060     },
69061     
69062     zoomable : function()
69063     {
69064         var minScale = this.thumbEl.getWidth() / this.minWidth;
69065         
69066         if(this.minWidth < this.minHeight){
69067             minScale = this.thumbEl.getHeight() / this.minHeight;
69068         }
69069         
69070         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
69071         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
69072  
69073         var maxWidth = this.imageEl.OriginWidth;
69074         var maxHeight = this.imageEl.OriginHeight;
69075
69076
69077         var newCanvasWidth = Math.floor(this.imageEl.OriginWidth * this.getScaleLevel());
69078         var newCanvasHeight = Math.floor(this.imageEl.OriginHeight * this.getScaleLevel());
69079
69080         var oldCenterLeft = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
69081         var oldCenterTop = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
69082
69083         var newCenterLeft = Math.ceil((this.bodyEl.getWidth() - newCanvasWidth) / 2);
69084         var newCenterTop = Math.ceil((this.bodyEl.getHeight() - newCanvasHeight) / 2);
69085
69086         var leftDiff = newCenterLeft - oldCenterLeft;
69087         var topDiff = newCenterTop - oldCenterTop;
69088
69089         var newPreviewLeft = this.previewEl.getLeft(true) + leftDiff;
69090         var newPreviewTop = this.previewEl.getTop(true) + topDiff;
69091
69092         var paddingLeft = newPreviewLeft - this.thumbEl.getLeft(true);
69093         var paddingTop = newPreviewTop - this.thumbEl.getTop(true);
69094
69095         var paddingRight = this.thumbEl.getLeft(true) + this.thumbEl.getWidth() - newCanvasWidth - newPreviewLeft;
69096         var paddingBottom = this.thumbEl.getTop(true) + this.thumbEl.getHeight() - newCanvasHeight - newPreviewTop;
69097
69098         var maxPaddingLeft = newCanvasWidth / 0.9 * 0.05;
69099         var maxPaddingTop = maxPaddingLeft * this.minHeight / this.minWidth;
69100
69101         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight <= this.minWidth / this.minHeight)) {
69102             maxPaddingLeft = (newCanvasHeight * this.minWidth / this.minHeight - newCanvasWidth) / 2 + maxPaddingLeft;
69103         }
69104
69105         if ((this.imageEl.OriginWidth / this.imageEl.OriginHeight >= this.minWidth / this.minHeight)) {
69106             maxPaddingTop = (newCanvasWidth * this.minHeight / this.minWidth - newCanvasHeight) / 2 + maxPaddingTop;
69107         }
69108         
69109         if(
69110                 this.isDocument &&
69111                 (this.rotate == 0 || this.rotate == 180) && 
69112                 (
69113                     width > this.imageEl.OriginWidth || 
69114                     height > this.imageEl.OriginHeight ||
69115                     (width < this.minWidth && height < this.minHeight)
69116                 )
69117         ){
69118             return false;
69119         }
69120         
69121         if(
69122                 this.isDocument &&
69123                 (this.rotate == 90 || this.rotate == 270) && 
69124                 (
69125                     width > this.imageEl.OriginWidth || 
69126                     height > this.imageEl.OriginHeight ||
69127                     (width < this.minHeight && height < this.minWidth)
69128                 )
69129         ){
69130             return false;
69131         }
69132         
69133         if(
69134                 !this.isDocument &&
69135                 (this.rotate == 0 || this.rotate == 180) && 
69136                 (
69137                     // for zoom out
69138                     paddingLeft > maxPaddingLeft ||
69139                     paddingRight > maxPaddingLeft ||
69140                     paddingTop > maxPaddingTop ||
69141                     paddingBottom > maxPaddingTop ||
69142                     // for zoom in
69143                     width > maxWidth ||
69144                     height > maxHeight
69145                 )
69146         ){
69147             return false;
69148         }
69149         
69150         if(
69151                 !this.isDocument &&
69152                 (this.rotate == 90 || this.rotate == 270) && 
69153                 (
69154                     width < this.minHeight || 
69155                     width > this.imageEl.OriginWidth || 
69156                     height < this.minWidth || 
69157                     height > this.imageEl.OriginHeight
69158                 )
69159         ){
69160             return false;
69161         }
69162         
69163         return true;
69164         
69165     },
69166     
69167     onRotateLeft : function(e)
69168     {   
69169         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
69170             
69171             var minScale = this.thumbEl.getWidth() / this.minWidth;
69172             
69173             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
69174             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
69175             
69176             this.startScale = this.scale;
69177             
69178             while (this.getScaleLevel() < minScale){
69179             
69180                 this.scale = this.scale + 1;
69181                 
69182                 if(!this.zoomable()){
69183                     break;
69184                 }
69185                 
69186                 if(
69187                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
69188                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
69189                 ){
69190                     continue;
69191                 }
69192                 
69193                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
69194
69195                 this.draw();
69196                 
69197                 return;
69198             }
69199             
69200             this.scale = this.startScale;
69201             
69202             this.onRotateFail();
69203             
69204             return false;
69205         }
69206         
69207         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
69208
69209         if(this.isDocument){
69210             this.setThumbBoxSize();
69211             this.setThumbBoxPosition();
69212             this.setCanvasPosition();
69213         }
69214         
69215         this.draw();
69216         
69217         this.fireEvent('rotate', this, 'left');
69218         
69219     },
69220     
69221     onRotateRight : function(e)
69222     {
69223         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
69224             
69225             var minScale = this.thumbEl.getWidth() / this.minWidth;
69226         
69227             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
69228             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
69229             
69230             this.startScale = this.scale;
69231             
69232             while (this.getScaleLevel() < minScale){
69233             
69234                 this.scale = this.scale + 1;
69235                 
69236                 if(!this.zoomable()){
69237                     break;
69238                 }
69239                 
69240                 if(
69241                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
69242                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
69243                 ){
69244                     continue;
69245                 }
69246                 
69247                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
69248
69249                 this.draw();
69250                 
69251                 return;
69252             }
69253             
69254             this.scale = this.startScale;
69255             
69256             this.onRotateFail();
69257             
69258             return false;
69259         }
69260         
69261         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
69262
69263         if(this.isDocument){
69264             this.setThumbBoxSize();
69265             this.setThumbBoxPosition();
69266             this.setCanvasPosition();
69267         }
69268         
69269         this.draw();
69270         
69271         this.fireEvent('rotate', this, 'right');
69272     },
69273     
69274     onRotateFail : function()
69275     {
69276         this.errorEl.show(true);
69277         
69278         var _this = this;
69279         
69280         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
69281     },
69282     
69283     draw : function()
69284     {
69285         this.previewEl.dom.innerHTML = '';
69286         
69287         var canvasEl = document.createElement("canvas");
69288         
69289         var contextEl = canvasEl.getContext("2d");
69290         
69291         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69292         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69293         var center = this.imageEl.OriginWidth / 2;
69294         
69295         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
69296             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69297             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69298             center = this.imageEl.OriginHeight / 2;
69299         }
69300         
69301         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
69302         
69303         contextEl.translate(center, center);
69304         contextEl.rotate(this.rotate * Math.PI / 180);
69305
69306         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
69307         
69308         this.canvasEl = document.createElement("canvas");
69309         
69310         this.contextEl = this.canvasEl.getContext("2d");
69311         
69312         switch (this.rotate) {
69313             case 0 :
69314                 
69315                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69316                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69317                 
69318                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69319                 
69320                 break;
69321             case 90 : 
69322                 
69323                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69324                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69325                 
69326                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69327                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69328                     break;
69329                 }
69330                 
69331                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69332                 
69333                 break;
69334             case 180 :
69335                 
69336                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
69337                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
69338                 
69339                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69340                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69341                     break;
69342                 }
69343                 
69344                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69345                 
69346                 break;
69347             case 270 :
69348                 
69349                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
69350                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
69351         
69352                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69353                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69354                     break;
69355                 }
69356                 
69357                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
69358                 
69359                 break;
69360             default : 
69361                 break;
69362         }
69363         
69364         this.previewEl.appendChild(this.canvasEl);
69365         
69366         this.setCanvasPosition(false);
69367     },
69368     
69369     crop : function()
69370     {
69371         if(!this.canvasLoaded){
69372             return;
69373         }
69374         
69375         var imageCanvas = document.createElement("canvas");
69376         
69377         var imageContext = imageCanvas.getContext("2d");
69378         
69379         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
69380         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
69381         
69382         var center = imageCanvas.width / 2;
69383         
69384         imageContext.translate(center, center);
69385         
69386         imageContext.rotate(this.rotate * Math.PI / 180);
69387         
69388         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
69389         
69390         var canvas = document.createElement("canvas");
69391         
69392         var context = canvas.getContext("2d");
69393
69394         canvas.width = this.thumbEl.getWidth() / this.getScaleLevel();
69395         
69396         canvas.height = this.thumbEl.getHeight() / this.getScaleLevel();
69397
69398         switch (this.rotate) {
69399             case 0 :
69400                 
69401                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
69402                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
69403                 
69404                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69405                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69406                 
69407                 var sx = this.thumbEl.getLeft(true) - this.previewEl.getLeft(true);
69408                 var sy = this.thumbEl.getTop(true) - this.previewEl.getTop(true);
69409
69410                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69411                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69412
69413                 if(canvas.width > this.outputMaxWidth) {
69414                     var scale = this.outputMaxWidth / canvas.width;
69415                     canvas.width = canvas.width * scale;
69416                     canvas.height = canvas.height * scale;
69417                     context.scale(scale, scale);
69418                 }
69419
69420                 context.fillStyle = 'white';
69421                 context.fillRect(0, 0, this.thumbEl.getWidth() / this.getScaleLevel(), this.thumbEl.getHeight() / this.getScaleLevel());
69422
69423
69424                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69425                 
69426                 break;
69427             case 90 : 
69428                 
69429                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
69430                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
69431                 
69432                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69433                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69434                 
69435                 var targetWidth = this.minWidth - 2 * x;
69436                 var targetHeight = this.minHeight - 2 * y;
69437                 
69438                 var scale = 1;
69439                 
69440                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69441                     scale = targetWidth / width;
69442                 }
69443                 
69444                 if(x > 0 && y == 0){
69445                     scale = targetHeight / height;
69446                 }
69447                 
69448                 if(x > 0 && y > 0){
69449                     scale = targetWidth / width;
69450                     
69451                     if(width < height){
69452                         scale = targetHeight / height;
69453                     }
69454                 }
69455                 
69456                 context.scale(scale, scale);
69457                 
69458                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69459                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69460
69461                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69462                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69463                 
69464                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
69465                 
69466                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69467                 
69468                 break;
69469             case 180 :
69470                 
69471                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
69472                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
69473                 
69474                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69475                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69476                 
69477                 var targetWidth = this.minWidth - 2 * x;
69478                 var targetHeight = this.minHeight - 2 * y;
69479                 
69480                 var scale = 1;
69481                 
69482                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69483                     scale = targetWidth / width;
69484                 }
69485                 
69486                 if(x > 0 && y == 0){
69487                     scale = targetHeight / height;
69488                 }
69489                 
69490                 if(x > 0 && y > 0){
69491                     scale = targetWidth / width;
69492                     
69493                     if(width < height){
69494                         scale = targetHeight / height;
69495                     }
69496                 }
69497                 
69498                 context.scale(scale, scale);
69499                 
69500                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69501                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69502
69503                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69504                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69505
69506                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
69507                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
69508                 
69509                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69510                 
69511                 break;
69512             case 270 :
69513                 
69514                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
69515                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
69516                 
69517                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
69518                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
69519                 
69520                 var targetWidth = this.minWidth - 2 * x;
69521                 var targetHeight = this.minHeight - 2 * y;
69522                 
69523                 var scale = 1;
69524                 
69525                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
69526                     scale = targetWidth / width;
69527                 }
69528                 
69529                 if(x > 0 && y == 0){
69530                     scale = targetHeight / height;
69531                 }
69532                 
69533                 if(x > 0 && y > 0){
69534                     scale = targetWidth / width;
69535                     
69536                     if(width < height){
69537                         scale = targetHeight / height;
69538                     }
69539                 }
69540                 
69541                 context.scale(scale, scale);
69542                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
69543                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
69544
69545                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
69546                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
69547                 
69548                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
69549                 
69550                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
69551                 
69552                 break;
69553             default : 
69554                 break;
69555         }
69556         
69557         this.cropData = canvas.toDataURL(this.cropType);
69558         
69559         if(this.fireEvent('crop', this, this.cropData) !== false){
69560             this.process(this.file, this.cropData);
69561         }
69562         
69563         return;
69564         
69565     },
69566     
69567     setThumbBoxSize : function()
69568     {
69569         var width, height;
69570         
69571         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
69572             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
69573             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
69574             
69575             this.minWidth = width;
69576             this.minHeight = height;
69577             
69578             if(this.rotate == 90 || this.rotate == 270){
69579                 this.minWidth = height;
69580                 this.minHeight = width;
69581             }
69582         }
69583         
69584         height = this.windowSize;
69585         width = Math.ceil(this.minWidth * height / this.minHeight);
69586         
69587         if(this.minWidth > this.minHeight){
69588             width = this.windowSize;
69589             height = Math.ceil(this.minHeight * width / this.minWidth);
69590         }
69591         
69592         this.thumbEl.setStyle({
69593             width : width + 'px',
69594             height : height + 'px'
69595         });
69596
69597         return;
69598             
69599     },
69600     
69601     setThumbBoxPosition : function()
69602     {
69603         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
69604         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
69605         
69606         this.thumbEl.setLeft(x);
69607         this.thumbEl.setTop(y);
69608         
69609     },
69610     
69611     baseRotateLevel : function()
69612     {
69613         this.baseRotate = 1;
69614         
69615         if(
69616                 typeof(this.exif) != 'undefined' &&
69617                 typeof(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
69618                 [1, 3, 6, 8].indexOf(this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']]) != -1
69619         ){
69620             this.baseRotate = this.exif[Roo.dialog.UploadCropbox['tags']['Orientation']];
69621         }
69622         
69623         this.rotate = Roo.dialog.UploadCropbox['Orientation'][this.baseRotate];
69624         
69625     },
69626     
69627     baseScaleLevel : function()
69628     {
69629         var width, height;
69630         
69631         if(this.isDocument){
69632             
69633             if(this.baseRotate == 6 || this.baseRotate == 8){
69634             
69635                 height = this.thumbEl.getHeight();
69636                 this.baseScale = height / this.imageEl.OriginWidth;
69637
69638                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
69639                     width = this.thumbEl.getWidth();
69640                     this.baseScale = width / this.imageEl.OriginHeight;
69641                 }
69642
69643                 return;
69644             }
69645
69646             height = this.thumbEl.getHeight();
69647             this.baseScale = height / this.imageEl.OriginHeight;
69648
69649             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
69650                 width = this.thumbEl.getWidth();
69651                 this.baseScale = width / this.imageEl.OriginWidth;
69652             }
69653
69654             return;
69655         }
69656         
69657         if(this.baseRotate == 6 || this.baseRotate == 8){
69658             
69659             width = this.thumbEl.getHeight();
69660             this.baseScale = width / this.imageEl.OriginHeight;
69661             
69662             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
69663                 height = this.thumbEl.getWidth();
69664                 this.baseScale = height / this.imageEl.OriginHeight;
69665             }
69666             
69667             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69668                 height = this.thumbEl.getWidth();
69669                 this.baseScale = height / this.imageEl.OriginHeight;
69670                 
69671                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
69672                     width = this.thumbEl.getHeight();
69673                     this.baseScale = width / this.imageEl.OriginWidth;
69674                 }
69675             }
69676             
69677             return;
69678         }
69679         
69680         width = this.thumbEl.getWidth();
69681         this.baseScale = width / this.imageEl.OriginWidth;
69682         
69683         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
69684             height = this.thumbEl.getHeight();
69685             this.baseScale = height / this.imageEl.OriginHeight;
69686         }
69687         
69688         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
69689             
69690             height = this.thumbEl.getHeight();
69691             this.baseScale = height / this.imageEl.OriginHeight;
69692             
69693             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
69694                 width = this.thumbEl.getWidth();
69695                 this.baseScale = width / this.imageEl.OriginWidth;
69696             }
69697             
69698         }
69699
69700         if(this.imageEl.OriginWidth < this.minWidth || this.imageEl.OriginHeight < this.minHeight) {
69701             this.baseScale = width / this.minWidth;
69702         }
69703
69704         return;
69705     },
69706     
69707     getScaleLevel : function()
69708     {
69709         return this.baseScale * Math.pow(1.02, this.scale);
69710     },
69711     
69712     onTouchStart : function(e)
69713     {
69714         if(!this.canvasLoaded){
69715             this.beforeSelectFile(e);
69716             return;
69717         }
69718         
69719         var touches = e.browserEvent.touches;
69720         
69721         if(!touches){
69722             return;
69723         }
69724         
69725         if(touches.length == 1){
69726             this.onMouseDown(e);
69727             return;
69728         }
69729         
69730         if(touches.length != 2){
69731             return;
69732         }
69733         
69734         var coords = [];
69735         
69736         for(var i = 0, finger; finger = touches[i]; i++){
69737             coords.push(finger.pageX, finger.pageY);
69738         }
69739         
69740         var x = Math.pow(coords[0] - coords[2], 2);
69741         var y = Math.pow(coords[1] - coords[3], 2);
69742         
69743         this.startDistance = Math.sqrt(x + y);
69744         
69745         this.startScale = this.scale;
69746         
69747         this.pinching = true;
69748         this.dragable = false;
69749         
69750     },
69751     
69752     onTouchMove : function(e)
69753     {
69754         if(!this.pinching && !this.dragable){
69755             return;
69756         }
69757         
69758         var touches = e.browserEvent.touches;
69759         
69760         if(!touches){
69761             return;
69762         }
69763         
69764         if(this.dragable){
69765             this.onMouseMove(e);
69766             return;
69767         }
69768         
69769         var coords = [];
69770         
69771         for(var i = 0, finger; finger = touches[i]; i++){
69772             coords.push(finger.pageX, finger.pageY);
69773         }
69774         
69775         var x = Math.pow(coords[0] - coords[2], 2);
69776         var y = Math.pow(coords[1] - coords[3], 2);
69777         
69778         this.endDistance = Math.sqrt(x + y);
69779         
69780         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
69781         
69782         if(!this.zoomable()){
69783             this.scale = this.startScale;
69784             return;
69785         }
69786         
69787         this.draw();
69788         
69789     },
69790     
69791     onTouchEnd : function(e)
69792     {
69793         this.pinching = false;
69794         this.dragable = false;
69795         
69796     },
69797     
69798     process : function(file, crop)
69799     {
69800         if(this.loadMask){
69801             this.maskEl.mask(this.loadingText);
69802         }
69803         
69804         this.xhr = new XMLHttpRequest();
69805         
69806         file.xhr = this.xhr;
69807
69808         this.xhr.open(this.method, this.url, true);
69809         
69810         var headers = {
69811             "Accept": "application/json",
69812             "Cache-Control": "no-cache",
69813             "X-Requested-With": "XMLHttpRequest"
69814         };
69815         
69816         for (var headerName in headers) {
69817             var headerValue = headers[headerName];
69818             if (headerValue) {
69819                 this.xhr.setRequestHeader(headerName, headerValue);
69820             }
69821         }
69822         
69823         var _this = this;
69824         
69825         this.xhr.onload = function()
69826         {
69827             _this.xhrOnLoad(_this.xhr);
69828         }
69829         
69830         this.xhr.onerror = function()
69831         {
69832             _this.xhrOnError(_this.xhr);
69833         }
69834         
69835         var formData = new FormData();
69836
69837         formData.append('returnHTML', 'NO');
69838
69839         if(crop){
69840             formData.append('crop', crop);
69841             var blobBin = atob(crop.split(',')[1]);
69842             var array = [];
69843             for(var i = 0; i < blobBin.length; i++) {
69844                 array.push(blobBin.charCodeAt(i));
69845             }
69846             var croppedFile =new Blob([new Uint8Array(array)], {type: this.cropType});
69847             formData.append(this.paramName, croppedFile, file.name);
69848         }
69849         
69850         if(typeof(file.filename) != 'undefined'){
69851             formData.append('filename', file.filename);
69852         }
69853         
69854         if(typeof(file.mimetype) != 'undefined'){
69855             formData.append('mimetype', file.mimetype);
69856         }
69857
69858         if(this.fireEvent('arrange', this, formData) != false){
69859             this.xhr.send(formData);
69860         };
69861     },
69862     
69863     xhrOnLoad : function(xhr)
69864     {
69865         if(this.loadMask){
69866             this.maskEl.unmask();
69867         }
69868         
69869         if (xhr.readyState !== 4) {
69870             this.fireEvent('exception', this, xhr);
69871             return;
69872         }
69873
69874         var response = Roo.decode(xhr.responseText);
69875         
69876         if(!response.success){
69877             this.fireEvent('exception', this, xhr);
69878             return;
69879         }
69880         
69881         var response = Roo.decode(xhr.responseText);
69882         
69883         this.fireEvent('upload', this, response);
69884         
69885     },
69886     
69887     xhrOnError : function()
69888     {
69889         if(this.loadMask){
69890             this.maskEl.unmask();
69891         }
69892         
69893         Roo.log('xhr on error');
69894         
69895         var response = Roo.decode(xhr.responseText);
69896           
69897         Roo.log(response);
69898         
69899     },
69900     
69901     prepare : function(file)
69902     {   
69903         if(this.loadMask){
69904             this.maskEl.mask(this.loadingText);
69905         }
69906         
69907         this.file = false;
69908         this.exif = {};
69909         
69910         if(typeof(file) === 'string'){
69911             this.loadCanvas(file);
69912             return;
69913         }
69914         
69915         if(!file || !this.urlAPI){
69916             return;
69917         }
69918         
69919         this.file = file;
69920         if(typeof(file.type) != 'undefined' && file.type.length != 0) {
69921             this.cropType = file.type;
69922         }
69923         
69924         var _this = this;
69925         
69926         if(this.fireEvent('prepare', this, this.file) != false){
69927             
69928             var reader = new FileReader();
69929             
69930             reader.onload = function (e) {
69931                 if (e.target.error) {
69932                     Roo.log(e.target.error);
69933                     return;
69934                 }
69935                 
69936                 var buffer = e.target.result,
69937                     dataView = new DataView(buffer),
69938                     offset = 2,
69939                     maxOffset = dataView.byteLength - 4,
69940                     markerBytes,
69941                     markerLength;
69942                 
69943                 if (dataView.getUint16(0) === 0xffd8) {
69944                     while (offset < maxOffset) {
69945                         markerBytes = dataView.getUint16(offset);
69946                         
69947                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
69948                             markerLength = dataView.getUint16(offset + 2) + 2;
69949                             if (offset + markerLength > dataView.byteLength) {
69950                                 Roo.log('Invalid meta data: Invalid segment size.');
69951                                 break;
69952                             }
69953                             
69954                             if(markerBytes == 0xffe1){
69955                                 _this.parseExifData(
69956                                     dataView,
69957                                     offset,
69958                                     markerLength
69959                                 );
69960                             }
69961                             
69962                             offset += markerLength;
69963                             
69964                             continue;
69965                         }
69966                         
69967                         break;
69968                     }
69969                     
69970                 }
69971                 
69972                 var url = _this.urlAPI.createObjectURL(_this.file);
69973                 
69974                 _this.loadCanvas(url);
69975                 
69976                 return;
69977             }
69978             
69979             reader.readAsArrayBuffer(this.file);
69980             
69981         }
69982         
69983     },
69984     
69985     parseExifData : function(dataView, offset, length)
69986     {
69987         var tiffOffset = offset + 10,
69988             littleEndian,
69989             dirOffset;
69990     
69991         if (dataView.getUint32(offset + 4) !== 0x45786966) {
69992             // No Exif data, might be XMP data instead
69993             return;
69994         }
69995         
69996         // Check for the ASCII code for "Exif" (0x45786966):
69997         if (dataView.getUint32(offset + 4) !== 0x45786966) {
69998             // No Exif data, might be XMP data instead
69999             return;
70000         }
70001         if (tiffOffset + 8 > dataView.byteLength) {
70002             Roo.log('Invalid Exif data: Invalid segment size.');
70003             return;
70004         }
70005         // Check for the two null bytes:
70006         if (dataView.getUint16(offset + 8) !== 0x0000) {
70007             Roo.log('Invalid Exif data: Missing byte alignment offset.');
70008             return;
70009         }
70010         // Check the byte alignment:
70011         switch (dataView.getUint16(tiffOffset)) {
70012         case 0x4949:
70013             littleEndian = true;
70014             break;
70015         case 0x4D4D:
70016             littleEndian = false;
70017             break;
70018         default:
70019             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
70020             return;
70021         }
70022         // Check for the TIFF tag marker (0x002A):
70023         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
70024             Roo.log('Invalid Exif data: Missing TIFF marker.');
70025             return;
70026         }
70027         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
70028         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
70029         
70030         this.parseExifTags(
70031             dataView,
70032             tiffOffset,
70033             tiffOffset + dirOffset,
70034             littleEndian
70035         );
70036     },
70037     
70038     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
70039     {
70040         var tagsNumber,
70041             dirEndOffset,
70042             i;
70043         if (dirOffset + 6 > dataView.byteLength) {
70044             Roo.log('Invalid Exif data: Invalid directory offset.');
70045             return;
70046         }
70047         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
70048         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
70049         if (dirEndOffset + 4 > dataView.byteLength) {
70050             Roo.log('Invalid Exif data: Invalid directory size.');
70051             return;
70052         }
70053         for (i = 0; i < tagsNumber; i += 1) {
70054             this.parseExifTag(
70055                 dataView,
70056                 tiffOffset,
70057                 dirOffset + 2 + 12 * i, // tag offset
70058                 littleEndian
70059             );
70060         }
70061         // Return the offset to the next directory:
70062         return dataView.getUint32(dirEndOffset, littleEndian);
70063     },
70064     
70065     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
70066     {
70067         var tag = dataView.getUint16(offset, littleEndian);
70068         
70069         this.exif[tag] = this.getExifValue(
70070             dataView,
70071             tiffOffset,
70072             offset,
70073             dataView.getUint16(offset + 2, littleEndian), // tag type
70074             dataView.getUint32(offset + 4, littleEndian), // tag length
70075             littleEndian
70076         );
70077     },
70078     
70079     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
70080     {
70081         var tagType = Roo.dialog.UploadCropbox.exifTagTypes[type],
70082             tagSize,
70083             dataOffset,
70084             values,
70085             i,
70086             str,
70087             c;
70088     
70089         if (!tagType) {
70090             Roo.log('Invalid Exif data: Invalid tag type.');
70091             return;
70092         }
70093         
70094         tagSize = tagType.size * length;
70095         // Determine if the value is contained in the dataOffset bytes,
70096         // or if the value at the dataOffset is a pointer to the actual data:
70097         dataOffset = tagSize > 4 ?
70098                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
70099         if (dataOffset + tagSize > dataView.byteLength) {
70100             Roo.log('Invalid Exif data: Invalid data offset.');
70101             return;
70102         }
70103         if (length === 1) {
70104             return tagType.getValue(dataView, dataOffset, littleEndian);
70105         }
70106         values = [];
70107         for (i = 0; i < length; i += 1) {
70108             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
70109         }
70110         
70111         if (tagType.ascii) {
70112             str = '';
70113             // Concatenate the chars:
70114             for (i = 0; i < values.length; i += 1) {
70115                 c = values[i];
70116                 // Ignore the terminating NULL byte(s):
70117                 if (c === '\u0000') {
70118                     break;
70119                 }
70120                 str += c;
70121             }
70122             return str;
70123         }
70124         return values;
70125     }
70126     
70127 });
70128
70129 Roo.apply(Roo.dialog.UploadCropbox, {
70130     tags : {
70131         'Orientation': 0x0112
70132     },
70133     
70134     Orientation: {
70135             1: 0, //'top-left',
70136 //            2: 'top-right',
70137             3: 180, //'bottom-right',
70138 //            4: 'bottom-left',
70139 //            5: 'left-top',
70140             6: 90, //'right-top',
70141 //            7: 'right-bottom',
70142             8: 270 //'left-bottom'
70143     },
70144     
70145     exifTagTypes : {
70146         // byte, 8-bit unsigned int:
70147         1: {
70148             getValue: function (dataView, dataOffset) {
70149                 return dataView.getUint8(dataOffset);
70150             },
70151             size: 1
70152         },
70153         // ascii, 8-bit byte:
70154         2: {
70155             getValue: function (dataView, dataOffset) {
70156                 return String.fromCharCode(dataView.getUint8(dataOffset));
70157             },
70158             size: 1,
70159             ascii: true
70160         },
70161         // short, 16 bit int:
70162         3: {
70163             getValue: function (dataView, dataOffset, littleEndian) {
70164                 return dataView.getUint16(dataOffset, littleEndian);
70165             },
70166             size: 2
70167         },
70168         // long, 32 bit int:
70169         4: {
70170             getValue: function (dataView, dataOffset, littleEndian) {
70171                 return dataView.getUint32(dataOffset, littleEndian);
70172             },
70173             size: 4
70174         },
70175         // rational = two long values, first is numerator, second is denominator:
70176         5: {
70177             getValue: function (dataView, dataOffset, littleEndian) {
70178                 return dataView.getUint32(dataOffset, littleEndian) /
70179                     dataView.getUint32(dataOffset + 4, littleEndian);
70180             },
70181             size: 8
70182         },
70183         // slong, 32 bit signed int:
70184         9: {
70185             getValue: function (dataView, dataOffset, littleEndian) {
70186                 return dataView.getInt32(dataOffset, littleEndian);
70187             },
70188             size: 4
70189         },
70190         // srational, two slongs, first is numerator, second is denominator:
70191         10: {
70192             getValue: function (dataView, dataOffset, littleEndian) {
70193                 return dataView.getInt32(dataOffset, littleEndian) /
70194                     dataView.getInt32(dataOffset + 4, littleEndian);
70195             },
70196             size: 8
70197         }
70198     },
70199     
70200     footer : {
70201         STANDARD : [
70202             {
70203                 tag : 'div',
70204                 cls : 'btn-group roo-upload-cropbox-rotate-left',
70205                 action : 'rotate-left',
70206                 cn : [
70207                     {
70208                         tag : 'button',
70209                         cls : 'btn btn-default',
70210                         html : '<i class="fa fa-undo"></i>'
70211                     }
70212                 ]
70213             },
70214             {
70215                 tag : 'div',
70216                 cls : 'btn-group roo-upload-cropbox-picture',
70217                 action : 'picture',
70218                 cn : [
70219                     {
70220                         tag : 'button',
70221                         cls : 'btn btn-default',
70222                         html : '<i class="fa fa-picture-o"></i>'
70223                     }
70224                 ]
70225             },
70226             {
70227                 tag : 'div',
70228                 cls : 'btn-group roo-upload-cropbox-rotate-right',
70229                 action : 'rotate-right',
70230                 cn : [
70231                     {
70232                         tag : 'button',
70233                         cls : 'btn btn-default',
70234                         html : '<i class="fa fa-repeat"></i>'
70235                     }
70236                 ]
70237             }
70238         ],
70239         DOCUMENT : [
70240             {
70241                 tag : 'div',
70242                 cls : 'btn-group roo-upload-cropbox-rotate-left',
70243                 action : 'rotate-left',
70244                 cn : [
70245                     {
70246                         tag : 'button',
70247                         cls : 'btn btn-default',
70248                         html : '<i class="fa fa-undo"></i>'
70249                     }
70250                 ]
70251             },
70252             {
70253                 tag : 'div',
70254                 cls : 'btn-group roo-upload-cropbox-download',
70255                 action : 'download',
70256                 cn : [
70257                     {
70258                         tag : 'button',
70259                         cls : 'btn btn-default',
70260                         html : '<i class="fa fa-download"></i>'
70261                     }
70262                 ]
70263             },
70264             {
70265                 tag : 'div',
70266                 cls : 'btn-group roo-upload-cropbox-crop',
70267                 action : 'crop',
70268                 cn : [
70269                     {
70270                         tag : 'button',
70271                         cls : 'btn btn-default',
70272                         html : '<i class="fa fa-crop"></i>'
70273                     }
70274                 ]
70275             },
70276             {
70277                 tag : 'div',
70278                 cls : 'btn-group roo-upload-cropbox-trash',
70279                 action : 'trash',
70280                 cn : [
70281                     {
70282                         tag : 'button',
70283                         cls : 'btn btn-default',
70284                         html : '<i class="fa fa-trash"></i>'
70285                     }
70286                 ]
70287             },
70288             {
70289                 tag : 'div',
70290                 cls : 'btn-group roo-upload-cropbox-rotate-right',
70291                 action : 'rotate-right',
70292                 cn : [
70293                     {
70294                         tag : 'button',
70295                         cls : 'btn btn-default',
70296                         html : '<i class="fa fa-repeat"></i>'
70297                     }
70298                 ]
70299             }
70300         ],
70301         ROTATOR : [
70302             {
70303                 tag : 'div',
70304                 cls : 'btn-group roo-upload-cropbox-rotate-left',
70305                 action : 'rotate-left',
70306                 cn : [
70307                     {
70308                         tag : 'button',
70309                         cls : 'btn btn-default',
70310                         html : '<i class="fa fa-undo"></i>'
70311                     }
70312                 ]
70313             },
70314             {
70315                 tag : 'div',
70316                 cls : 'btn-group roo-upload-cropbox-rotate-right',
70317                 action : 'rotate-right',
70318                 cn : [
70319                     {
70320                         tag : 'button',
70321                         cls : 'btn btn-default',
70322                         html : '<i class="fa fa-repeat"></i>'
70323                     }
70324                 ]
70325             }
70326         ],
70327         CENTER : [
70328             {
70329                 tag : 'div',
70330                 cls : 'btn-group roo-upload-cropbox-center',
70331                 action : 'center',
70332                 cn : [
70333                     {
70334                         tag : 'button',
70335                         cls : 'btn btn-default',
70336                         html : 'CENTER'
70337                     }
70338                 ]
70339             }
70340         ]
70341     }
70342 });